Writing your first kernel module

From Crashcourse Wiki

Jump to: navigation, search

Contents

Overview

As a followup to the kernel module talk I gave at the 2007 Ontario Linux Fest, here are the bare essentials to getting started with writing, compiling and loading your first kernel module. With little modification, I'm hoping this will work on most Linux distros.

The bare essentials

In order to build a loadable kernel module, you'll need the following bits and pieces (which you probably have or are easily installable by your system administrator):

  • Your current kernel version, which you can get by running uname -r,
  • The standard development utilities such as gcc, binutils and so on,
  • A kernel source tree against which to compile that module.

But first, we need to talk about that kernel source tree a bit more since it's a bit tricky.

The kernel source tree

In order to compile a kernel module, you need to have at least part of a kernel source tree against which to compile it. That's because when you write your simple module, all those preprocessor includes you use do not refer to the standard user-space header files. Instead, they refer to the kernel-space header files that you can find in the kernel source; hence the need for a kernel source tree.

For now, your easiest approach is to just ask your system administrator to install whatever corresponds to the kernel headers or kernel development package on your system, and build against that. That gives you the benefit that the module will load against the currently-running kernel as well, for testing later. You can get trickier but let's avoid that for now.

(As an aside, the reason I refer to needing only part of the kernel source tree is that, for modules, you need only the header files and the build infrastructure -- there's no need for the source part of the kernel which is why most kernel development packages are considerably smaller than a full kernel source tree.)

Your first module

As a first attempt, start with the following module, which does little more than print a few lines to the log file /var/log/messages:

/* Module source file 'hi.c'. */

#include <linux/module.h>       // for all modules
#include <linux/init.h>         // for entry/exit macros
#include <linux/kernel.h>       // for printk priority macros
#include <asm/current.h>        // process information, just for fun
#include <linux/sched.h>        // for "struct task_struct"

static int hi(void)
{
        printk(KERN_INFO "Hello from hi module.\n");
        printk(KERN_INFO "The user space process is '%s'\n", current->comm);
        printk(KERN_INFO "The user space PID is  %i\n", current->pid);
        return 0;       // to show a successful load
}

static void bye(void)
{
        printk(KERN_INFO "Goodbye from hi module.\n");
}

module_init(hi);
module_exit(bye);

MODULE_AUTHOR("Robert P. J. Day");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION("2:1.0") ;
MODULE_DESCRIPTION("You have to start somewhere.");

NOTE: At this point, you really need root privilege for being able to load modules, and to examine the file /var/log/messages. It would be nice to claim that you can do all of this without needing the superuser account but, sadly, that's not the case.

Compiling the module

As I mentioned above, compiling a kernel module is completely different from compiling a typical user-space program since you're coding against a different set of header files, and you need a totally different build infrastructure. To make a long story short, you don't want to try to figure out the recipe for this; instead, you write your module wherever you want and use the Makefile in the kernel source tree to do all the work for you by creating your own Makefile that invokes that first one and passes it the location of your module source to be compiled.

In a nutshell, you write your module source in some convenient directory, and your Makefile is set up to wander off to the kernel source tree, invoke the Makefile there to come back and compile your source. Simple, no?

ifeq ($(KERNELRELEASE),)

    KERNELDIR ?= /lib/modules/$(shell uname -r)/build
    PWD := $(shell pwd)

.PHONY: module clean

module:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

clean:
        rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module.symvers

else
    $(info Building with KERNELRELEASE = ${KERNELRELEASE})

    # called from kernel build system: just declare what our modules are

    obj-m :=    hi.o

endif

To compile your module, simply type make module (or, because it's the first target in the Makefile, just make), which should produce the final result of the loadable module hi.ko.

Loading and unloading the module

Once you have the loadable module, you should be able to load it (with root privilege) with just:

# insmod hi.ko

And once you've verified the load via the file /var/log/messages, you can remove it with:

# rmmod hi

It doesn't get much simpler than that, does it?

Personal tools