kernel 模块与简单 hello 模块
Kernel 模块与简单 hello 模块
kernel 模块的简介
Linux 内核进行扩展时,例如编写驱动程序、netfilter功能等,最方便的方式是通过编写模块,然后加载到内核中。由于 kernel 模块已加载到内核,因此如果模块出现错误,将导致内核出错甚至系统崩溃。所以,一般建议在测试内核模块时,如果与设备无关的模块(非驱动程序等),最好是在虚拟机中进行。
大多数 Linux 发行版本都没有把 Linux 编译成一个整个文件,而是把非核心的子系统,如驱动程序等,编译成 kernel 模块,并在启动时加载。模块的目录一般是 /lib/modules/<内核版本>/
目录下。内核版本号通过 $(uname -r)
获取。
加载内核模块时,可通过 modprobe
(从默认路径搜索并加载) 或 insmod
(指定模块路径)
# 加载 intel wifi 驱动
sudo modprobe iwlwifi
# 或
sudo insmod "/lib/modules/$(uname -r)/kernel/drivers/net/wireless/iwlwifi/iwlwifi.ko"
用 lsmod 命令查看已加载的模块
lsmod
而从内核中把模块删除则通过 rmmod
命令
sudo rmmod iwlwifi
编写 hello 示例模块
内核模块也是通过 C 编写和编译的本机代码,只是由于它需要被 Linux 模块加载框架来载入,因此需要特殊的模块注册处理才能生效。内核模块能直接访问内核的功能,比一般的 C 编程要更加小心,预防出现 BUG 和安全问题。
编写内核模块时,使用的头文件为 /lib/modules/$(uname -r)/build/include
(通常 Linux 头文件在安装 kernel-image 同时安装,在一些发行版上 build
链接到 /usr/src/linux-headers-$(uname-r)/
)。
先看看一个简单的 hello 模块
/*
* hello.c
* 简单的 hello 内核模块
*/
#include <linux/module.h> /* 所有模块使用 module.h */
#include <linux/kernel.h> /* 包含内核常用的函数声明等 */
#include <linux/init.h> /* 进行内存初始化和清理 */
MODULE_AUTHOR("fengyc");
MODULE_DESCRIPTION("This is a demo.");
MODULE_VERSION("0.0.1");
MODULE_LICENSE("GPL");
static int __init hello_init(void)
{
printk(KERN_INFO "Hello, world!\n");
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_INFO "Goodbye, world!\n");
}
module_init(hello_init);
module_exit(hello_exit);
所有的模块都需要到 module.h 头文件,使用 module_init()
module_exit()
函数来注册模块入口和退出处理。这里的代码逻辑很简单,就是在模块加载时,打印 Hello, world!
,以及在退出时打印 Goodbye, world!
(由于代码运行在内核空间里面,不能直接使用用户空间的 print 函数,而要使用内核中的 printk 函数)
编译时通过一个 Makefile 文件进行,把这个 Makefile 文件置于 hello.c 同一目录下(Makefile 中使用 tab 作为分隔符 )
obj-m += hello.o
all:
make -C "/lib/modules/$(shell uname -r)/build" M=$(PWD) modules
clean:
make -C "/lib/modules/$(shell uname -r)/build" M=$(PWD) clean
现在,通过 make 即可生成模块 hello.ko
。现在可通过 modinfo 命令查看模块信息
modinfo hello.ko
然后通过 insmod / rmmod 命令加载、卸载模块,并用 dmesg 查看内核的环形缓存区( kernel ring buffer )的信息( printk 输出到了这里)
sudo insmod hello.ko
sudo rmmod hello
dmesg
如果一切顺利,就可以看到 hello.ko 模块的输出。而有个这个基本的代码框架,就可以继续深入,使用 netlink 作为接口,进行用户空间与内核空间的通信,并调用内核的功能,实现在用户态无法完成的动作。
p.s. 为了更好地使用 GUI 的协助,可使用参考使用 eclipse 内核开发环境 http://blog.chinaunix.net/uid-24512513-id-3183457.html