CSE 422S: Studio 6

Loadable Kernel Modules


There was a big book with plain red leather covers; its tall pages were now almost filled. At the beginning there were many leaves covered with Bilbo's thin wandering hand; but most of it was written in Frodo's firm flowing script. It was divided into chapters but Chapter 80 was unfinished, and after that were some blank leaves.

"Why, you have nearly finished it, Mr. Frodo!" Sam exclaimed. "Well, you have kept at it, I must say."

"I have quite finished, Sam," said Frodo. "The last pages are for you."

The Return of the King, Book VI, Chapter 9

In this studio, you will:

  1. Build and install kernel modules
  2. Write a kernel module and use it to observe changes in a kernel variable

Please complete the required exercises below, as well as any optional enrichment exercises that you wish to complete.

As you work through these exercises, please record your answers, and when finished email your results to eng-cse422s@email.wustl.edu with the phrase Loadable Kernel Modules in the subject line.

Make sure that the name of each person who worked on these exercises is listed in the first answer, and make sure you number each of your responses so it is easy to match your responses with each exercise.


Required Exercises

  1. As the answer to the first exercise, list the names of the people who worked together on this studio.

  2. In the linux_source directory (which contains the linux directory in which you've built the kernel), create a new directory to hold your kernel modules, and cd into it. Save a copy of simple_module.c (a simple template for writing kernel modules in this course, based on the "Hello, World!" module shown on pages 338 and 339 of Robert Love's Linux Kernel Development, Third Edition) into that directory. In that directory also create a Makefile that contains the line

    obj-m := simple_module.o

    and save that file. You should then build the module with the commands

    module add raspberry

    and then

    KERNEL=kernel7

    (or set KERNEL=kernel7 if that's what your shell requires) and then

    make -C ../linux/ ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- SUBDIRS=$PWD modules

    (this assumes that the directory you created for your kernel module code is a sibling to the linux directory that holds the Linux code - if not, instead replace ../linux/ with the path to the location of your linux directory). If that command is successful, it should produce a file named simple_module.ko, in the current directory.

    As the answer to this exercise, show the output that was produced by make.

  3. Boot up your Raspberry Pi, open up a terminal window, create a directory to hold your kernel modules, and use sftp to get the simple_module.ko file you produced in the previous exercise.

    First, clear out the contents of the system log using the command

    sudo dmesg --clear

    and then use the insmod utility to load your kernel module into the kernel, as in:

    sudo insmod simple_module.ko

    If you recieved no error messages, then your module has been successfully loaded. To confirm this, you can check the system log by issuing the command

    dmesg

    which prints out the system log, which now should show the message that was printed when the module loaded. As the answer to this exercise, please show the message that appears in the system log.

  4. To confirm your module was loaded, you can also issue the command

    lsmod

    to see a listing of all currently loaded kernel modules. Verify that your module appears in the list, and as the answer to this exercise, copy the output that was produced by lsmod.

  5. The basic utility for removing modules from the kernel is called rmmod. When using this tool you can either specify the module name (as shown in lsmod) or you can specify a .ko file, as in

    sudo rmmod simple_module.ko

    Remove your module now, and verify its removal as before. As the answer to this exercise, copy the output of lsmod and the line of the system log, which show that the module was unloaded.

  6. One major reason for using kernel modules (as opposed to running a userspace program with root permissions) is that module code has direct access to all of the kernel's resources. Userspace programs, in contrast, must use system calls to access such resources, and even then, most of the kernel remains opaque to user processes.

    One kernel variable we've talked about previously is the jiffies counter. Recall that this variable keeps track of how many timer interrupts (also called ticks) have occured since system boot. Copy simple_module.c to a new file called jiffies_module.c and modify that new file, so that the system log messages that are generated when the module is loaded and unloaded also give the value of the jiffies variable (hint: it's an unsigned long) to the system log. Note that although this jiffies variable is not readily available to userspace programs, it is available directly when in kernelspace.

    Update your Makefile, build your new kernel module and use sftp to copy the jiffies_module.ko file that was produced for it, over to your Raspberry Pi.

    On your Raspberry Pi, load and unload your new kernel module, and as the answer to this exercise please copy and paste the system log message that shows the values of the jiffies variable when your module was loaded and when it was unloaded, and say how many ticks occurred between those messages.

    Things to turn in

    In addition to the answers for the exercises above (and any of the optional enrichment exercises below that you may have completed), please submit:


Optional Enrichment Exercises

  1. Module init and exit functions are supposed to use the "0/-E" return convention. This means that these functions should return a 0 on success, or in the event of a failure they return a negative value that is one of the error codes found in /include/uapi/asm-gerneic/errno-base.h. Modify your init function to return positive and negative values, respectively. As the answer to this exercise please describe briefly what happens when you load the module, and what you see in the system logs because of that.

  2. Kernel symbols that are available to be used in loadable modules are called exported symbols and are identified by the EXPORT_SYMBOL macro. You can see a list of all kernel symbols by looking at the file /proc/kallsyms, e.g. cat /proc/kallsyms. You might notice that this is very similar to the symbol table of a traditional application. (Which you can print with the program nm, if you've never done that before. Try it on any binary!) In fact, their syntax is identical, so you can use man nm to find some more information about how to decode the contents of /proc/kallsyms.

    Symbols that have been exported can be found in this list with the prefixes __kstrtab_, and __ksymtab_. These prefixes denote a special kernel symbol that stores the name of the exported symbol and a struct that stores information about the symbol, respectively. See the definition of EXPORT_SYMBOL to see how these are generated. As the answer to this exercise, please list a few of those symbols that are available for use in a kernel module.

  3. The insmod utility has been superceeded by another program called modprobe, which in general should be used because it performs dependency resolution between modules. We use insmod and rmmod here simply to demonstrate the low-level utilities that actually perform module loading and unloading.

    On your Raspberry Pi, run the command

    man modprobe

    to study the modprobe utility, and then use that utility to load and unload your modules from the required exercises. As the answer to this exercise, please describe what you observed, including any differences compared to what you saw when you used insmod.