0x3 可装载内核模块(LKM)
可装载内核模块(LKM)
我们的Linux kernel虽然成功启动了,但是其本身的功能似乎有些单调,那么我们不如自己编写可装载内核模块(Loadable Kernel Modules)来扩充内核的功能吧!
〇、预备知识
前面我们讲到,LKM同样是ELF格式文件,但是其不能够独立运行,而只能作为内核的一部分存在
同样的,对于LKM而言,其所处在的内核空间与用户空间是分开的,对于通常有着SMAP/SMEP保护的Linux而言,这意味着LKM并不能够使用libc中的函数,也不能够直接与用户进行交互
虽然我们同样能够使用C语言编写LKM,但是作为内核的一部分,LKM编程在一定意义上便是内核编程, 内核版本的每次变化意味着某些函数名也会相应地发生变化,因此LKM编程与内核版本密切相关
一、简单的测试模块
我们来编写这样一个简单的内核模块,其功能是在载入/卸载时会在内核缓冲区打印字符串:
1 |
|
头文件
linux/module.h
:对于LKM而言这是必须包含的一个头文件linux/kernel.h
:载入内核相关信息linux/init.h
:包含着一些有用的宏
通常情况下,这三个头文件对于内核模块编程都是不可或缺的
入口点/出口点
内核模块的初始化函数在编译时通过 module_init()
定义,在内核模块被载入时会调用所定义的函数,这里我们将初始化函数设定为 kernel_module_init
内核模块的卸载函数在编译时通过 module_exit()
定义,在内核模块被卸载时会调用所定义的函数,这里我们将卸载函数定义为 kernel_module_exit
其他…
__init
与__exit
宏:用来显式标识内核模块出入口函数MODULE_AUTHOR() & MODULE_LICENSE()
:声明内核作者与发行所用许可证
二、编译内核模块:makefile 与 Kbuild
与一般的可执行文件所不同的是,我们应当使用 Makefile 来构建一个内核模块,并使用 Kbuild 说明编译规则
首先创建一个 Kbuild
文件,写入如下内容:
1 |
|
简单说明一下这个 Kbuild:
MODULE_NAME ?= hellokernel
:定义了一个局部变量MODULE_NAME
,值为hellokernel
obj-m += $(MODULE_NAME).o
:指定了编译的结果应当为.ko
文件,即可装载内核模块,同时指定了模块名为hellokernel
,当模块编译时相应的源文件所编译得到的中间文件会先被链接为hellokernel.o
,之后再构建.ko
文件;其他可替代标识为:obj-y
编译进内核 ,obj-n
不编译$(MODULE_NAME)-y += main.o
:编译该内核模块所需要的文件,例如这里我们需要main.c
,那么我们就在该变量中添加main.o
接下来创建一个名为 Makefile
的文件,写入如下内容:
1 |
|
这里简单说明一下这个 makefile :
LINUX_KERNEL_SRC := /lib/modules/$(shell uname -r)/build
:当前系统所使用的内核源码路径make -C $(LINUX_KERNEL_SRC) M=$(CURRENT_PATH) modules
:-C
表示进入源码目录进行编译,M=
意味着当前正在编译一个外部模块、该变量用以指示外部模块的源码目录,modules
则意为进行内核模块编译操作make -C $(LINUX_KERNEL_SRC) M=$(CURRENT_PATH) clean
:同上,不过此时进行的是清理指令
然后就是直接make即可
1 |
|
MD,沟槽的微软,WSL中没有提供/lib/modules/
WSL2的内核是修改过的,无法使用 ubuntu上游的内核头文件和modules文件,因此,我们需要手动编译并安装一个版本。
别用WLS搞。。。
Ubuntu20.04下班这个,真别用
WSL补上内核头文件和modules文件
1. 下载对应版本的内核代码
1 |
|
到WSL git仓库,找到对应的release:
1 |
|
2. 编译和安装
cd进文件夹后
1 |
|
3. 安装headers
1 |
|
4. else
可能那个文件夹名字会只叫版本号,记得重命名一下,改成uname -r
的结果
1 |
|
看到这里,我建议你装个Vmware吧
17好像初步解决了和Hyper-V冲突的问题了