Linux模块化机制和module_init
致谢:
微信公众号:嵌入式企鹅圈 每天都新增爱好者关注,感谢大家的支持和大牛们的建议。
本人将竭力出品很多其它优质的原创文章回馈大家的厚爱。
引子:模块化机制长处
模块化机制(module)是Linux系统的一大创新。是Linux驱动开发和执行的基础(当然,module并不不过支撑驱动)。
其长处在于:
1.在系统执行动态载入模块。扩充内核的功能。
不须要时能够卸载。
2. 改动内核功能,不必又一次所有编译整改内核,仅仅须要编译对应模块就可以。
3.模块目标代码一旦被载入重定位到内核,其作用域和静态链接的代码全然等价。
本文重点阐述Linux module载入的来龙去脉,当中的奥秘就在于对宏module_init的解读。
一、模块样例
hello_module.c代码例如以下:
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_ALERT */
#include <linux/init.h> /*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 <asm-generic/vmlinux.lds.h>
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 < __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.
模块的传參、符号导出、模块依赖等机制以后再另文描写叙述
Linux模块化机制和module_init的更多相关文章
- Linux模块机制浅析
Linux模块机制浅析 Linux允许用户通过插入模块,实现干预内核的目的.一直以来,对linux的模块机制都不够清晰,因此本文对内核模块的加载机制进行简单地分析. 模块的Hello World! ...
- Linux模块机制浅析_转
Linux模块机制浅析 转自:http://www.cnblogs.com/fanzhidongyzby/p/3730131.htmlLinux允许用户通过插入模块,实现干预内核的目的.一直以来,对l ...
- 【ARM-Linux开发】Linux模块机制浅析
Linux模块机制浅析 Linux允许用户通过插入模块,实现干预内核的目的.一直以来,对linux的模块机制都不够清晰,因此本文对内核模块的加载机制进行简单地分析. 模块的Hello World! ...
- android & Linux uevent机制
Linux uevent机制 Uevent是内核通知android有状态变化的一种方法,比如USB线插入.拔出,电池电量变化等等.其本质是内核发送(可以通过socket)一个字符串,应用层(andro ...
- 利用linux信号机制调试段错误(Segment fault)
在实际开发过程中,大家可能会遇到段错误的问题,虽然是个老问题,但是其带来的隐患是极大的,只要出现一次,程序立即崩溃中止.如果程序运行在PC中,segment fault的调试相对比较方便,因为可以通过 ...
- Linux 内存机制详解宝典
Linux 内存机制详解宝典 在linux的内存分配机制中,优先使用物理内存,当物理内存还有空闲时(还够用),不会释放其占用内存,就算占用内存的程序已经被关闭了,该程序所占用的内存用来做缓存使用,对于 ...
- Linux Namespaces机制——实现
转自:http://www.cnblogs.com/lisperl/archive/2012/05/03/2480573.html 由于Linux内核提供了PID,IPC,NS等多个Namespace ...
- Linux Namespaces机制
转自:http://www.cnblogs.com/lisperl/archive/2012/05/03/2480316.html Linux Namespaces机制提供一种资源隔离方案.PID,I ...
- Linux分页机制之概述--Linux内存管理(六)
1 分页机制 在虚拟内存中,页表是个映射表的概念, 即从进程能理解的线性地址(linear address)映射到存储器上的物理地址(phisical address). 很显然,这个页表是需要常驻内 ...
随机推荐
- 1.1输出“hello,world”
#include<iostream> using namespace std; int main() { cout<<"Hello, World!"< ...
- [转]linux之patch命令
转自:http://blog.chinaunix.net/uid-9525959-id-2001542.html patch [选项] [原始文件 [补丁文件]] [功能] 给文件1应用补丁文件变成另 ...
- activiti 表——介绍
Activiti 用到的表都是act_开头 下面分别来介绍下 activiti 所用到的表: Act_RE_* 表示 RepositoryService接口所操作的表:包含了流程定义信息 .流程静态资 ...
- 将电脑浏览器User-Agent识别改成手机浏览器UA几种简单方法
第一种方法:修改浏览器的快捷方式 右击桌面上的Chrome浏览器图标,在弹出的右键菜单中选择“复制”,复制一个图标副本到桌面.右击该副本,选择“属性”,打开相应的对话框,在“目标”文本框的字符后面添加 ...
- x264
x264 x264 h264 1. x264调用主要过程 x264_param_default():设置参数集结构体x264_param_t的缺省值. x264_picture_alloc():为 ...
- SSH整合框架+mysql简单的实现
SSH整合框架+mysql简单的实现 1. 框架整合原理: struts2整合Spring 两种: 一种struts2自己创建Action,自动装配Service : 一种 将Action交给Spri ...
- 用最简单的脚本完成supertab的基本功能并实现一个更加合理的功能
supertab是vim的一个出名的插件, 相信会vim的人没几个不知道的, 我在之前的<<vim之补全1>>中首先说明的也是它, supertab实现的功能简单的说就是用ta ...
- python mongodb压力测试脚本
$ pip install pymongo #!/usr/bin/env python #coding=utf-8 #Author: Ca0Gu0 from pymongo import MongoC ...
- if({1,0}, , )
=VLOOKUP(F2,IF({1,0},D2:D10,C2:C10),2,),用F2作为查找条件,对D列进行查找,如果改成{0,1}则是对C列为查找范围.返回内存数组,对F2进行精确查找. 为什么写 ...
- 初学者怎么才能快速学会Python?
提起对Python的印象,除了全能之外恐怕就是简单易学了.很多人都在推荐新手学Python入门,毕竟语法简单.语句简洁,所谓“人生苦短我用Python”绝不是一句空话.不过也不能忽视一点:Python ...