学习编写内核程序(一)kernel modules

您所在的位置:网站首页 内核驱动模块程序结构图 学习编写内核程序(一)kernel modules

学习编写内核程序(一)kernel modules

2023-07-05 06:41| 来源: 网络整理| 查看: 265

尽管单块内核比微内核快,但它的缺点是缺乏模块化和可扩展性。在现代的单片内核上,这已经通过使用内核模块解决了。内核模块(或可加载内核模式)是一个目标文件,其中包含可以在运行时扩展内核功能的代码(根据需要加载);当不再需要内核模块时,可以卸载它。大多数设备驱动程序以内核模块的形式使用。

一个kernel module例子

这是一个简单的kernel module例子,载入内核时发出“hello”,再卸载的时候发出“bye”。

#include #include #include MODULE_DESCRIPTION("My kernel module"); MOUDLE_AUTHOR("Me"); MODULE_LICENDE("GPL"); static int k_init(void) { pr_debug("hello\n"); return 0; } static void k_exit(void) { pr_debug("bye\n"); } module_init(k_init); module_exit(k_exit);

产生的消息不会再控制台出现需要通过日志守护进程(syslog)。

# cat /var/log/syslog | tail -2 Feb 20 13:57:38 asgard kernel: Hi Feb 20 13:57:43 asgard kernel: Bye # dmesg | tail -2 Hi Bye kernel module 编译

编译kernel module和用户程序编译不一样。module必须有和加载时一样的内核选项(option),等等很多。所以就引入了Makefile和Kbuild这两个文件。这里就不解释怎样写makefile,过一段时间我会有另一篇文章写。 首先,我们看Makefile:

KDIR = /lib/modules/'uname -r'/build kbuild: make -C $(KDIR) M = 'pwd' clean: make -C $(KDIR) M = 'pwd' clean

Kbuild:

EXTRA_CFLAGS = -Wall -g obj-m = supermodule.o supermodule-y = module-a.o module-b.o

在例子中对Makefile文件调用make将导致内核源目录(/lib/modules/ ’ uname -r ’ /build)中的make调用,并引用当前目录(M = ’ pwd ')。 一个Kbuild文件会包含编译一个或多个文件的指令。最简单的例子是编译一个文件的。上面的例子是需要编译多个文件的例子。首先需要编译module-a.c和module-b.o得到module-a.o和module-b.o文件。module-a.o和module-b.o会被链接进supermodule.o。最后用supermodule.o来创建supermodule.ko。

对于再Kbuild中后缀名的使用规则是这样的: 1.M是可加载的kernel module文件 2.Y表示要编译的对象文件的目标,然后链接到模块($(mode_name)-y)或内核(obj-y)中 任何其它的后缀名会直接被Kbuild忽视不会编译

这些后缀可以通过运行make menuconfig命令或直接编辑.config文件来配置内核。该文件设置了一系列变量,用于确定在构建时向内核添加哪些特性。例如,当使用make menuconfig添加BTRFS时,将CONFIG_BTRFS_FS = y添加到.config文件中。kbuild包含一行obj-$(CONFIG_BTRFS_FS):= BTRFS.o,即obj-y:= btrfs.o。这将编译btrfs.o,并将其链接到内核。在没这样变量设置之前还只是odj:=btrfs.o,这样就会被忽略不会有BTRFS。

加载和卸载kernel module

要加载内核模块,请使用insmod实用程序。该实用程序将*的路径作为参数接收。编译和链接模块的ko文件。使用rmmod命令从内核卸载模块,该命令接收模块名作为参数。

$ insmod module.ko $ rmmod module.ko

加载内核模块时,将执行作为module_init宏参数指定的例程。类似地,在卸载模块时,将执行作为module_exit参数指定的例程。

Debugging

排除内核模块的故障要比调试常规程序复杂得多。首先,内核模块中的一个错误可能导致阻塞整个系统。因此,可以大大降低故障排除的速度。为了避免重新启动,建议使用虚拟机(qemu、virtualbox、vmware)。当一个包含bug的模块被插入内核时,它将最终生成一个内核oops。内核oops是内核检测到的无效操作,只能由内核生成。对于一个稳定的内核版本,这一定是在该模块包含一个bug。在oops出现之后,内核将继续工作。 保存生成的oops消息非常重要。内核生成的消息保存在日志中,可以使用dmesg命令显示。为了确保没有丢失内核消息,建议直接从控制台插入/测试内核,或者定期检查内核消息。值得注意的是,oops可能因为编程错误而发生,也可能因为硬件错误而发生。 以下有个例子:

/* * Oops generating kernel module */ #include #include #include MODULE_DESCRIPTION ("Oops"); MODULE_LICENSE ("GPL"); MODULE_AUTHOR ("PSO"); #define OP_READ 0 #define OP_WRITE 1 #define OP_OOPS OP_WRITE static int my_oops_init (void) { int *a; a = (int *) 0x00001234; #if OP_OOPS == OP_WRITE *a = 3; #elif OP_OOPS == OP_READ printk (KERN_ALERT "value = %d\n", *a); #else #error "Unknown op for oops!" #endif return 0; } static void my_oops_exit (void) { } module_init (my_oops_init); module_exit (my_oops_exit);

这样就会产生oops



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3