Linux模块机制浅析
Linux模块机制浅析
Linux允许用户通过插入模块,实现干预内核的目的。一直以来,对linux的模块机制都不够清晰,因此本文对内核模块的加载机制进行简单地分析。
模块的Hello World!
我们通过创建一个简单的模块进行测试。首先是源文件main.c和Makefile。
florian@florian-pc:~/module$ cat main.c
#include<linux/module.h>
#include<linux/init.h>
static int __init init(void)
{
printk("Hi module!\n");
return 0;
}
static void __exit exit(void)
{
printk("Bye module!\n");
}
module_init(init);
module_exit(exit);
其中init为模块入口函数,在模块加载时被调用执行,exit为模块出口函数,在模块卸载被调用执行。
florian@florian-pc:~/module$ cat Makefile
obj-m += main.o
#generate the path
CURRENT_PATH:=$(shell pwd)
#the current kernel version number
LINUX_KERNEL:=$(shell uname -r)
#the absolute path
LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)
#complie object
all:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
#clean
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
其中,obj-m指定了目标文件的名称,文件名需要和源文件名相同(扩展名除外),以便于make自动推导。
然后使用make命令编译模块,得到模块文件main.ko。
florian@florian-pc:~/module$ make
make -C /usr/src/linux-headers-2.6.35-22-generic M=/home/florian/module modules
make[1]: 正在进入目录 `/usr/src/linux-headers-2.6.35-22-generic'
Building modules, stage 2.
MODPOST 1 modules
make[1]:正在离开目录 `/usr/src/linux-headers-2.6.35-22-generic'
使用insmod和rmmod命令对模块进行加载和卸载操作,并使用dmesg打印内核日志。
florian@florian-pc:~/module$ sudo insmod main.ko;dmesg | tail -1
[31077.810049] Hi module!
florian@florian-pc:~/module$ sudo rmmod main.ko;dmesg | tail -1
[31078.960442] Bye module!
通过内核日志信息,可以看出模块的入口函数和出口函数都被正确调用执行。
模块文件
使用readelf命令查看一下模块文件main.ko的信息。
florian@florian-pc:~/module$ readelf -h main.ko
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 1120 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 40 (bytes)
Number of section headers: 19
Section header string table index: 16
我们发现main.ko的文件类型为可重定位目标文件,这和一般的目标文件格式没有任何区别。我们知道,目标文件是不能直接执行的,它需要经过链接器的地址空间分配、符号解析和重定位的过程,转化为可执行文件才能执行。
那么,内核将main.ko加载后,是否对其进行了链接呢?
模块数据结构
首先,我们了解一下模块的内核数据结构。
linux3.5.2/kernel/module.h:220
struct module
{
……
/* Startup function. */
int (*init)(void);
……
/* Destruction function. */
void (*exit)(void);
……
};
模块数据结构的init和exit函数指针记录了我们定义的模块入口函数和出口函数。
模块加载
模块加载由内核的系统调用init_module完成。
linux3.5.2/kernel/module.c:3009
/* This is where the real work happens */
SYSCALL_DEFINE3(init_module, void __user *, umod,
unsigned long, len, const char __user *, uargs)
{
struct module *mod;
int ret = 0;
……
/* Do all the hard work */
mod = load_module(umod, len, uargs);//模块加载
……
/* Start the module */
if (mod->init != NULL)
ret = do_one_initcall(mod->init);//模块init函数调用
……
return 0;
}
系统调用init_module由SYSCALL_DEFINE3(init_module...)实现,其中有两个关键的函数调用。load_module用于模块加载,do_one_initcall用于回调模块的init函数。
函数load_module的实现为。
linux3.5.2/kernel/module.c:2864
/* Allocate and load the module: note that size of section 0 is always
zero, and we rely on this for optional sections. */
static struct module *load_module(void __user *umod,
unsigned long len,
const char __user *uargs)
{
struct load_info info = { NULL, };
struct module *mod;
long err;
……
/* Copy in the blobs from userspace, check they are vaguely sane. */
err = copy_and_check(&info, umod, len, uargs);//拷贝到内核
if (err)
return ERR_PTR(err);
/* Figure out module layout, and allocate all the memory. */
mod = layout_and_allocate(&info);//地址空间分配
if (IS_ERR(mod)) {
err = PTR_ERR(mod);
goto free_copy;
}
……
/* Fix up syms, so that st_value is a pointer to location. */
err = simplify_symbols(mod, &info);//符号解析
if (err < 0)
goto free_modinfo;
err = apply_relocations(mod, &info);//重定位
if (err < 0)
goto free_modinfo;
……
}
函数load_module内有四个关键的函数调用。copy_and_check将模块从用户空间拷贝到内核空间,layout_and_allocate为模块进行地址空间分配,simplify_symbols为模块进行符号解析,apply_relocations为模块进行重定位。
由此可见,模块加载时,内核为模块文件main.ko进行了链接的过程!
至于函数do_one_initcall的实现就比较简单了。
linux3.5.2/kernel/init.c:673
int __init_or_module do_one_initcall(initcall_t fn)
{
int count = preempt_count();
int ret;
if (initcall_debug)
ret = do_one_initcall_debug(fn);
else
ret = fn();//调用init module
……
return ret;
}
即调用了模块的入口函数init。
模块卸载
模块卸载由内核的系统调用delete_module完成。
linux3.5.2/kernel/module.c:768
SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
unsigned int, flags)
{
struct module *mod;
char name[MODULE_NAME_LEN];
int ret, forced = 0;
……
/* Final destruction now no one is using it. */
if (mod->exit != NULL)
mod->exit();//调用exit module
……
free_module(mod);//卸载模块
……
}
通过回调exit完成模块的出口函数功能,最后调用free_module将模块卸载。
结论
如此看来,内核模块其实并不神秘。传统的用户程序需要编译为可执行程序才能执行,而模块程序只需要编译为目标文件的形式便可以加载到内核,有内核实现模块的链接,将之转化为可执行代码。同时,在内核加载和卸载的过程中,会通过函数回调用户定义的模块入口函数和模块出口函数,实现相应的功能。
参考资料
http://hi.baidu.com/20065562/item/15dcc4ce92c3d510b67a24af
http://blog.chinaunix.net/uid-26009923-id-3840337.html
Linux模块机制浅析的更多相关文章
- Linux模块机制浅析_转
		Linux模块机制浅析 转自:http://www.cnblogs.com/fanzhidongyzby/p/3730131.htmlLinux允许用户通过插入模块,实现干预内核的目的.一直以来,对l ... 
