版权说明:引用请注明出处。
虽然linux的内核是作为一个整体来运行的,但是linux的内核是由模块化组成的,它允许内核在运行的过程中动态地插入或删除程序,这些程序被放在一个单独的二进制文件中,即所谓的可装载的内核模块中,简称为模块。支持模块的好处是基本内核镜像可以尽可能的小,而让那些不必要可不常用的部分以模块的形式单独存在,在需要时在装入,用完后就可以删除掉。
1.第一个模块
下面是一个简单的hello world的内核模块,从中我们可能了解基本的内核模块开发方法。
#include
<
linux
/
init.h
>
#include
<
linux
/
module.h
>
#include
<
linux
/
kernel.h
>
static
int
__init hello_init(
void
){
printk(KERN_ALERT
"
Hello World !
"
);
return
0
;
}
static
void
__exit hello_exit(
void
){
printk(KERN_ALERT
"
Goodbye!
"
);
}
module_init( hello_init);
module_exit( hello_exit);
MODULE_LICENSE(
"
GPL
"
);
MODULE_AUTHOR(
"
MyName
"
);
这个简单的程序以包含了模块的主要特征,每个模块都有一个入口和出口,在这个程序中是hello_init()和hello_exit(),它们分别通过module_init()和module_exit()系统调用注册到内核中,内核在加载这个模块时,调用hello_init()函数,在这个函数中一般做一些初始化工作,如果初始化顺利完成,返回零,否则返回一个非零值。hello_exit()是模块的出口函数,内核在卸载该模块是调用,负责释放资源等。
MODULE_LICENSE用于指定版权,linux一般使用"GPL"。
MODULE_AUTHOR用于指定代码的工作。
2.构建模块
在2.6内核中,使用新的"kbuild"构建系统,使构建模块更加容易了。我们写的模块可以单独放在一个我们自己的目录中,以可能直接放到源代码做中,下面是一个单独使用的Makefile
obj
-
m :
=
hello.o
KERNELDIR
?=
/
lib
/
modules
/
$(shell uname
-
r)
/
build
PWD :
=
$(shell pwd)
all:
$(MAKE)
-
C $(KERNELDIR) M
=
$(PWD)
clean:
rm
-
rf
*
.o
*~
core .depend .
*
.cmd
*
.ko
*
.mod.c .tmp_versions
运行make后正常的话会有类似下面的输出:
make
-
C
/
lib
/
modules
/
2.6
.
17
-
10
-
generic
/
build M
=/
opt
/
sources
/
kernel_prg
make[
1
]: Entering directory `
/
usr
/
src
/
linux
-
headers
-
2.6
.
17
-
10
-
generic
'
LD
/
opt
/
sources
/
kernel_prg
/
built
-
in
.o
CC [M]
/
opt
/
sources
/
kernel_prg
/
hello.o
Building modules, stage
2
.
MODPOST
CC
/
opt
/
sources
/
kernel_prg
/
hello.mod.o
LD [M]
/
opt
/
sources
/
kernel_prg
/
hello.ko
make[
1
]: Leaving directory `
/
usr
/
src
/
linux
-
headers
-
2.6
.
17
-
10
-
generic
'
这时会在生成一个hello.ko的文件。如果你的模块是由多个源文件组成的,那么在Makefile中在加一行就行了:
hello
-
objs :
=
file_a.o file_b.o
3.加载模块
要加载或卸载以root用户运行命令insmod或rmmod:
使用下面的命令可以看到我们的模块已经加载了:
使用下面的命令可以卸载该模块:
这是在用lsmod就看不到hello模块了。但是我们的输入信息到那去了呢,不急,如果你是在X Windows下的XTerm中insmod的,你不会看到输出,使用dmesg就可能看到在加载和卸载模块时的输出内容,只有直接在console下才能直接显示到屏幕上。
我们还可能使用modinfo来看看关于模块更多的东西:
$ modinfo hello.ko
filename: hello.ko
license: GPL
author: Shakespeare
vermagic:
2.6
.
17
-
10
-
generic SMP mod_unload
586
REGPARM gcc
-
4.1
depends:
srcversion: C44F4F7F7B8E8D49F537485
呵呵,好,现在我们已经是一个内核模块开发者了,不要停下,继续吧!
4.printk()函数
记住这里用的是
printk而不是
printf,在内核中我们是不能调用C库中的函数的,不过C库上的大部份函数在内核中都有实现。
printk主要是为内核提供日志功能, 记录内核信息或用来给出警告用的,因些在调用printk时,我们可以指定输入信息的级别,下表列出了可用的级别。
---------------------------
级别 说明
---------------------------
| KERN_EMERG 紧急情况
| KERN_ALERT 需要立即被注意的错误
| KERN_CRIT 临界情况
| KERN_ERR 错误
| KERN_WARNING 警告
| KERN_NOTICE 普通的
| KERN_INFO 非正式的消息
| KERN_DEBUG 调试信息
---------------------------
以上级别没有一个绝对的定义应该在什么时候用,只能你自己拿主意了。
参考文献:
<Linux内核设计与实现>第二版
The Linux Kernel Module Programming Guid