内核模块是Linux内核向外部提供的一个插口,其全称为
动态可加载内核模块(Loadable Kernel Module,LKM),简称为模块。Linux内核之所以提供模块机制,是因为它本身是一个单内核(monolithic kernel)。单内核的最大优点是效率高,因为所有的内容都集成在一起,但其缺点是可扩展性和可维护性相对较差,模块机制就是为了弥补这一缺陷。
一.什么是模块
模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行,这与运行在用户空间的进程是不同的。模块通常由一组函数和数据结构组成,用来实现一种文件系统、一个驱动程序或其他内核上层的功能。
二. 编写一个简单的模块
模块和内核都在内核空间运行,模块编程在一定意义上说就是内核编程。因为内核版本的每次变化,其中的某些函数名也会相应地发生变化,因此模块编程与内核版本密切相关。
在学习程序的时候,基本都是从打印hello world!来实现的,所以,现在也尝试着编写一个简单的内核模块,然后加载在内核中去运行,体会与应用层不一样的实现。
三.实现过程
使用cat /proc/version 得到本机Linux的基本信息,如下:
Linux version 4.4.0-142-generic (buildd@lcy01-amd64-006)
(gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.4) ) #168~14.04.1-Ubuntu SMP Sat Jan 19 11:26:28 UTC 2019
这里主要实现一个.c文件和一个Makefile
hello.c如下:
#include
#include
#include
说明:不知道什么原因,头文件后面的部分,博客上显示不了
static void __init test_module_init(void)
{
printk("The test_module_init init !\n");
printk("Hello world!\n");
}
static void __exit test_module_exit(void)
{
printk("The test_module_exit exit!\n");
}
module_init(test_module_init);
module_exit(test_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("CHENG");
MODULE_DESCRIPTION("first module for hello world");
对各部分简单介绍:
#include
#include
#include
//这三个头文件是必须要有的,里面包含了函数声明和宏,缺少会出现错误
test_module_init()
//模块的入口函数,模块运行时,会从init函数开始执行
test_module_exit()
//模块的退出,在模块退出时,会从exit函数开始执行
module_init(test_module_init); //入口处
module_exit(test_module_exit); //出口处
MODULE_LICENSE("GPL"); //接受软件许可证协议
MODULE_AUTHOR("CHENG"); //模块作者的信息描述
MODULE_DESCRIPTION("first module for hello world"); //用来描述模块的用途或者功能实现
Makefile的内容:
注意:空格不能随便添加在makefile文件中
#makefile file
obj-m := hello.o
#编译为模块
CURRENT_PATH:=$(shell pwd) #当前工作路径
LINUX_KERNEL:=$(shell uname -r) #内核的版本信息
LINUX_KERNEL_PATH:= /usr/src/linux-headers-$(LINUX_KERNEL)
all:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules #编译模块
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean #清除模块
这里就不放截图了,将上面连个文件保存后,终端执行make 命令,就能编译出如下文件(黑色文件为原有文件)
hello.c hello.ko hello.mod.c hello.mod.o hello.o Makefile modules.order Module.symvers
使用模块加载命令insmod hello.ko ,然后用lsmod命令进行查看,若成功加载,则
root@ubuntu:/home/module_hello# lsmod //命令查看内核模块
Module Size Used by
hello 16384 0
xt_nat 16384 4
iptable_nat 16384 1
重复加载会出现以下结果:
root@ubuntu:/home/module_hello# insmod hello.ko //命令加载内核模块
insmod: ERROR: could not insert module hello.ko: File exists
使用dmesg命令可以查看模块运行的结果,会有很多的打印信息,暂时不用关注这些,以后再深入了解。
[149912.283813] [] SyS_finit_module+0xe/0x10
[149912.283890] [] entry_SYSCALL_64_fastpath+0x22/0xcb
[150620.196179] The test_module_init init !
[150620.196184] Hello world!
[150620.196215] do_init_module: 'hello'->init suspiciously returned 12, it should follow 0/-E convention
[150620.196215] do_init_module: loading module anyway...
[150620.196221] CPU: 0 PID: 10966 Comm: insmod Tainted: P OE 4.4.0-142-generic #168~14.04.1-Ubuntu
[150620.196222] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 07/02/2015
[150620.196227] 0000000000000000 ffff880044eb7d38 ffffffff813ee007 ffffffffc0442000
[150620.196231] ffff88005372ea80 ffff880044eb7d60 ffffffff8118cf32 0000000000000001
[150620.196234] ffffffffc0442050 ffff880066f140c0 ffff880044eb7ea0 ffffffff8110b0ed
[150620.196237] Call Trace:
[150620.196263] [] dump_stack+0x63/0x8c
[150620.196271] [] do_init_module+0x90/0x1d2
[150620.196274] [] load_module+0x145d/0x1b50
[150620.196299] [] ? __symbol_put+0x40/0x40
[150620.196306] [] ? kernel_read+0x41/0x60
[150620.196309] [] SYSC_finit_module+0x7e/0xa0
[150620.196313] [] SyS_finit_module+0xe/0x10
[150620.196353] [] entry_SYSCALL_64_fastpath+0x22/0xcb
[150674.456301] The test_module_exit exit!
root@ubuntu:/home/module_hello# rmmod hello.ko //命令卸载内核模块
root@ubuntu:/home/module_hello# lsmod //再次查看内核模块,hello.ko模块被卸载掉了
Module Size Used by
xt_nat 16384 4
iptable_nat 16384 1
nf_conntrack_ipv4 16384 1
看到红色字体部分,就是模块所实现的内容,那么我们利用这个模块框架,再模块中实现我们自己想要的功能,再将模块加入带内核中运行,就实现了简单的内核编程。
这是指一个最基本的实现,却包含很多模块相关的知识,有点麻雀虽小五脏俱全的意思了,以后利用这个模块模板,再进一步的学习了。
四.差异对比: 应用程序与内核模块程序的比较
|
C语言应用程序
|
内核模块程序
|
使用函数
|
Libc库
|
内核函数
|
运行空间
|
用户空间
|
内核空间
|
运行权限
|
普通用户
|
超级用户
|
入口函数
|
main()
|
module_init()
|
出口函数
|
exit()
|
module_exit()
|
编译
|
Gcc –c
|
Makefile
|
连接
|
Gcc
|
insmod
|
运行
|
直接运行
|
insmod
|
调试
|
Gdb
|
kdbug, kdb,kgdb等
|
参考书籍:《奔跑吧Linux内核》
阅读(2115) | 评论(0) | 转发(0) |