- 【ARM-Linux开发】Linux模块机制浅析
		Linux模块机制浅析 Linux允许用户通过插入模块,实现干预内核的目的.一直以来,对linux的模块机制都不够清晰,因此本文对内核模块的加载机制进行简单地分析. 模块的Hello World! ... 
- 【Linux开发】Linux模块机制浅析
		Linux允许用户通过插入模块,实现干预内核的目的.一直以来,对linux的模块机制都不够清晰,因此本文对内核模块的加载机制进行简单地分析. 模块的Hello World! 我们通过创建一个简单的模块 ... 
- 【linux驱动笔记】linux模块机制浅析
		1. 模块module 操作系统分微内核和宏内核,微内核优点,可以使操作系统仅作很少的事,其它事情如网络处理等都作为应用程序来实现,微内核精简的同时,必然带来性能的下降.而linux的宏内核设 ... 
- webpack模块机制浅析【一】
		webpack模块机制浅析[一] 今天看了看webpack打包后的代码,所以就去分析了下代码的运行机制. 下面这段代码是webpack打包后的最基本的形式,可以说是[骨架] (function(roo ... 
- Linux内核启动流程与模块机制
		本文旨在简单的介绍一下Linux的启动流程与模块机制: Linux启动的C入口位于/Linux.2.6.22.6/init/main.c::start_kernel() 下图简要的描述了一下内核初始化 ... 
- Linux内核(6) - 模块机制与“Hello World!
		有一种感动,叫内牛满面,有一种机制,叫模块机制.显然,这种模块机制给那些Linux的发烧友们带来了方便,因为模块机制意味着人们可以把庞大的Linux内核划分为许许多多个小的模块.对于编写设备驱动程序的 ... 
- typecho流程原理和插件机制浅析(第一弹)
		typecho流程原理和插件机制浅析(第一弹) 兜兜 393 2014年03月28日 发布 推荐 5 推荐 收藏 24 收藏,3.5k 浏览 虽然新版本0.9在多次跳票后终于发布了,在漫长的等待里始终 ... 
- Linux 设备模型浅析之 uevent 篇(2)
		Linux 设备模型浅析之 uevent 篇 本文属本人原创,欢迎转载,转载请注明出处.由于个人的见识和能力有限,不可能面 面俱到,也可能存在谬误,敬请网友指出,本人的邮箱是 yzq.seen@gma ... 
随机推荐
- 解决报错 ora-00704 ora-00604 ora-00942 启动不了数据库问题
			早上海南的同事打电话说他们的审计库连不上了启动也报错,问了下最近做了些什么操作,答复是之前添加了一次磁盘. 猜测是添加磁盘启动后/dev/sdx顺序出错,或者没有正常的关闭数据库导致数据库无法正常启动 ... 
- Java多线程之CountDownLatch学习
			给出官网上的例子:http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html Java中conc ... 
- Java 之 常用类(二)
			1.StringBuffer a.StringBuffer 与 String:①StringBuffer是一个全新的类型,与String没有继承关系 ②StringBuffer的出现是为了解决Stri ... 
- Drools 查询学习
			Drools 查询学习查询以 query 关键字开始,以 end 关键字结束,在 package 当中一个查询要有唯一的名称,查询的内容就是查询的条件部分,条件部分内容的写法与规则的 LHS 部分写法 ... 
- 無間道III 終極無間
			凭良心说,它绝对算是诚意之作,而非急功近利或者说抢市.因为导演尤其是编剧都用了心,为了和第一二集融合而在细节处理上做足了文章,麦兆辉也实在够天才. 关于时间问题,本片不是完全杂乱无章,只不过是前后两段 ... 
- Rdeis 详解    小白
			Redis 与其他 key - value 缓存产品有以下三个特点: Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用. Redis不仅仅支持简单的key-v ... 
- 丢手帕问题即约瑟夫问题的PHP解法
			问题描述:n个人排成一圈.从某个人开始,依次报数,数到m的人被杀死.下一个人重新从1开始报数,数到m的人被杀死.直到剩下最后一个人. 解决思路:从数学角度去看,每一次报数决定谁去死是一个n.m的求余数 ... 
- React 学习,需要注意几点
			最近要开始学习 React 框架了,在学习的过程中,个人作为一个新手觉得需要注意的一些问题 小细节有以下几点: 1:在JSX语法当中,如果遇到 HTML 标签(以 < 开头),将用 HTML 规 ... 
- ajax请求action返回json数据
			1,服务端: HttpServletResponse response = ServletActionContext.getResponse(); response.setContentType(&q ... 
- C# uploadify 上传 -220 IO Error 问题
			1. 前端: uploadify 上文件大小限制配置. 2. 后端: web.config 配置 <?xml version="1.0"?> <configura ... 
