实验环境
VirtualBox + Ubuntu12.04 32bit + linux-3.10.103内核
这次编译的是32位的内核,因此64-bit kernel
一项不能选中,而且一般将内核源码放置在/usr/src
目录下。
编译命令make
执行完之后,会产生核心bzImage
和可加载模块modules
。
接着是安装过程:
1 2 3 4 5
| $ sudo make modules_install # 安装模块 $ sudo make install # 安装核心 $ mkinitramfs -o /boot/initrd.img-3.10.103 # 创建initrd文件 $ cd /boot/grub $ sudo update-grub # 更新grub来显示自己安装的内核
|
经过以上的步骤,内核基本上已经编译成功并且已经安装上了,重启即可使用自己编译的内核了。
Hello World模块
先写一个最简单的内核模块的”Hello World”模块:
代码清单 hello.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void) { printk(KERN_ALERT "Hello, world!\n"); return 0; }
static void hello_exit(void) { printk(KERN_ALERT "Goodbye, cruel world\n"); }
module_init(hello_init); module_exit(hello_exit);
|
所有模块代码中都包含module.h
和init.h
两个头文件,module.h包含可装载模块需要的大量符号和函数定义。包含init.h的目的是指定初始化和清除函数。大部分模块还包括moduleparam.h
头文件,这样就可以在装载的时候向模块传递参数,下一节中会介绍。
接着需要用make来编译这个源文件,Makefile如下:
代码清单 Makefile
1 2 3 4 5 6 7 8 9
| obj-m := hello.o KERNELBUILD := /lib/modules/$(shell uname -r)/build
EXTRA_CFLAGS = -g -O0
modules: make -C $(KERNELBUILD) M=$(CURDIR) modules clean: make -C $(KERNELBUILD) M=$(CURDIR) clean
|
Makefile文件与源代码hello.c位于同一目录,开启其中的EXTRA_CFLAGS = -g -O0
,可以包含调试信息。CURDIR
是当前目录。
编译并加载和卸载模块:
1 2 3 4 5 6 7 8 9 10 11
| $ make $ sudo insmod hello.ko # 加载模块需要root权限 $ lsmod # 查看已经加载的模块 Module Size Used by hello 590 0 ... $ sudo rmmod hello # 卸载模块 $ dmesg # 查看内核信息 ... [4618.166833] Hello, world [4686.249465] Goodbye, cruel world
|
带参数的模块
代码清单 hellop.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <linux/init.h> #include <linux/module.h> #include <linux/moduleparam.h> MODULE_LICENSE("Dual BSD/GPL");
static char *whom = "world"; static int howmany = 1; module_param(howmany, int, S_IRUGO); module_param(whom, charp, S_IRUGO); static int hello_init(void) { int i; for (i = 0; i < howmany; i++) printk(KERN_ALERT "(%d) Hello, %s\n", i, whom); return 0; }
static void hello_exit(void) { printk(KERN_ALERT "Goodbye, cruel world\n"); }
module_init(hello_init); module_exit(hello_exit);
|
参数必须使用module_param
宏来声明,这个宏在moduleparam.h
中定义。module_param需要三个参数:变量的名称、类型以及用于sysfs入口项的访问许可掩码,这个宏必须放在任何函数之外,通常在源文件头部。
Makefile和hello模块的基本相同:
代码清单 Makefile
1 2 3 4 5 6 7 8 9
| obj-m := hellop.o KERNELBUILD := /lib/modules/$(shell uname -r)/build
EXTRA_CFLAGS = -g -O0
modules: make -C $(KERNELBUILD) M=$(CURDIR) modules clean: make -C $(KERNELBUILD) M=$(CURDIR) clean
|
编译加载和卸载模块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| $ sudo insmod hellop.ko howmany=10 whom="Fan" $ sudo rmmod hellop $ dmesg [23365.767137] (0) Hello, Fan [23365.767137] (1) Hello, Fan [23365.767137] (2) Hello, Fan [23365.767137] (3) Hello, Fan [23365.767137] (4) Hello, Fan [23365.767137] (5) Hello, Fan [23365.767137] (6) Hello, Fan [23365.767137] (7) Hello, Fan [23365.767137] (8) Hello, Fan [23365.767137] (9) Hello, Fan [23388.198252] Goodbye, cruel world
|
reference
《Linux设备驱动程序》