构造和运行模块
下载内核源码
Linux设备驱动程序这本书使用的内核版本是2.6.x, 我身边电脑安装的是archlinux, 内核5.17.9。
ole@archlinux ~]$ uname -a
[Linux archlinux 5.17.9-arch1-1 #1 SMP PREEMPT Wed, 18 May 2022 17:30:11 +0000 x86_64 GNU/Linux
实际上,archlinux当前内核的源码树已经存在于本地了。
ole@archlinux ~]$ ls -l /usr/src/
[total 0
lrwxrwxrwx 1 root root 35 May 25 06:00 linux -> ../lib/modules/5.18.0-arch1-1/build
额, 这里不知道为什么已经是更新的5.18.0了。 可能是最近安装软件的时候,触发了内核源码的更新。
可以去 https://www.kernel.org/ 下载标准的内核5.17.9, 从页面中 https://cdn.kernel.org/pub/linux/kernel/v5.x/ 找到并下载 https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.17.9.tar.xz
解压内核
cd ~
mkdir kernelbuild
cd kernelbuild
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.17.9.tar.xz
tar -xvJf linux-5.17.9.tar.xz
Hello World模块
第一个模块(仅作演示)
单独建个文件夹
cd ~/Documents
mkdir helloworld
cd hello world
只需要hello.c和Makefile即可。
下面是hello.c
vim hello.c
#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);
linux/init.h, linux/module.h 一般模块开发都会包含这两个头文件。
MODULE_LICENSE宏说明模块源码采用BSD/GPL双许可证, 可省略
module_init,module_exit指定模块的初始化函数,模块删除时的清理函数
printk是内核实现的格式化输出函数,内核输出可用dmesg命令或journalctl等命令查看。 注意输出级别KERN_ALERT后无逗号分割。 注意内核中无法使用libc库,也就无法使用标准库函数printf。
然后是Makefile, 只有一行
vim Makefile
obj-m := hello.o
《Linux设备驱动》说obj-m是GNU make的扩展语法,进一步请参考这本书或GNU make
然后在此目录(~/Documents/helloworld)下编译此模块
make -C ~/kernelbuild/linux-5.17.9 M=`pwd` modules
#或者用当前系统内核源码编译
#make -C /usr/src/linux M=`pwd` modules
make -C ~/kernelbuild/linux-5.17.9 指示跳转到对应内核目录下编译, 因此会使用~/kernelbuild/linux-5.17.9/Makefile 来编译。
M= 是~/kernelbuild/linux-5.17.9/Makefile中的一个变量,指明要跳回给定的目录继续编译。 结合后面pwd命令的结果,已经给定的目标modules, 指明在编译modules目标时,跳回当前目录继续编译。
《Linux设备驱动》中还有一种更普适的编译方式, 有兴趣可以进一步参考。
以上编译可能会失败,查看具体报错内容,参考~/kernelbuild/linux-5.17.9/README , ~/kernelbuild/linux-5.17.9/admin-guides/README.rst , 以及借助搜索引擎和别人的一些博客,可以解决。
cd ~/kernelbuild/linux-5.17.9
# 一个全新的内核源码树, 编译前需要配置需要编译的内容。
#根据当前运行内核做配置
make localyesconfig
#若有其他需要调整的, 再运行make menuconfig调整
#可能会找不到头文件
#make asm-generic
#可能会报找不到modules.lds,查看~/kernelbuild/linux-5.17.9/Makefile,可能需要运行如下目标
make modules_prepare
参考内核文档配置需要编译的内容: https://kernel.org/doc/html/v5.17/admin-guide/README.html
make有个V选项, 可以指定输出的详细内容, 可以设为V=1或V=2 查看具体错误
make htmldocs 可以将内核源码中的.rst等文件 在本地生成和 在线文档(https://kernel.org/doc/html/v5.17)一样的html文档。(可能需要安装一些依赖的软件)
https://www.cnblogs.com/wrjvszq/p/4257164.html,https://www.cnblogs.com/oddcat/articles/9812750.html,https://lkml.org/lkml/2019/11/12/1333,https://github.com/raspberrypi/linux/issues/1441
最终编译通过后,可以看到生成了内核模块 hello.ko
ole@archlinux helloworld]$ ls ~/Documents/helloworld/
[hello.c hello.ko hello.mod hello.mod.c hello.mod.o hello.o Makefile modules.order Module.symvers
将内核模块装载到正在运行的内核中, 然后再从内核中卸载
cd ~/Documents/helloworld/
#将hello.ko装载到内核
insmod hello.ko
#将hello.ko从内核中卸载
rmmod hello.ko
命令行没有输出, 内核的输出可能输出到了一些日志当中去了
ole@archlinux linux-5.17.11]$ cd ~/Documents/helloworld/
[ole@archlinux helloworld]$ sudo dmesg -C
[ole@archlinux helloworld]$ sudo insmod hello.ko
[ole@archlinux helloworld]$ sudo rmmod hello.ko
[ole@archlinux helloworld]$ sudo dmesg
[#...
448877.765361] Hello, world
[#...
448891.904974] Goodbye, cruel world
[#...
可以看到夹杂了两行我们模块初始化与清理的输出。
发表回复