大多数的Linux驱动程序,都以内核模块的形式,运行在Linux内核中。
Linux内核与内核模块内核模块可以通过insmod/rmmod命令加载/卸载。此过程中不需要重启动任何东西,这使得调试内核模块非常方便。
写一个简单的内核模块
Hello.c:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
static int __init init_hello(void)
{
pr_info("Hello world\n");
return 0;
}
static void __exit cleanup_hello(void)
{
pr_info("Goodbye world\n");
}
module_init(init_hello);
module_exit(cleanup_hello);
保存hello.c到任意目录,然后再同一目录中创建Makefile文件。
Makefile:
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
注意,第4和7行中make前的缩进是<tab>。
确认编译内核模块的头文件都已经在当前系统中安装了。
运行:
# ls /usr/src/kernels/$(shell uname -r)
如果此目录不存在,则需要安装或下载相应的内核头文件。
例如,在我的Linux (Fedora Core 26)中,我是通过下面命令安装:
# dnf install kernel-devel
编译:
# make
make -C /lib/modules/4.11.6-201.fc25.x86_64/build M=/opt4/foo/hello modules
make[1]: Entering directory '/usr/src/kernels/4.11.6-201.fc25.x86_64'
Building modules, stage 2.
MODPOST 1 modules
make[1]: Leaving directory '/usr/src/kernels/4.11.6-201.fc25.x86_64'
[root@euca-10-254-112-100 hello]# ls
Makefile hello.c hello.mod.c hello.o
Module.symvers hello.ko hello.mod.o modules.order
运行:
[root@euca-10-254-112-100 hello]# insmod hello.ko
[root@euca-10-254-112-100 hello]# dmesg | tail -1
[ 2467.713120] Hello world
[root@euca-10-254-112-100 hello]# rmmod hello.ko
[root@euca-10-254-112-100 hello]# dmesg | tail -1
[ 2516.974827] Goodbye world
pr_info(...) 等同于printk(KERN_INFO...)。
内核日志也可以通过 journalctl -k 命令查看。
内核模块的额外信息
可以给内核模块加上描述和许可:
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxx");
MODULE_DESCRIPTION("xxxx");
在hello.c的尾部加上以上3行,执行 make 命令。
使用modinfo命令查看内核模块的额外信息:
[root@euca-10-254-112-100 hello]# modinfo hello.ko
filename: /opt4/foo/hello/hello.ko
description: xxxx
author: xxx
license: GPL
depends:
vermagic: 4.11.6-201.fc25.x86_64 SMP mod_unload
内核模块的启动参数
Hello.c:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
static char * str = "world";
module_param(str, charp, 0);
MODULE_PARM_DESC(str, "the name to say hello");
static int __init init_hello(void)
{
pr_info("Hello %s\n", str);
return 0;
}
static void __exit cleanup_hello(void)
{
pr_info("Goodbye %s\n", str);
}
module_init(init_hello);
module_exit(cleanup_hello);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxx");
MODULE_DESCRIPTION("xxxx");
上述代码给内核模块启动时,增加了一个字符串类型的参数。现在,可以跟任何人说 hello 了。
运行:
[root@euca-10-254-112-100 hello]# insmod hello.ko str="mountain"
[root@euca-10-254-112-100 hello]# dmesg | tail -1
[ 5487.679867] Hello mountain
[root@euca-10-254-112-100 hello]# rmmod hello.ko
[root@euca-10-254-112-100 hello]# dmesg | tail -1
[ 5495.265233] Goodbye mountain
网友评论