linux内核模块基本框架练习
my_test.c代码段
1 /*
2 *包含两个头文件:
3 *<linux/init.h>对应的是内核源代码的include/linux/init.h文件,包含了module_init()和module_exit()函数声明。
4 *<linux/module.h>对应的是内核源码的include/linux/module.h文件,包含了MODULE_XXX等函数的声明。
5 */
6
7 #include <linux/init.h>
8 #include <linux/module.h>
9
10 /*
11 *内核模块初始化函数。
12 *这个函数在内核模块被加载时运行,可以使用insmod命令加载一个内核模块。
13 */
14 static int __init my_test_init(void)
15 {
16 printk("my first kernel module init\n");
17 return 0;
18 }
19
20 /*
21 *内核模块退出函数。
22 *可以使用rmmod命令卸载一个内核模块。
23 */
24 static void __exit my_test_exit(void)
25 {
26 printk("goodbye\n");
27 }
28
29 /*
30 *module_init()告诉内核这是该模块的入口。内核在各个模块初始化时有一个优先级顺序。
31 *对于驱动模块来说,它的优先级不是特别高,而且内核把所有的模块的初始化函数都存放在一个特殊的段中来管理。
32 */
33 module_init(my_test_init);
34 module_exit(my_test_exit);
35
36 /*
37 *MODULE_LICENSE()表示这个模块接受的软件许可协议。
38 *MODULE_AUTHOR()用来描述该模块的作者信息,可以包括作者的姓名和邮箱等。
39 *MODULE_DESCRIPTION()用来简单的描述该模块的用途或者功能介绍。
40 *MODULE_ALIAS()为用户空间提供一个合适的别名。
41 */
42 MODULE_LICENSE("GPL");
43 MODULE_AUTHOR("sjx");
44 MODULE_DESCRIPTION("my test kernel module");
45 MODULE_ALIAS("mytest");
Makefile文件
1 # BASHINCLUDE指向正在运行Linux的内核变异目录。
2 # 一般来说,Linux系统的内核模块都会安装到/lib/modules这个目录下,通过`uname -r`命令可以找到对应的内核版本。
3
4 BASHINCLUDE ?= /lib/modules/`uname -r`/build
5
6 # 表示该内核模块需要哪些目标文件,格式是:
7 # <模块名>-objs := <目标文件名>.o
8 mytest-objs := my_test.o
9
10 # 表示要生成的模块。注意:该模块的名字不能和目标文件的名字相同。
11 # 格式是:obj-m :=<模块名>.o
12 obj-m := mytest.o
13
14 # 表示要编译执行的动作。
15 all :
16 make -C $(BASHINCLUDE) M=$(CURDIR) modules;
17
18 # 表示执行make clean需要的动作。
19 clean :
20 $(MAKE) -C $(BASHINCLUDE) M=$(CURDIR) clean;
21 rm -f *.ko;
执行make生成mytest.ko文件
sjx@sjx-PC:~/work/study/test/test-1$ ls
Makefile my_test.c
sjx@sjx-PC:~/work/study/test/test-1$ make
make -C /lib/modules/`uname -r`/build M=/home/sjx/work/study/test/test-1 modules;
make[1]: 进入目录“/usr/src/linux-headers-5.4.70-amd64-desktop”
CC [M] /home/sjx/work/study/test/test-1/my_test.o
LD [M] /home/sjx/work/study/test/test-1/mytest.o
Building modules, stage 2.
MODPOST 1 modules
CC [M] /home/sjx/work/study/test/test-1/mytest.mod.o
LD [M] /home/sjx/work/study/test/test-1/mytest.ko
make[1]: 离开目录“/usr/src/linux-headers-5.4.70-amd64-desktop”
sjx@sjx-PC:~/work/study/test/test-1$ ls
Makefile modules.order Module.symvers my_test.c mytest.ko mytest.mod mytest.mod.c mytest.mod.o my_test.o mytest.o
可以通过file命令检查编译的模块是否正确.
sjx@sjx-PC:~/work/study/test/test-1$ file mytest.ko
mytest.ko: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), BuildID[sha1]=5fe83f4f20a74ddbc11fe613f9b1c638715029de, with debug_info, not stripped
也可以使用modinfo命令进一步检查
sjx@sjx-PC:~/work/study/test/test-1$ modinfo mytest.ko
filename: /home/sjx/work/study/test/test-1/mytest.ko
alias: mytest
description: my test kernel module
author: sjx
license: GPL
srcversion: 3F1D46D94CA8792B542B12A
depends:
retpoline: Y
name: mytest
vermagic: 5.4.70-amd64-desktop SMP mod_unload
加载内核模块
$ sudo insmod mytest.ko
查看日志信息,查看module_init()打印的信息
sjx@sjx-PC:~/work/study/test/test-1$ sudo dmesg |grep my
1445:[28028.835811] my first kernel module init
使用lsmod,查看mytest模块是否被加载
sjx@sjx-PC:~/work/study/test/test-1$ lsmod
Module Size Used by
mytest 16384 0
ses 20480 0
enclosure 16384 1 ses
scsi_transport_sas 40960 1 ses
uas 28672 0
加载模块之后,系统会在/sys/modules目录下新建一个目录。
sjx@sjx-PC:/sys/module/mytest$ tree -a
.
├── coresize
├── holders
├── initsize
├── initstate
├── notes
│ ├── .note.gnu.build-id
│ └── .note.Linux
├── refcnt
├── sections
│ ├── .exit.text
│ ├── .gnu.linkonce.this_module
│ ├── .init.text
│ ├── __mcount_loc
│ ├── .note.gnu.build-id
│ ├── .note.Linux
│ ├── .rodata.str1.1
│ ├── .strtab
│ └── .symtab
├── srcversion
├── taint
└── uevent
3 directories, 18 files