Chinaunix首页 | 论坛 | 博客
  • 博客访问: 140833
  • 博文数量: 27
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 136
  • 用 户 组: 普通用户
  • 注册时间: 2019-08-05 22:12
个人简介

纸上得来终觉浅,绝知此事要躬行。 生命不息,奋斗不止。

文章分类

全部博文(27)

文章存档

2021年(1)

2020年(26)

我的朋友

分类: LINUX

2020-04-10 23:55:42

内核模块是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内核》







阅读(2105) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~