Operating Systems

DM510, Spring 2010

Daniel Merkle

DM510 Required Assignment 2: Kernel Module

In this assignment, your task is to make a kernel module. This module must implement a driver that exposes two character devices to user-space. Their function will be explained below.

To complete this task it will be necessary to study the following sources:

  • Chapters 2, 3, 5 (especially for semaphores) and 6 (especially for blocking and non-blocking I/O and for device control with ioctl) in the online book Linux Device Drivers (LDD), 3rd edition (for 2.6 kernels) hold all the information you need to complete this assignment. The original source code of all examples from LDD can be found here. At the time of writing LDD, the Linux kernel 2.6.10 was used. An updated version of all files that should work with kernel 2.6.28 can be found here. The example code from LDD for the scull driver that you have to study for this assignment was updated to work with kernel (see FAQ - the main changes are related to handling of credentials).
  • You can also use The Linux Kernel Module Programming Guide [pdf, ps.gz, html] as a source of information.

The files that you can use as a starting point for you kernel module can be found here.

The Driver

Normally a device driver sits between some hardware and the kernel I/O subsystem. Its purpose is to give the kernel a consistent interface to the type of hardware it "drives". This way the kernel can communicate with all hardware of a given type through the same interface, even though the actual hardware differs.

In this assignment we will not interface to hardware because of the complexity this would impose. Instead we will make two processes communicate trough a device driver.

The figure to the left illustrates how the driver should work. Basically it solves the producer-consumer problem. Here the two processes can be both producers and consumers. This resembles the functionality of a real hardware device driver, where both the hardware and the kernel can produce and consume data.

When a process writes (produces) to character device /dev/dm510-0 the data is stored in bounded buffer 1. If the buffer is full the process has to wait until another process has read from /dev/dm510-1.

When a process reads (consumes) from character device /dev/dm510-0 the data is read from bounded buffer 0. If the buffer is empty the process has to wait until another process has written to /dev/dm510-1.

Writes and reads to and from /dev/dm510-1 are handled in the same way.

In the code from LDD there is an example (scull/pipe.c) which does almost the same (chapter 6 of LDD). The difference is that only one character device has been used (one minor). This means that the data you write to it can be read back from the same device.


The requirements are:

  • Your module has to be made for kernel
  • You should either use a major number of 254 and minor numbers of 0 and 1 or automatically assigned major/minor numbers (see Chapter 3 of LDD).
  • Blocking and Non-blocking I/O must be supported (see Chapter 6 of LDD, page 147ff).
  • Several processes at a time must be able to open each device for reading, only one process at the time should be able to open a device for writing.
  • Simple device control has to be possible via the ioctl system call. Implement i.) adjusting the buffer sizes and ii.) setting the maximal number of processes that are allowed to read the device at a time via ioctl (see Chapter 6 of LDD, page 135ff or Chapter 7 of The Linux Kernel Module Programming Guide).
  • The device should support multiple processes executing simultaneously in the device driver at the same time. As this could lead to race conditions and other problems, you should use semaphores (or other synchronisation methods) to avoid potential problems.

This is not required:

  • Backward compatibility. The module does only have to be compatible with kernel
  • udev and devfs support.
  • Poll, select and asynchronous notification support (see Chapter 6 of LDD).

Attacking the Problem

The hardest part of most kernel programming is to understand the environment that your code should run in. Therefore, most of your time should be spent studying how character devices work. This will minimize the testing and debugging phase. Recommended is to start with reading the first chapters of LDD. Here is a starting point for your implementation:

Compiling and inserting a simple module

Create a working directory for your project as a subdirectory of the directory dm510, that you created for installing UML. Fetch the files that are the starting point for your implementation (dm510_dev.c, Makefile, dm510_load, and dm510_unload), store them in the working directory, change into the working directory containing these files and edit Makefile if necessary. (You may have to change the KERNELDIR variable with the path to your UML kernel, if you are using a non-standard location).

Now you can compile the module by running make. If the compilation succeeds there will now be a file called dm510_dev.ko which is the module. Start the UML kernel. Create the necessary devices and insert the module by executing


To remove the module and to delete the devices execute:


See LDD chapter 4 and check the two scripts for details.

Making the Driver

The file dm510_dev.c is the code for the simple module above. It has almost empty declarations of all the functions that you need to modify in order to complete this assignment. These are:

int dm510_init_module(void);
void dm510_cleanup_module(void);
int dm510_open(struct inode *, struct file *);
int dm510_release(struct inode *, struct file *);
ssize_t dm510_read(struct file *, char *, size_t, loff_t *);
ssize_t dm510_write(struct file *, const char *, size_t, loff_t *);
int dm510_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);

To get an idea of how to implement these functions you should study the implementation of the scull device driver, especially the pipelined version of the driver. This driver is explained in detail in LDD, especially pages 153ff. The source code including the necessary script to load and unload the driver can be found here. Study this example thoroughly, especially pipe.c.

Report / Submission

The report submission procedure is similar to the submission procedure of the first required assignment. The report must be short and precise (maximum 10 pages, English language, without source code). Remember that you must prove that you have understood the problems and solutions. It must include the following:

  • A small introduction.
  • A description of the design decisions you have made.
  • A description of your implementation.
  • A description of the tests you have made and the motivation for these. The tests must be able to verify that your solution works correctly, but you do not have to test all possible error scenarios. Remember it is better to document a bug rather than just ignoring it. An inspiring test program for one test can be found here.
  • A discussion of what would happen if multiple processes use the devices simultaneously.
  • A small conclusion.

Furthermore, your report has to be supplemented again by a recorded desktop session, in which you show that your implementation is working correctly. During the session you should also perform all or some of the tests that you described in your report and show that device control via ioctl is possible. In your report you should refer to the tests you performed by giving the precise time in the video file. The session you record should start when you start UML with the command ./linux in a shell. The recorded session should be maximally 5 minutes long and have a maximal size of 20MB. If one of these constraints is not fulfilled the video file will be considered as not submitted.

For recording the desktop session you should again use the tool gtk-recordMyDesktop. Do not forget to change the "Video Quality" to 10 (using the slider), and the "Frames per Second" to be recorded to 5 (Advanced Setting / Performance) to reduce the file size. Sound recording is not required.

For submitting the report, the sources, and the desktop session, proceed as follows: Create the following directory structure


Put your report, sources, and video in the corresponding directory. The sources should not include the source code of the complete Linux kernel, but only the files you changed and those files you added. The report directory and the sources directory should not contain more than 10MB of data. Similar to the size of the video file: If this constraint is not fulfilled the submission will be not considered.

Change to the directory assignment2 and type aflever DM510.

Additionally, in contrast to the first required assignment, you shall hand in a printout of your report and of your source code. (Department secretaries office).

The strict deadline for submission (electronically and printouts) is Wednesday, April 21th, 12:00.

As this project is quite resource consuming, please take care, that you clean up your home directory and that you remove unneeded files as soon as possible.

Note that the third required assignment is scheduled to be announced before the submission deadline of this assignment. The last required assignment will require a significantly larger amount of implementation. Therefore I recommend to submit this second required assignment before the strict deadline.

Frequently Asked Questions (FAQ)

  • Are we allowed to work in groups?
  • For everything except the report and recording the desktop session, you are allowed to work in groups of at most three people. Write the members of your group on the front page of your report. The report you have to write on your own. Remember that the three required assignments are part of your final exam, i.e., you have to fully understand all parts of what you have done.

  • When compiling the exampled from the LDD book I get errors like error: struct task_struct has no member named uid .
  • Recent kernels use a different handling of credentials. The important fact for this assignment is that accessing credentials is now done via wrappers (for example uid_t current_uid(void) for the current's real UID). This used to be accessible via fields in the structure task_struct. The scull driver and especially the file access.c have been adapted.

Design by 1234.info | Modified by Daniel Merkle | CSS 2.0