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

人生难得几回搏,此时不搏待何时。这篇文章主要讲述内核模块实验奔跑吧Linux内核相关的知识,希望能为你提供帮助。


文章目录

  • ??Debug??
  • ??可复用Makefile??
  • ??模块代码??
  • ??Makefile??
  • ??执行??
  • ??验证??
  • ??加载模块??
  • ??验证模块是否加载成功 —— lsmod??
  • ??验证模块是否加载成功 —— /sys/modules/*??
  • ??卸载模块??
  • ??给模块传递参数??
  • ??符号共享??
  • ??其他类型的符号??
  • ??链接其它本地文件??
  • ??查看系统文件内容??
内核模块(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
ModuleSizeUsed by
mytest163840
ccm204809
hid_generic163840
...

验证模块是否加载成功 —— /sys/modules/*加载完模块后,系统会在该目录下新建一个目录。
jiaming@ubuntu:/sys/module$ ll | grep mytest
drwxr-xr-x5 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]
...

【内核模块实验奔跑吧Linux内核】??符号在内核地址空间中的地址;符号属性;符号的字符串;哪些内核模块在使用这些符号??
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

    推荐阅读