内核模块实验奔跑吧Linux内核
Posted Jiamings
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内核模块实验奔跑吧Linux内核相关的知识,希望对你有一定的参考价值。
文章目录
- Debug
- 模块代码
- Makefile
- 执行
- 验证
- 加载模块
内核模块(Loadable Kernel Module,LKM),可在内核运行时加载一组目标代码来实现某个特定的功能,这样在实际使用 Linux 的过程中就不需要重新编译内核代码来实现动态扩展。
Debug
可复用Makefile
如果你想这么写Makefile,可能不太起作用,虽然好像能够具备良好的易用性。建议还是针对性地将文件名显示地写出来,就像下面这样,只需要改注释的那两行即可。(目前没有解决这个问题)
# Makefile to make module
BASEINCLUDE ?= /lib/modules/`uname -r`/build
# MODULE_NAME-objs := TARGET_FILE.o (TARGET_FILE.c)
parm_module-objs := module_parm.o
# obj-m := MODULE_NAME.o
obj-m := parm_module.o # module.o
all:
$(MAKE) -C $(BASEINCLUDE) M=`pwd` modules
clean:
$(MAKE) -C $(BASEINCLUDE) M=`pwd` clean
rm -f *.ko
模块代码
// my_test.c
// 包含了module_init()、module_exit()函数申明
// MODULE_xxx
// 模块入口,内核在初始化各个模块时有优先级顺序,内核把所有模块的初始化函数都存放在一个特别的段中来管理
static int __init my_test_init(void)
printk("my first kernel module init.\\n");
return 0;
// 模块退出函数
static void __exit my_test_exit(void)
printk("goodbye.\\n");
module_init(my_test_init);
module_exit(my_test_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jia ming");
MODULE_DESCRIPTION("my test kernel module.");
MODULE_ALIAS("mytest"); // 为用户空间提供合适的别名
Makefile
BASEINCLUDE ?= /lib/modules/`uname -r`/build # 模块安装路径
mytest-objs := my_test.o # 目标文件
obj-m := mytest.o # 定义模块名
all:
$(MAKE) -C $(BASEINCLUDE) M=$(PWD) modules;
clean:
$(MAKE) -C $(BASEINCLUDE) M=$(PWD) clean;
rm -f *.ko;
执行
jiaming@ubuntu:~/Documents/CProject$ tree .
.
├── Makefile
└── my_test.c
0 directories, 2 files
jiaming@ubuntu:~/Documents/CProject$ make
make -C /lib/modules/`uname -r`/build M=/home/jiaming/Documents/CProject modules;
make[1]: Entering directory /usr/src/linux-headers-5.10.0-1044-oem
CC [M] /home/jiaming/Documents/CProject/my_test.o
LD [M] /home/jiaming/Documents/CProject/mytest.o
MODPOST /home/jiaming/Documents/CProject/Module.symvers
CC [M] /home/jiaming/Documents/CProject/mytest.mod.o
LD [M] /home/jiaming/Documents/CProject/mytest.ko
make[1]: Leaving directory /usr/src/linux-headers-5.10.0-1044-oem
jiaming@ubuntu:~/Documents/CProject$ tree . # 编译完成后会生成 mytest.ko 文件
.
├── Makefile
├── modules.order
├── Module.symvers
├── my_test.c
├── mytest.ko
├── mytest.mod
├── mytest.mod.c
├── mytest.mod.o
├── my_test.o
└── mytest.o
0 directories, 10 files
验证
jiaming@ubuntu:~/Documents/CProject$ file mytest.ko # x86_64 ELF 文件
mytest.ko: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), BuildID[sha1]=80fe0e29ec10947eedd66ab3b21be2ab33f3b533, not stripped
jiaming@ubuntu:~/Documents/CProject$ modinfo mytest.ko
filename: /home/jiaming/Documents/CProject/mytest.ko
alias: mytest
description: my test kernel module.
author: Jia ming
license: GPL
srcversion: B30B37B2BFE0784D8BC61C4
depends:
retpoline: Y
name: mytest
vermagic: 5.10.0-1044-oem SMP mod_unload modversions
加载模块
sudo insmod mytest.ko
dmesg
查看 printk
输出内容。
...
[182158.234851] [drm] ib test on ring 2 succeeded in 0 usecs
[182158.234864] [drm] ib test on ring 3 succeeded in 0 usecs
[182158.234880] [drm] ib test on ring 4 succeeded in 0 usecs
[184937.411931] my first kernel module init.
验证模块是否加载成功 —— lsmod
jiaming@ubuntu:~/Documents/CProject$ lsmod
Module Size Used by
mytest 16384 0
ccm 20480 9
hid_generic 16384 0
...
验证模块是否加载成功 —— /sys/modules/*
加载完模块后,系统会在该目录下新建一个目录。
jiaming@ubuntu:/sys/module$ ll | grep mytest
drwxr-xr-x 5 root root 0 Aug 31 11:09 mytest/
卸载模块
sudo rmmod mytest
-
dmesg
命令查看:
...
[185276.971010] goodbye.
-
lsmod
中没有 mytest 模块。 -
/sys/modules
中没有 mytest 目录。
给模块传递参数
可接受参数的模块
#include <linux/init.h> // 包含了module_init()、module_exit()函数申明
#include <linux/module.h> // MODULE_xxx
static int debug = 1;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable debugging information");
#define dprintk(args...) \\
if(debug) \\
printk(KERN_DEBUG args); \\
static int mytest = 100;
module_param(mytest, int, 0644);
MODULE_PARM_DESC(mytest, "test for module parameter");
// 模块入口,内核在初始化各个模块时有优先级顺序,内核把所有模块的初始化函数都存放在一个特别的段中来管理
static int __init my_test_init(void)
dprintk("my first kernel module init.\\n");
dprintk("module parameter=%d\\n", mytest);
return 0;
// 模块退出函数
static void __exit my_test_exit(void)
printk("goodbye.\\n");
module_init(my_test_init);
module_exit(my_test_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jia ming");
MODULE_DESCRIPTION("my test kernel module.");
MODULE_ALIAS("mytest");
传入参数
sudo insmod mytest.ko mytest=200
查看结果
dmesg
查看
...
[185891.125462] my first kernel module init.
[185891.125473] module parameter=200
符号共享
模块之间相互调用。
EXPORT_SYMBOL() # 把函数或符号对全部内核代码公开,将函数以符号的方式导出给内核中的其它模块使用
EXPORT_SYMBOL_GPL() # 只能包含 GPL 许可的模块,MODULE_LICENSE("GPL")
导出的内核符号表可以通过 /proc/kallsyms
查看
...
0000000000000000 t cleanup_module [video]
0000000000000000 T acpi_video_get_edid [video]
0000000000000000 T acpi_video_unregister [video]
0000000000000000 T acpi_video_get_backlight_type [video]
0000000000000000 T acpi_video_set_dmi_backlight_type [video]
0000000000000000 t acpi_video_detect_exit [video]
0000000000000000 T acpi_video_register [video]
0000000000000000 t bpf_prog_6deef7357e7b4530 [bpf]
0000000000000000 t bpf_prog_6deef7357e7b4530 [bpf]
...
符号在内核地址空间中的地址;符号属性;符号的字符串;哪些内核模块在使用这些符号
module1_file.c
// 包含了module_init()、module_exit()函数申明
// MODULE_xxx
static int debug = 1;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable debugging information");
static int mytest = 100;
module_param(mytest, int, 0644);
MODULE_PARM_DESC(mytest, "test for module parameter");
// 模块入口,内核在初始化各个模块时有优先级顺序,内核把所有模块的初始化函数都存放在一个特别的段中来管理
static int __init my_test_init(void)
dprintk("my first kernel module init.\\n");
dprintk("module parameter=%d\\n", mytest);
return 0;
// 模块退出函数
static void __exit my_test_exit(void)
printk("goodbye.\\n");
static int getvalue(void)
return mytest;
EXPORT_SYMBOL(getvalue);
module_init(my_test_init);
module_exit(my_test_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jiaming");
MODULE_DESCRIPTION("my test kernel module.");
MODULE_ALIAS("module1");
Makefile
# Makefile to make module
BASEINCLUDE ?= /lib/modules/`uname -r`/build
# MODULE_NAME-objs := TARGET_FILE.o (TARGET_FILE.c)
module1-objs := module1_file.o
# obj-m := MODULE_NAME.o
obj-m := module1.o
all:
$(MAKE) -C $(BASEINCLUDE) M=`pwd` modules
clean:
$(MAKE) -C $(BASEINCLUDE) M=`pwd` clean
rm -f *.ko
module2_file.c
// 包含了module_init()、module_exit()函数申明
// MODULE_xxx
extern int getvalue(void);
static int debug = 1;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable debugging information");
// static int mytest = 100;
// module_param(mytest, int, 0644);
// MODULE_PARM_DESC(mytest, "test for module parameter");
// 模块入口,内核在初始化各个模块时有优先级顺序,内核把所有模块的初始化函数都存放在一个特别的段中来管理
static int __init my_test_init(void)
dprintk("my second kernel module init.\\n");
static int mytest;
mytest = getvalue();
dprintk("module parameter=%d\\n", mytest);
return 0;
// 模块退出函数
static void __exit my_test_exit(void)
printk("goodbye.\\n");
module_init(my_test_init);
module_exit(my_test_e以上是关于内核模块实验奔跑吧Linux内核的主要内容,如果未能解决你的问题,请参考以下文章