linux模块化机制,Linux模块化机制和module_init
> 引子:模块化机制优点
模块化机制(module)是Linux系统的一大创新,是Linux驱动开发和运行的基础(当然,module并不仅仅是支撑驱动)。其优点在于:
1.在系统运行动态加载模块,扩充内核的功能。不需要时可以卸载。
2\. 修改内核功能,不必重新全部编译整改内核,只需要编译相应模块即可。
3.模块目标代码一旦被加载重定位到内核,其作用域和静态链接的代码完全等价。
本文重点阐述Linux module加载的来龙去脉,其中的奥秘就在于对宏module_init的解读。
> 一、模块例子
hello_module.c代码如下:
~~~
#include /* Needed by all modules */
#include /* Needed for KERN_ALERT */
#include /*Needed for __init */
static int __init test_init(void){
printk(KERN_ALERT"Hello world!\n");
return 0;
}
static void __exit test_exit(void){
printk(KERN_ALERT"Goodbye world!\n");
}
module_init(test_init);
module_exit(test_exit);
~~~
> 二、模块编程要点
1.头文件 linux/module.h、linux/kernel.h、linux/init.h
2. 定义模块的初始化函数test_init(名字任意)和卸载函数test_exit(名字任意)。
3. 用宏module_init声明初始化函数,用宏module_exit声明卸载函数。
> 三、模块运行
模块代码有两种运行的方式:
1. 编译成可动态加载的module,并通过insmod来动态加载,接着进行初始化。
2. 静态编译链接进内核,在系统启动过程中进行初始化。
有些模块是必须要编译到内核,和内核一起运行的,从不卸载,如vfs、platform_bus等等。
> 四、静态链接和初始化
Make menuconfig时选择将模块编译到内核即为静态链接,或者直接在makefile文件中指定为obj-y +=hello_module.o
1module宏展开
头文件路径:include/linux/init.h
//静态编译链接时没有定义宏MODULE
~~~
#ifndef MODULE
typedef int (*initcall_t)(void);
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
#define device_initcall(fn) __define_initcall("6",fn,6)
#define __initcall(fn) device_initcall(fn)
#define module_init(x) __initcall(x);
~~~
所以:
~~~
module_init(test_init)展开为:
__initcall(test _init)->
device_initcall(test _init)->
__define_initcall("6", test _init,6)->
static initcall_t __initcall_test_init_6 __attribute__((__section__(".initcall6.init"))) = test_init;
~~~
即是定义了一个类型为initcall_t的函数指针变量__initcall_test_init_6,并赋值为test_init,该变量在链接时会链接到section(.initcall6.init).
2linux链接脚本
路径 arch/arm/kernel/vmlinux.ld.S
~~~
#include
SECTIONS{
…
INIT_CALLS
…
}
~~~
路径:include/ asm-generic/vmlinux.lds.h
~~~
#define INIT_CALLS \
VMLINUX_SYMBOL(__initcall_start) = .; \
INITCALLS \
VMLINUX_SYMBOL(__initcall_end) = .;
#define INITCALLS \
….
*(.initcall6.init) \
…
~~~
可见__initcall_test_init_6将会链接到section(.initcall6.init).
3初始化
在linux启动的第三个阶段kernel_init的函数里会调用:
路径init/main.c
~~~
Kernel_init
do_basic_setup
do_initcalls
static void __init do_initcalls(void){
initcall_t *fn;
for (fn = __early_initcall_end; fn
do_one_initcall(*fn);
}
~~~
即取出函数指针__initcall_test_init_6的值并进行调用,即执行test_init。
> 五、动态链接加载和初始化
Make menuconfig时选择将模块编译成模块即为动态链接,或者直接在makefile文件中指定为obj-m +=hello_module.o
编译成模块的命令是:
make –C $KERNEL_PATH M=$MODULE_PATH modules
即使用linux根目录下的makefile,执行该makefile下的modules伪目标,对当前模块进行编译。编译的结果是可重定位文件,insmod加载时才完成最终的链接动作。
1Module编译选项
Linux根目录下的makefile定义了modules伪目标会用到的编译选项。
//即定义宏MODULE,-D是GCC定义宏的语法。
MODFLAGS = -DMODULE
//GCC编译模块代码时会用到该选项,即定义宏MODULE。这与在头文件中用#define MODULE是一样的。
CFLAGS_MODULE = $(MODFLAGS)
2Module_init宏展开
头文件路径:include/linux/init.h
~~~
#ifndef MODULE /*编译成module时定义了宏MODULE*/
#else /* MODULE obj-m*/
typedef int (*initcall_t)(void);
#define module_init(initfn) \
static inline initcall_t __inittest(void) \
{ return initfn; } \
int init_module(void) __attribute__((alias(#initfn)));
~~~
__inittest仅仅是为了检测定义的函数是否符合initcall_t类型,如果不是__inittest类型在编译时将会报错。所以真正的宏定义是:
~~~
#define module_init(initfn)
int init_module(void) __attribute__((alias(#initfn)));
~~~
alias属性是GCC的特有属性,将定义init_module为函数initfn的别名。所以module_init(test_init)的作用就是定义一个变量名init_module,其地址和test_init是一样的。
3Hello_module.mod.c
编译成module的模块都会自动产生一个*.mod.c的文件,Hello_module.mod.c的内容如下:
~~~
struct module __this_module
__attribute__((section(".gnu.linkonce.this_module"))) = {
.name = KBUILD_MODNAME,
.init = init_module,
#ifdef CONFIG_MODULE_UNLOAD
.exit = cleanup_module,
#endif
.arch = MODULE_ARCH_INIT,
};
~~~
即定义了一个类型为module的全局变量__this_module,其成员init即为init_module,也即是test_init.并且该变量会链接到section(".gnu.linkonce.this_module").
4动态加载
insmod是busybox提供的用户层命令:
路径busybox/modutils/ insmod.c
~~~
insmod_main
bb_init_module
init_module
路径busybox/modutils/modutils.c:
# define init_module(mod, len, opts) .\
syscall(__NR_init_module, mod, len, opts)
~~~
该系统调用对应内核层的sys_init_module函数。
路径:kernel/module.c
SYSCALL_DEFINE3(init_module,…)
//加载模块的ko文件,并解释各个section,重定位
mod = load_module(umod, len, uargs);
//查找section(".gnu.linkonce.this_module")
modindex = find_sec(hdr, sechdrs, secstrings,
".gnu.linkonce.this_module");
//找到Hello_module.mod.c定义的module数据结构
mod = (void *)sechdrs[modindex].sh_addr;
if (mod->init != NULL)
ret = do_one_initcall(mod->init); //调用test_init.
模块的传参、符号导出、模块依赖等机制以后再另文描述
更多原创技术分享敬请关注微信公众号:嵌入式企鹅圈
![](https://box.kancloud.cn/2016-01-13_5695f8f8d24b2.jpg)
linux模块化机制,Linux模块化机制和module_init相关推荐
- 【Linux 内核】宏内核与微内核架构 ( 操作系统需要满足的要素 | 宏内核 | 微内核 | Linux 内核动态加载机制 )
文章目录 一.操作系统需要满足的要素 二.宏内核 三.微内核 四.Linux 内核动态加载机制 一.操作系统需要满足的要素 电脑上运行的 操作系统 , 是一个 软件 ; 设备管理 : 操作系统需要 为 ...
- 解析 Linux 中的 VFS 文件系统机制
简介: 本文阐述 Linux 中的文件系统部分,源代码来自基于 IA32 的 2.4.20 内核.总体上说 Linux 下的文件系统主要可分为三大块:一是上层的文件系统的系统调用,二是虚拟文件系统 V ...
- Linux系列之soft lockup机制 浅析
Linux系列之soft lockup机制 浅析 1.背景 2.什么是lockup? 2.1 lockup检测机制 2.2 softlockup的工作原理 3.soft lockup机制分析 3.1 ...
- 解析Linux中的VFS文件系统机制
文阐述 Linux 中的文件系统部分,源代码来自基于 IA32 的 2.4.20 内核.总体上说Linux下的文件系统主要可分为三大块:一是上层的文件系统的系统调用,二是虚拟文件系统 VFS(Virt ...
- 解析 Linux 中的 VFS 文件系统机制(1)
住:这里只转载一篇,还有几篇没有转载,但这里给出源地址: http://www.51cto.com/art/200803/67283.htm 摘要:本文阐述 Linux 中的文件系统部分,源代码来自基 ...
- (二)Linux设备驱动的模块化编程
本系列导航 (一)初识Linux驱动 (二)Linux设备驱动的模块化编程 (三)写一个完整的Linux驱动程序访问硬件并写应用程序进行测试 (四)Linux设备驱动之多个同类设备共用一套驱动 (五) ...
- linux中vfs和fs区别,解析Linux中的 VFS 文件系统机制(rootfs)一
本文阐述 Linux 中的文件系统部分,源代码来自基于 IA32 的 2.4.20 内核.总体上说 Linux 下的文件系统主要可分为三大块:一是上层的文件系统的系统调用,二是虚拟文件系统 VFS(V ...
- 20155301 滕树晨linux基础——linux进程间通信(IPC)机制总结
20155301 滕树晨linux基础--linux进程间通信(IPC)机制总结 共享内存 共享内存是在多个进程之间共享内存区域的一种进程间的通信方式,由IPC为进程创建的一个特殊地址范围,它将出现在 ...
- linux kernel and user space通信机制,Linux内核与用户空间通信机制研究.pdf
ISSN 1009-3044 E-mail:info@CCCC.net.CR ComputerKnowledgeandTechnology电脑知识与技术 http://www.dnzs.net.cn ...
最新文章
- 模型神器组合,yyds!
- win8 查看 linux硬盘大小,如何查看显存大小_win8如何查看显存大小
- python opencv cv.waitKey(1) 0xFF 的作用
- c++ overide
- springMVC:前端请求后台并传值
- 斐讯k3cfe刷lede_斐讯 K3 A1 刷机经历
- 西门子博途v14 SP1 S7-1200之间的以太网双边通讯(两个S7-1200 在一个项目中)
- Aurora AD308e 一体机驱动
- m126a linux驱动下载,惠普m126a驱动_惠普M126a打印机驱动安装 v15.0.15 稳定版-126g驱动网...
- 组合数学1--组合数学简介
- 实践“鱼塘养鱼论”,不断养鱼和捕鱼
- 英语计算机的读音,井号'#'用英语怎么说(计算机字符 - 英文读音)
- 文科生 python 简书_文科生学 Python 系列 15:泰坦尼克数据 1
- 两个总体方差的F检验
- uploadify上传图片
- 大小鲨鱼走红 纳米防水技能大有作为
- java 访客模式,每日一课 | Java 8中的instanceof运算符和访客模式替换
- c语言 自动dump 函数,(转)写个dump_stack
- 教师职称评定论文发表【新规定】老师必看
- 想要成为架构师?夯实基础最重要
热门文章
- 程序员的“鱿鱼游戏”,你能活到第几关?
- 皮一皮:据说这是英超第一轮之后的情况...
- 皮一皮:编!继续编啊你...
- 面试:GET和POST两种基本请求方法有什么区别
- ES 在数据量很大的情况下(数十亿级别)如何提高查询效率?
- mysql加入新的从节点怎么配置_MySql主从复制配置
- linux php 升级5.3,Linux php5.2.10升级到PHP5.3.29
- set OpenCV_ROOT
- mxnet优化器 SGD_GC
- pytorch MaxUnpool2d