内核模块实验奔跑吧Linux内核

Posted Jiamings

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内核模块实验奔跑吧Linux内核相关的知识,希望对你有一定的参考价值。


文章目录

内核模块(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
#include <linux/init.h> // 包含了module_init()、module_exit()函数申明
#include <linux/module.h> // 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

#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");


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

#include <linux/init.h> // 包含了module_init()、module_exit()函数申明
#include <linux/module.h> // MODULE_xxx

extern int getvalue(void);

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 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内核的主要内容,如果未能解决你的问题,请参考以下文章

Linux内核内存管理总结

进程调度案例分析:为何不能调度?

内核模块编译实验

实验二 内核模块编译

GCC内联汇编常见陷阱

内存管理实战案例分析3:为何分配不出一个页面?