02构造和运行模块

构造和运行模块

下载内核源码

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
#...

可以看到夹杂了两行我们模块初始化与清理的输出。


评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注