Linux Loadable
Kernel Modules
(LKM’s)
Frédéric Dreier, I3S
Thomas Zimmermann, I3S
July 2002
2/2
Table of contents
A: Aim Of This Document
A1 Objective............................................................................................. 3
A2 Acknowledgement................................................................................. 3
A3 Changes.............................................................................................. 3
B: Introduction
B1 Loadable Kernel Modules........................................................................ 4
B2 History of Loadable Kernel Modules ......................................................... 4
B3 Module Types....................................................................................... 4
B4 Loadable vs. Built-in ............................................................................. 5
C: Our First Module
C1 Goal ................................................................................................... 6
C2 Hello World Deployment ........................................................................ 6
C3 Explanations ........................................................................................ 7
C4 Variations ............................................................................................ 7
D: Proc File Entry
D1 Communicating With Userland................................................................ 10
D2 The /proc Directory............................................................................... 10
D3 A Procfs Example.................................................................................. 10
E: Device Drivers
E1 Introduction ........................................................................................ 14
F: System Calls
F1 Introduction ........................................................................................ 15
F2 Header Files ........................................................................................ 15
F3 Interception ........................................................................................ 15
G: Conclusion
G1 Summary ............................................................................................ 18
H: Annexes
H1 References .......................................................................................... 19
3/3
A: Aim Of This Document
A1: Objective
The aim of this document is to share the results of our research and experiments about
Linux Loadable Kernel Modules (LKM’s). Since we are no experts and we first heard about
programming LKM’s two months ago, we will not present this paper as a reference but
more as a smooth diving into the Linux kernel.
We expect that you have some little experience in C programming (very little). We also
suppose that you have the kernel header files somewhere in /usr/src.
We hope this document will be pleasant and instructive.
A2: Acknowledgement
We would like to thank everybody who has already published something about LKM’s on
the internet, and who those who care about Linux documentation (HOWTO’s, guide, …).
A3: Changes
Zimmermann First draft 01.07.2002
Dreier Customization 03.07.2002
Zimmermann Corrections 07.07.2002
Dreier Updates 08.07.2002
4/4
B: Introduction
B1: Loadable Kernel Modules
Linux Loadable Kernel Modules (LKM’s) are the most convenient way to customize the
Linux kernel. Instead of modifying the source code and recompiling it, we got the
possibility to plug-in little pieces of code, called modules, at runtime.
B2: History of Loadable Kernel Modules
An operating system kernel provides all the primitive functions to access hardware
resources in a reliable way. The kernel should stay small to ensure robustness, but
should be adaptive to match all possible different hardware (keep in mind that Linux is
portable on a bunch of different platforms).
A strict monolithic architecture would be a kernel that is compiled with all needed
functions (i.e. drivers). Each time we need new hardware support we would have to
recompile the whole Kernel.
LKM’s have been introduced in Linux 1.2 (1995). An LKM is a piece of code that we can
add and remove from kernel at runtime. It does not require a kernel recompilation or
reboot of the operating system.
Some People think of LKM’s as outside of the kernel. This is a mistake: LKM’s (when
loaded) are very much part of the kernel.
Actually, almost everything that makes sense as an LKM has the option of being an LKM.
B3: Modules types
The kernel offers us several access points to control the hardware through its system call
interfaces. We can formally split these services in five families (see fig. B-1). As you can
see, the following services are implemented as modules: filesystem, block devices,
character devices and network drivers.
Very common uses of modules are device drivers. They are used to map standard I/O
functions on a specific hardware device. Unix system distinguishes between several
device types like character-, block-, network- but also SCSI- or USB- devices.
On the other hand we can use modules to customize already defined functions in the
kernel. Because modules are part of it, they can interact with the virtual filesystem
(‘/proc’ entries) or directly modify system calls.
5/5
B: Definitions
fig. B-1: kernel split view
Finally, we can influence the execution of all binaries and modify the whole computer:
hiding files or processes, tracing users, etc.
B4: Loadable vs. Built-in
There are a lot of advantages with LKM’s. They save time, because you do not have to
recompile your kernel each time you make changes. If your modules are buggy (and
they will be) they will probably crash your computer. LKM’s are loaded after the kernel
base has been initialized. Therefore it is easier to find the problem. LKM’s save memory
too, since they are loaded only when needed. LKM’s are faster to maintain and debug.
LKM’s are not slower, by the way, than compiled kernel modules.
You cannot use LKM’s for things needed during system startup (like e.g. the disk driver
where the root filesystem resides)
Process
management
Memory
management
Filesystems Device
control
Networking
System Call Interface
Concurrency,
multicasting
Virtual
memory
Files and
dirs, VFS
Tty’s and
device driver
Connectivity Implemented
features
Kernel
subsystem
Arch.
dependent
code
Memory
manager
Filesystem
types
Character
devices
Network
subsystem Software
support
IF drivers Block
devices
CPU Memory Disks & COs Consoles Network
devices
Hardware
Kernel
Implemented as modules
6/6
C: Our First Module
C1: Goal
In following chapter, we will explore the possibilities of LKM’s through some simple
examples that you will be able to run without difficulties. We will also explain you, step
by step, several thoughts about the underlying kernel mechanisms.
We will not cover some obscure aspects of the kernel programming like versioning or
multi-processors problems. We give you some references, if you want to step in more
advance topics, in the annexes section at the end of this document.
Let’s start with the well-known hello world example.
C2: Hello World Deployment
How can I write a module in less than 5 minutes? Let’s take a look at the following code:
#define MODULE
#include
int init_module()
{
printk(“Hello, world\n”);
return 0;
}
void cleanup_module()
{
printk(“Goodbye cruel world\n”);
}
Table C-1: hello world example
Save the code as hello.c and compile it as follows:
$ gcc –c hello.c
Table C-2: compilation
This results in a hello.o file. To link it to the kernel use (as root):
# insmod hello.o
Table C-3: link module into kernel
To see the loaded modules list:
# lsmod
Table C-4: list modules
To remove the module, just type:
# rmmod hello
Table C-5: remove module
To see the output, you have to take a look at the log file:
# tail /var/log/messages
Table C-6: see output
7/7
C: Our First Module
C3: Explanations
When a module is loaded into the kernel using insmod, the init_module() function is
called. This function is generally used to initialize resources and registrations. It returns 0
if no error has been encountered. The function cleanup_module() is called just before the
module is unloaded and cannot fail, therefore it returns void.
We have to define MODULE before any imports. It could also be set through the –D
option of gcc. Importing linux/module.h is mandatory, too.
We use printk(), which works like the printf() function that is not accessible from the
kernel. As you can see, the printk() function is not imported: because the module is
linked to the kernel, it gets automatically access to all kernel public symbols. printk() is a
very useful function for debugging and tracing. Therefore it supports in addition priority
symbols that can be concatenated directly to the message:
printk(“<2>Goodbye cruel world\n”); /* <0> = high priority, …, <7> = low priority */
printk(KERN_DEBUG “Goodbye cruel world\n”); /* or using constants from linux/kernel.h */
Table C-7: messages priorities
The location of the logged message depends on your klogd configuration. Normally, all
messages are reported in /var/log/messages, but the highest priority (<0> or KERN_EMERG) is
generally displayed on all terminals and on the console. The next priority (<1> or
KERN_ALERT) is displayed on the console, too.
C4: Variations
C4.1: The Code
Now we will revisit our first example, using new features. We will also write a Makefile.
Table C-8: hello world revisited
#include /* contains module definitions */
/* There exists macros to insert documentation in object files
MODULE_AUTHOR("John Doe");
MODULE_DESCRIPTION("This module is a demo for our presentation");
MODULE_SUPPORTED_DEVICE("This is not a real driver. Actually, no device is supported");
// we can use a macro to initialize a value through the insmod command
// insmod hello2.o anIntValue=300
//
static int anIntValue = 100;
MODULE_PARM (anIntValue, "i");
MODULE_PARM_DESC (anIntValue, "A dummy value used for our presentation (default 100)");
/* This method is called when the module is loaded */
static int __init my_init(void)
{
printk ("<1>Hello, world. anIntValue is %i\n", anIntValue);
return 0;
}
/* This method is invoked when the module is unloaded */
static void __exit my_cleanup(void)
{
printk("<1>Goodbye cruel world\n");
}
/* define init and cleanup methods */
module_init(my_init);
module_exit(my_cleanup);
8/8
C: Our First Module
Our small Makefile:
INCLUDEDIR = /usr/include
CFLAG = -D__KERNEL__ -DMODULE -O -Wall -I$(INCLUDEDIR)
all: hello2.o
hello2.o: hello2.c
gcc $(CFLAG) -c -o $@ $<
Table C-9: makefile
As already announced, we introduce new macros, which make the code look slightly
different.
C4.2: Module Description
We add a useful module description that is saved in the module object file (MODULE_AUTHOR,
MODULE_DESCRIPTION, MODULE_SUPPORTED_DEVICE). This description can be viewed with the
modinfo command.
[root@pc-linux hello]# modinfo hello2.o
filename: hello2.o
description: "This module is a demo for our presentation"
author: "John Doe"
parm: anIntValue int, description "A dummy value used for our show (default 100)"
Table C-10: modinfo output
C4.3: Passing Parameter
We use another macro to pass arguments from the insmod command. MODULE_PARM takes
two parameters: the variable to set and its type (“i” for integer, “0-4i” for an array, …).
MODULE_PARM_DESC describes the parameter and is optional.
C4.4: Built-in Modules
If you take a look at the Linux source files (2.2 and later), you will often find the same
declarations for the init and the cleanup methods. We use them in our example, too:
…
static int __init my_init(void)
…
static void __exit my_cleanup(void)
…
Table C-11: explicit declarations
__init and __exit are macros. You should form a habit of marking the initialization and
cleanup functions where appropriated. __init marks initialization code which is thrown
away after boot is complete. __exit marks exit code which is not required if the module is
built in the kernel. Both have no effect if the LKM is loaded at runtime. It is a way to save
some memory for the kernel.
C4.5: Explicit Functions
To simplify debugging (tracing), it is smart to use personalized function names to
initialize and cleanup the module. We can do that using the module_init(my_init) and
module_exit(my_cleanup) macros.
9/9
C: Our First Module
C4.6: Namespace pollution
You can see in our example, that symbols are static. This brings another problematic to
our attention: If a module is part of the kernel, then all symbols defined in the module
are visible by the rest of the kernel. This can be useful if we want to stack modules
together (module using other modules functions). But if this is not the case, it only
results in so-called namespace pollution. Old style code sets members as static to
prevent this phenomena (static members are not exported by almost all modutils). But
more recent kernels provide macros to explicitly hide symbols using the EXPORT_NO_SYMBOLS
macro defined in sysdep.h. There is also a mechanism to explicitly export symbols, but
we will not dig further into this.
[root@pc-linux hello]# cat /proc/ksyms
e1aeb060 __insmod_hello_S.text_L56[hello]
e1aeb098 __insmod_hello_S.rodata_L41[hello]
e1aeb000 __insmod_hello_O/home/john/Documents/school/opsys/modules/…
e1ae9060 read_func_foo[proc_example]
e1ae94c4 directory [proc_example]
e1ae90c8 write_func_foo [proc_example]
e1ae94c0 fileOne [proc_example]
e1ae9000 __insmod_proc_example_O/home/john/Documents/school/opsys/ …
e1ae9060 __insmod_proc_example_S.text_L380
Table C-12: ksym output
C4.7: Makefile
We do not define MODULE in our source file anymore, but we use the –DMODULE parameter
in the Makefile, which has the same effect. We also use the –O option to properly expand
inline functions, because they are widely used in kernel and should be optimized. –Wall is
suggested in order show all warnings. Finally, it is mandatory to define the
-D__KERNEL__ symbol when compiling kernel code, because some parts of kernel
headers are protected with #ifdef __KERNEL__ blocks.
10/10
D: Proc File Entry
D1: Communicating with Userland
Communication between the kernel and userland is one main task of modules. There are
two ways to perform that with LKM’s. We can add an entry in the /proc directory or we
can make a driver device, and communicate with it through a normal device file.
D2: The /proc Directory
The /proc directory is not linked on a physical location on a disk. It is mounted on a data
structure in memory that is maintained by the kernel. As a module we can ask the kernel
to add an entry that looks like a file but which will, in fact, be mapped on our module. It
is really convenient because the user can communicate with such a module using simple
terminal commands:
$ cat /proc/my_entry
…
$ echo “blabla” > /proc/my_entry
…
Table D-1: proc entry I/O
D3: A Procfs Example
D3.1: The Code
#include /* contains module declaration */
#include /* contains all procfs methods signature */
/* some constants used in our module */
#define MODULE_NAME "opsys_seminaries"
#define MODULE_VERSION "1.0"
/* variables */
struct proc_dir_entry *directory, *fileOne; /* pseudo file entries */
/* Procfs callback method
* read is called when someone opens
* the file entry in the proc directory
* using: cat /proc/our_dir/our_module
*/
int read_func_foo(char* page, /* buffer where we should write*/
char** start, /* seem to be never used in the kernel */
off_t off, /* where we should start to write */
int count, /* how many character we could write */
int* eof, /* used to signal the end of file */
void* data) /* only used if we have defined our own buffer */
{
static int readRecallCnt = 0;
int len;
MOD_INC_USE_COUNT;
len = sprintf(page, "Here are some data that the module could\nreturn to userland.\n \
(recall cnt: %d)\n", readRecallCnt );
*eof = 1;
printk("(read_%d)\n",readRecallCnt);
readRecallCnt++;
MOD_DEC_USE_COUNT;
return len; /* return number of bytes returned */
}
11/11
D: Proc File Entry
/* Procfs callback method
* write is called when someone redirects
* data to the file:
* using: echo "texttext" > /proc/our_dir/our_module
*/
int write_func_foo(struct file* file, /* this parameter is usually ignored */
const char* buffer, /* it contains data that is passed to the module
(at maximum of possible). buffer is not in
kernel memory(ro)! Think about copying it */
unsigned long count, /* says how many bytes should be read */
void* data) /* this parameter could be set to use our
own buffer (f.e.: using a method for many files */
{
static int writeRecallCnt = 0;
MOD_INC_USE_COUNT;
printk("receive %ld chars (write_%d).\n", count, writeRecallCnt);
writeRecallCnt++;
MOD_DEC_USE_COUNT;
return count; /* return how many chars we have consumed, or
* we will receive them again...
* WARNING returning a to big count could crash the module!
*/
}
/* This method is invoked when module is loaded */
int __init my_init(void)
{
/* make a directory in /proc */
directory = proc_mkdir(MODULE_NAME, NULL);
if (directory == NULL) goto fail_dir;
directory->owner = THIS_MODULE; // mandatory
/* make file */
fileOne = create_proc_entry("myProcEntry", 0666, directory);
if (fileOne == NULL) goto fail_entry;
/* set callback functions on file */
fileOne->read_proc = read_func_foo;
fileOne->write_proc = write_func_foo;
/* small output */
printk ("Module initialized. Proc entry added.\n");
return 0;
fail_entry:
printk("<1>ERROR creating myProcEntry");
remove_proc_entry(MODULE_NAME,NULL); /* remove already registered directory */
fail_dir:
printk("<1>ERROR creating directory");
return -1;
}
/* This method is invoked when a module is removed from kernel */
void cleanup_module(void)
{
// remove pseudo file entry
remove_proc_entry("myProcEntry", directory);
remove_proc_entry(MODULE_NAME,NULL);
// print a small message
printk("Module cleanup. Proc entry removed.\n");
}
Table D-2: proc_example code
12/12
D: Proc File Entry
D3.2: Output
[root@pc-linux proc]# insmod proc_example.o
[root@pc-linux proc]# echo "What's up?" > /proc/opsys_seminaries/myProcEntry
[root@pc-linux proc]# tail /var/log/messages
Jul 4 22:42:44 pc-linux kernel: Module initialized. Proc entry added.
Jul 4 22:42:49 pc-linux kernel: receive 11 chars (write_0).
[root@pc-linux proc]# cat /proc/opsys_seminaries/myProcEntry
Here are some data that the module could
return to userland.
(recall cnt: 2)
[root@pc-linux proc]# tail /var/log/messages
Jul 4 22:42:44 pc-linux kernel: Module initialized. Proc entry added.
Jul 4 22:42:49 pc-linux kernel: receive 11 chars (write_0).
Jul 4 22:43:44 pc-linux kernel: (read_0)
Jul 4 22:43:44 pc-linux kernel: (read_1)
本文档为【Linux Loadable Kernel Modules】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑,
图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。