Tuesday, July 15, 2008

Writing the First Kernel Module

Having read the bare essential theory, we are ready to get revealed to the amateur beauty of a module. Well, at least I found it beautiful. Let’s find out, how you feel...

A Linux module is just a C program. However, writing a module requires a lot more attention, skill, awareness (and so on ...) than generally required in a normal C program, which runs in user-space. Since a kernel module runs in kernel-space, errors must be handled very intelligently, as even a smallest problem may result in a system crash.

Now, have a glance at the code below and then read the following text. There is nothing in this program that a C acquaint can't understand, except the absence of the main() function.

/***********************************************/

/* myFirstModuleSource.c */

#include linux/module.h /* macros for init(), exit() functions*/

#include linux/time.h

/* kernel data structures to represent time */

struct timespec moduleLoadTime, moduleUnloadTime;

int myFirstModuleInit(void) /* mandatory syntax for an init function */

{

printk("Hey! myFirstModule is in the kernel now.\n"); /* No, this ain't a typo error for printf() */

moduleLoadTime = current_kernel_time(); /* kernel routine to determine current timestamp */

return 0;

}

int calculateDifference(int one, int two) /* a normal C function*/

{

return (one-two);

}

void myFirstModuleExit(void) /* mandatory syntax for an exit function */

{

int moduleLifespan;

printk("myFirstModule is removed from kernel\n");


moduleUnloadTime = current_kernel_time(); /* kernel routine to get current timestamp */

moduleLifespan = calculateDifference(moduleUnloadTime.tv_sec,moduleLoadTime.tv_sec); /* a C function call */

printk("myFirstModule was in kernel for %d seconds.\n",moduleLifespan);

}

module_init(myFirstModuleInit);

module_exit(myFirstModuleExit);

/***********************************************/

The above module is mere a "hello world" module, with an added functionality of displaying the duration for which it remained linked into the kernel.


No Main()'s Land.

For a user space C program, the main() function acts as an entry point, which tells the system where to start the execution from. For a kernel module, it is an init() function. The init function is executed only once, when the module is linked into the kernel (usually by insmod shell command). However, unlike a user space program, a kernel module requires an additional function, the exit() function, which is executed when the module is removed from kernel (usually using rmmod shell command). Therefore, running a kernel module requires at least two functions:

1. An init function to load the module

2. An exit function to unload the module

A programmer can specify any function to be an init or exit function for a module. It is just that the name of that particular function has to be registered with the kernel using macros module_init() and module_exit() (as done in the last two lines of the above code). But, the the syntax cannot be altered. Every init() and exit() must have the syntax as shown.


printk()??? A typo error?

Not really!! How could I make the same spelling mistake at three places?

The Linux kernel does not have the standard libc C library (or any user space library, for that matter) which contains printf(). Therefore, it has no access to printf(). But (thank God) it has its own output function printk(). Well there is lot to say about printk() which I plan to tell in another post. For now, just consider it as an avatar of our old friend printf(). But just remember always to put a '\n' at the end of the format string in every printk() call and to look for all printk() outputs in /var/log/messages.


Some points to remember:

  • Each module must have an init() function, but exit() is not mandatory. However, if there is no exit() registered, the module is permanently linked into the kernel. It gets only removed on reboot.
  • All the clean up activities should be done in the exit() function for writing a clean and safe module.
  • It is not possible to have floating point arithmetic in kernel modules, since these operations are heavy and kernel does not have required libraries to perform them.

Module writing is over now, in next post I shall tell how did I compile the above program and insert it into the kernel.

No comments: