Project 3: Write a simple device driver
Due date
Dec 6, 2016, 11:59pm (Please demo your project on Dec 07)
Goal
The goal of this project is (1) to obtain a good understanding of device drivers and file systems, (2) to learn the uses of a Loadable Kernel Module, and (3) to understand the difference between regular file I/O and memory-mapped file I/O.
Details
The project consists of three sub-projects.
The first sub-project is very simple; you only need to follow the steps to create a file system within a file. You will be asked to point out the meaning of each step during the demo. In particular, you need to distinguish the concepts of "device", "file system", and "mount point".
The second sub-project is to write two programs. The first is to use regular file I/O to count the number of each character (e.g., in "see", there is one 's' and two 'e's) in link. The second is to use memory-mapped file I/O to do the same thing. Compare the execution time of the two.
In the third sub-project, you need to write a device driver as well as a user program that
uses it. Please follow the first article
in the references to develop a device driver that registers a pseudo device and that
implements the driver functions. So that
in the user program you can open
the pseudo device and read
it.
The read
call should return the path name of the current working directory.
You are not allowed to use the API getcwd
. Instead, you have
to write some kernel code to retrieve the path name. One of the purposes of this sub-project
is to illustrate that, by writing a kernel module,
your can add functionalities to the kernel without recompiling and reinstalling it, which is very
different from what you did in the second project.
The graph below shows where the current working directory, pwd
, is
stored (current->fs->pwd
): it is
a path
struct
field inside
fs_struct
.

Hints
-
The following code returns a copy of
pwd
in a thread-safe way:get_fs_pwd(current->fs, &pwd)
(but don't forgetpath_put(&pwd)
after you usepwd
). -
d_path(&pwd,buf,buf_len)
converts astruct path
object to an ASCII path name. Below are the header files you may need at least:
Pay attention to the comment for#include <linux/init.h> #include <linux/module.h> #include <linux/sched.h>// current #include <linux/dcache.h> // d_path() #include <linux/fs_struct.h>// fs_struct, path
d_path()
: "Callers should use the returned pointer, not the passed in buffer, to use the name!". -
You can either declare a char array as a global variable or use
kmalloc
to allocate a buffer for storing the string of the path (don't forget to callkfree
in the latter case). -
Don't copy data to user space directly. Use
copy_to_user/copy_from_user
andget_user/put_user
to copy data between kernel and user spaces if you don't want to crash your system. They internally check whether the pointer parameters are valid. Please refer to here, here, and here. - If your kernel version does not have
d_path
exported, you can use dentry_path_raw. Note that this function requires its first parameter asstruct dentry *
; there is such a field inside thepath
structure, sopwd.dentry
should work. - Note that the Linux kernel code keeps evolving. The description and functions above are true for most of the recent kernel
versions, but don't be surprised if some specific details do not apply to your version.
If you are uncertain whether a specific symbol, e.g.,
d_path
, has been exported by your kernel version, you can run the commandcat /proc/kallsyms | grep d_path
to make sure. Please refer to here for more details about kernel exported symbols. - If you fail to install the kernel module due to errors "module verification failed...module has bad taint", don't panic. That is because your kernel has been configured with a security option requiring you to manually sign modules. Please follow the step of "MANUALLY SIGNING MODULES". You can find a concrete example here (answer provided by "P Royo").
Submission
Your submission should include the code, a readme file briefly describing your design, how to compile/use your code and the contribution in the case of pair programming, and a report which consists of the following parts:
- Is
/proc
a file system? We used one of the functions in the second project (cat /proc/$pid/maps
); please enumerate five other functions that you like most. In addition to peeking (i.e., reading) kernel secrets, can you also write to/proc
? If so, please give one example. - Is
/dev
a file system? What are pseudo devices? Trycat /dev/urandom | base64
, and describe how the system works in this example. - What are the uses of Loadable Kernel Modules? Is it possible to implement Project 2 (i.e., reading through the user space of a given process) using LKM?
Environment
Linux (any kernel after 2.6 is fine) and C/C++.
References
You only need the first reference for coding. If you have time and interest, you can read other articles.
- The Linux Kernel Module Programming Guide, Chapter 1--4. Peter Jay Salzman et al., 2007.
link
(Please skip Section 1.2.1, which is unnecessarily confusing. If you find the guide too obscure you can jump to a more straightforward article link; for Ubuntu users the step "Preparation: Installing Kernel Module Compilation Requirements" can be skipped safely.) - Special Devices and File Systems. Paul Krzyzanowski, 2012. link.
/proc
link and link.- The
sysfs
Filesystem. Patrick Mochel. link - How is
/dev
maintained? (udev
– A Userspace Implementation of devfs). Greg Kroah-Hartman, 2003. link - ramfs, rootfs and initramfs. Rob Landley, 2005. link