linux内核中的xx_initcall和module_init实现机制(linux3.1.0)
来源:互联网 发布:雷蒙德钱德勒 知乎 编辑:程序博客网 时间:2024/06/01 09:58
在内核代码中,arch_initcall 、device_initcall、module_init等经常遇到,本文分析其实现机制。
1 include/linux/init.h 下的相关定义
#ifndef MODULE#define pure_initcall(fn)__define_initcall("0",fn,0)#define core_initcall(fn)__define_initcall("1",fn,1)#define core_initcall_sync(fn)__define_initcall("1s",fn,1s)#define postcore_initcall(fn)__define_initcall("2",fn,2)#define postcore_initcall_sync(fn)__define_initcall("2s",fn,2s)#define arch_initcall(fn)__define_initcall("3",fn,3)#define arch_initcall_sync(fn)__define_initcall("3s",fn,3s)#define subsys_initcall(fn)__define_initcall("4",fn,4)#define subsys_initcall_sync(fn)__define_initcall("4s",fn,4s)#define fs_initcall(fn)__define_initcall("5",fn,5)#define fs_initcall_sync(fn)__define_initcall("5s",fn,5s)#define rootfs_initcall(fn)__define_initcall("rootfs",fn,rootfs)#define device_initcall(fn)__define_initcall("6",fn,6)#define device_initcall_sync(fn)__define_initcall("6s",fn,6s)#define late_initcall(fn)__define_initcall("7",fn,7)#define late_initcall_sync(fn)__define_initcall("7s",fn,7s)还有一个我们常遇到的module_init()
#define module_init(x)__initcall(x);#define __initcall(fn) device_initcall(fn)所以module_init(x)和device_initcall(fn)是等价的
__define_initcall(level,fn,id)level从0到7表示优先级,也就是pure_initcall() 最先被调用,late_initcall()最后被调用。调用的先后顺序
2 分析 __define_initcall宏定义
我们知道,以上所有的定义最后都转化为了 __define_initcall(level,fn,id),那么就来分析这个宏。
#define __define_initcall(level,fn,id) \static initcall_t __initcall_##fn##id __used \__attribute__((__section__(".initcall" level ".init"))) = fntypedef int (*initcall_t)(void);
从一上定义可知:
1 定义一个initcall_t类型函数指针
2 函数指针初值为fn
3 函数指针段属性为(".initcall" level ".init")
以arch_initcall(customize_machine) 为例,可以转化为:
__define_initcall("3",customize_machine,3)static initcall_t __initcall_customize_machine3 __used \__attribute__((__section__(".initcall3.init"))) = customize_machine
1 定义一个函数指针 __initcall_customize_machine3
2 函数指针初始化为customize_machine
3 这个函数指针的属性是.initcall3.init
3 __section__(".initcall" level ".init")段属性定义
/include/asm-generic/vmlinux.lds.h中定义段属性
#define INITCALLS\*(.initcallearly.init)\VMLINUX_SYMBOL(__early_initcall_end) = .;\ *(.initcall0.init)\ *(.initcall0s.init)\ *(.initcall1.init)\ *(.initcall1s.init)\ *(.initcall2.init)\ *(.initcall2s.init)\ *(.initcall3.init)\ *(.initcall3s.init)\ *(.initcall4.init)\ *(.initcall4s.init)\ *(.initcall5.init)\ *(.initcall5s.init)\*(.initcallrootfs.init)\ *(.initcall6.init)\ *(.initcall6s.init)\ *(.initcall7.init)\ *(.initcall7s.init)#define INIT_CALLS\VMLINUX_SYMBOL(__initcall_start) = .;\INITCALLS\VMLINUX_SYMBOL(__initcall_end) = .;
4 调用
其实,在linux内核中,有一个技巧是经常用到的,就是把某些具有类似特性的指针、函数、结构体等
放链接的时候放到同一个段,然后调用的时候到这个段中去遍历,或者去寻找匹配项。这里就是如此。
static void __init do_initcalls(void){initcall_t *fn;for (fn = __early_initcall_end; fn < __initcall_end; fn++)do_one_initcall(*fn);}
__early_initcall_end 在__initcall_start之后,在*(.initcall0.init)之前。
do_initcalls(void)就是到这个段中去遍历fn,然后调用do_one_initcall(*fn),do_one_initcall(*fn)调用fn。
5 调用过程
现在已经知道如何被调用的了,来看看何时被调用。整个调用过程:
/init/main.c start_kernel ->rest_init()->kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND)
->kernel_init->do_basic_setup()->do_initcalls()->do_one_initcall(initcall_t fn)->fn()
以上所有函数均在/init/main.c中
看到这里应该已经明白了xx_initcall()的实现机制。但是有个前提,是在静态编译内核
的时候是这样的。如果你看的够仔细的话,应该注意到在第一部分include/linux/init.h 下的
相关定义第一行是#ifndef MODULE。
所谓静态编译,就是在make menuconfig时候,选择 *,也就是yes,模块直接编译进了
内核。
动态编译
当你希望这个模块可以用insmod动态加载的时候,可以选择 M ,也就是只编译成模块,
不编译进内核,此时就定义了MODULE。MODULE参数的传递是编译时通过gcc -DMODULE
传递的。在编译一个自己写的驱动模块的时候,如果不把它融合到内核的目录里,那我们
自己写的Makefile里通常会在make 的后面加上modules,也是达到这个目的。
当动态编译一个模块的时候,定义如下:
#define early_initcall(fn)module_init(fn)#define core_initcall(fn)module_init(fn)#define postcore_initcall(fn)module_init(fn)#define arch_initcall(fn)module_init(fn)#define subsys_initcall(fn)module_init(fn)#define fs_initcall(fn)module_init(fn)#define device_initcall(fn)module_init(fn)#define late_initcall(fn)module_init(fn)#define security_initcall(fn)module_init(fn)所有这些 device_initcall(fn)等全部等效于module_init(fn)。其实也容易理解,既然你可以不
编译进内核,说明并不是那么的重要,那就不管你说以往是什么头衔,都一视同仁。那就只分析
module_init(fn)就可以了。
#define module_init(initfn)\static inline initcall_t __inittest(void)\{ return initfn; }\int init_module(void) __attribute__((alias(#initfn)));首先我们可以发现发现module_init有两个含义:
1、验证加载函数的格式
static inline initcall_t __inittest(void)\{ return initfn; }这个函数的作用是验证我们穿过来的加载函数格式是否正确,linux内核规定加载函数的的原型是:
typedef int (*initcall_t)(void);
所以我们写加载函数的时候必须是返回值为int参数为void的函数,这个在内核里要求比较严格,所以我们写
加载函数的时候必须按照这个约定。
2、定义别名
int init_module(void) __attribute__((alias(#initfn)));这段代码的作用是给我们的加载函数定义一个别名,别名就是我们前面提到的init_module,这样insmod
就能够执行我们的加载函数了。
- linux内核中的xx_initcall和module_init实现机制(linux3.1.0)
- Linux内核中的xx_initcall
- MACHINE_START-内核板级初始化实现机制(linux3.1.0)
- MACHINE_START-内核板级初始化实现机制(linux3.1.0)
- LINUX内核中的xx_initcall初始化标号
- LINUX内核中的xx_initcall初始化标号
- LINUX内核中的xx_initcall初始化标号
- LINUX内核中的xx_initcall初始化标号
- Linux内核中的xx_initcall初始化标号
- LINUX内核中的xx_initcall初始化标号
- LINUX内核中的xx_initcall初始化标号
- LINUX内核中的xx_initcall初始化标号
- LINUX内核中的xx_initcall初始化标号
- linux内核中的xx_initcall初始化标号
- LINUX内核中的xx_initcall初始化标号
- LINUX内核中的xx_initcall初始化标号
- LINUX内核中的xx_initcall初始化标号
- LINUX内核中的xx_initcall初始化标号
- Retrieving the COM class factory for component with CLSID {C1F400A0-3F08-11D3-9F0B-006008039E37} fai
- 一个应用实例详解卡尔曼滤波及其算法实现
- 异常处理的基本过程
- 关于block 中 何时使用 __weak/__block MyViewController * weakSelf 分析
- 【程序运行时找不到库文件】nginx: error while loading shared libraries: libpcre.so.1
- linux内核中的xx_initcall和module_init实现机制(linux3.1.0)
- 系统学习hbase
- CUDA学习(3)——运行黑屏后恢复
- mint 下fictx输入法
- nmon 介绍和使用
- Android动画之translate(位移动画)
- Jasper Reports Development's Shoulds and Shouldn'ts
- 模拟器运行出错-- Unable to run app in simulator:An error was encountered while running
- Linux环境进程间通信(二): 信号(上)