第一节:Re-Hello World!
| 2025-7-1
Words 2575Read Time 7 min
写到一半的时候,发现有更新的参考资料,所以重新写一下这里面的内容,之前的参考资料都已经是 2.6 时代的了
参考资料:

给系统加个模块瞧瞧!

首先看到helloworld节的内核模块,遇到一个之前没遇到过的函数 printk ,那我应该看看它是干啥的。
然后发现,这玩意可以在 include/linux/printk.h 找到,有空的时候可以读一下这里面的内容~最后会调用上 int _printk(const char *fmt, ...); 这个
 
那要怎么编译呢?其实只要在当前目录写好内核模块的代码,然后直接调用 Makefile 就可以了,然后内核模块调用当前系统的内核源码~如果你用的是直接克隆的代码,那应该也是同理的。为什么不用上面的buildroot了?可能是突然发现系统自带的内核会更方便吧QAQ
Makefile 里面写好各个操作步骤,比如 -C 指定内核目录,-M指定模块目录
然后一编译就出现了一个错误
看起来现在强制要求内核模块的许可证了呢,那就补上吧,直接加一个 MODULE_LICENSE("WTFPL") ,然后再次make就完事了,会在目录生成若干个文件,可以通过 modinfo hello-1.ko 来看这个内核模块文件的一些细节。可以看到刚刚设置的LICENSE已经有显示了,当然也可以继续写上一些作者之类的信息。
然后就可以尝试插入一下了。
那我们再尝试修改一下返回值为非0值,会发生什么呢?
看到了十分眼熟的报错,之前在折腾像mesa之类的东西的时候也遇到过类似的东西,不过目前还不知道怎么这些内容(coredumpctl之类的)

我不要你的名字,我要我的名字

参考资料说,这个函数名其实并不是固定要求的,其实你也可以根据自己的喜欢使用自己的函数名,只要记得使用 module_initmodule_exit 登记一下就行~
然后我们可以和 linux/drivers/char/Makefile 做对比,我们makefile用的是 obj-m ,而这个文件用的是 obj-y ,甚至还有一些用 obj-$(CONFIG_TTY_PRINTK),其实就是设置CONFIG_选项为y还是m的区别,也就是到底要不要预置到内核的二进制中。

哎呀,你为什么要给我塞初始化和退出宏?

在这章里面,可以留意到参考资料给代码加了一个 __init__exit 宏,那它到底是干嘛的呢?又是在哪里写下它的规则的呢?
我们可以看一下内核代码的 include/linux/init.h
看起来它是用来定义段地址的?那行,我们编译出来的时候再看看。编译完成后,使用 readelf -S hello-3.ko 查看其内容:
是喔,看起来__init 定义后,就有 .init.text 的定义了,重新看hello-2.ko,你会发现本来是没有的。

模块是我写的,你要做什么?!

虽然教程中有写,可以用 MODULE_AUTHOR 来声明模块作者,但我用 grep -rn "MODULE_AUTHOR" . 看了一下,似乎并没有找到这么用的人,这是为什么呢?
这是因为,你用dnf装的所谓的内核代码,并不是完整的内核代码,它只包含一些必要的,能让你编译内核模块用的一些代码,还有一些头文件,所以你当然就不能在这里找到作者信息咯~你得下载完整的内核代码再这样找,就能找到了。

咦,装载模块的时候我是不是可以传点东西……

模块装载的时候当然可以传变量,而且还是在 insmod 的时候就可以传!
先声明类似 static int myint=420; 然后跑一下这个函数进行一下这些设置
然后在insmod的时候设置一下,比如
在加载模块的时候用 pr_info 来打印一下变量就能看到啦~

我感觉其实可以不用写在一块?

其实文件也可以分开写,也就是各个模块方法写在不同的文件上,关键是在 Makefile 上要写一个类似这样的:

为什么内核模块一般不能跨版本使用?

内核模块一般是根据某个源码编译出来的,它会在 vermagic 里存储内核的版本信息,就像下面这样
也就是说,即使内核的主版本号完全一致,因为子版本(比如 EXTRAVERSION)不一致而导致加载不上,加载时要求 vermagic 这个字符串严格保持一致,避免因为内核版本不同而导致系统出现崩溃。即便如此,也可以通过 --force-vermagic 来解决这个问题,但很不安全就是了。
发行版的内核一般会放在 /boot/config-* ,厂商自己编的一般就开 /proc/config.gz 并使用 zcat 来看了。

神奇,为啥我可以直接使用 pr_info?

在内核模块通过 insmod 或者 modprobe 被加载的时候,会从内核本身获得需要使用的符号,所以我们并不需要使用IO库来进行信息打印,只需要借助系统本身有的符号就行。这些符号可以在 /proc/kallsyms 看到。

我可以怎么看程序的运行过程?

可以通过 strace 看到程序的运行过程,比如可以先写个hello world试试
然后用 gcc -Wall -o hello hello.c 来编译,生成 hello 后,执行 strace ./hello ,会得到下面一串东西,就能看到调用系统调用所执行的一串东西啦
其中 write(1, "hello", 5hello) 这行就是将hello输出到终端上。可以输入 man 2 write 来看这些东西的具体用法。
 

其它事

我靠,SSH怎么传vim的剪贴板?

用鼠标框选或者键盘框选对应选块,然后直接输入 "+y ,框选内容就到本机的剪贴板啦~

Lazyvim 的一些快捷操作

总开关:空格
切换焦点:CTRL+h j k l
切换buffer窗口:Shift+l
创建文件: :w filename
终端: CTRL+/
 
Loading...