在解释initcall调用顺序, 先要理一下编译链接的知识.

  每个.o文件都有自己的代码段, 数据段(存放初始化的全局变量), bss段(即未初始化的数据段) 在ld链接器将各.o文件的代码段和数据段组织在一起. 一般把各段都放在一起, 如代码段都放在一起:

  .text :  { *(.text)};
  这样各.o文件的代码段都会放在一起.放的顺序是按ld后面接着的文件顺序.如ld a.o b.o

作为链接的结果要有一个入口,由entry标示.entry标示的入口一般在代码段里, 如
ENTRY(_start)
在代码段里
.text : {
_start = .;
}

对于链接器,输入文件是各.o文件,输出文件是可执行文件,输入文件按段(section)来组织.section有几个重要组成部分,一是. 这个把当前地址重新定位,即可执行文件在内存中运行的位置,一般vmlinux.lds.S把.定义为0x300000000+0x800,即3G偏下的0x800位置,对于1G物理内存是这样的,在6795平台,因为是2G内存,初设置为其他值. 

链接脚本也是一种语言,需要稍微了解一下.整个脚本的最终目的是对最终可执行文件在内存各代码段等的合理布局.也有一些说明,如对这个文件指明是在arm体系上运行, 就用OUTPUT_ARCH(arm).

/DISCARD/ 表示符合条件的输入段,都不会输出到输出的可执行文件中

不想当架构师的不是好程序员
关于链接脚本的详细语法,参见: http://www.cnblogs.com/china_blue/archive/2010/04/07/1705976.html
----------
下面来说说initcall调试顺序. 
链接脚本文件也像c语言一样,可以包含.h文件.
在6795中,vmlinux.lds.s inicall部分通过引用.h文件中的INIT_CALLS来完成init.data段的定义. 如下
kernel-3.0/arch/arm/kernel/vmlinux.lds.s
.init.data : {
#ifndef CONFIG_XIP_KERNEL
INIT_DATA
#endif
INIT_SETUP(16)
INIT_CALLS
CON_INITCALL
SECURITY_INITCALL
INIT_RAM_FS
}
INIT_CALLS在kernel-3.0/include/asm-generic/vmlinux.lds.h中定义:
#define INIT_CALLS \
VMLINUX_SYMBOL(__initcall_start) = .; \
*(.initcallearly.init) \
INIT_CALLS_LEVEL(0) \
INIT_CALLS_LEVEL(1) \
INIT_CALLS_LEVEL(2) \
INIT_CALLS_LEVEL(3) \
INIT_CALLS_LEVEL(4) \
INIT_CALLS_LEVEL(5) \
INIT_CALLS_LEVEL(rootfs) \
INIT_CALLS_LEVEL(6) \
INIT_CALLS_LEVEL(7) \
VMLINUX_SYMBOL(__initcall_end) = .;

#define INIT_CALLS_LEVEL(level) \
VMLINUX_SYMBOL(__initcall##level##_start) = .; \
*(.initcall##level##.init) \
*(.initcall##level##s.init) \

#define VMLINUX_SYMBOL(x) x

##一般只用在宏定义中,将两个字串与替换者连在一起,组成一个新的字符串. 
INIT_CALLS最终被替换为: 

__initcall_start = .;
*(.initcallearly.init)
__initcall0_start = .;
*(.initcall0.init)
*(.initcall0s.init)

__initcall1_start = .;
*(.initcall1.init)
*(.initcall1s.init)

__initcall2_start = .;
*(.initcall2.init)
*(.initcall2s.init)

__initcall3_start = .;
*(.initcall3.init)
*(.initcall3s.init)
......

__initcallx_start 地址会被依次定义,这个地址在do_initcalls()时依次遍历.编号越小,越在内存布局中靠前的位置,越被先遍历到.
内核的各.o文件init函数会被定义为initcallx.init这样的函数. 这个编号决定了这个init被调用的时间.可以按时间先后,依次列为第0时间,第1时间,第2时间...
内核中各init函数如何被定义为initcallx.init函数.以常见的module_init为例.

#define module_init(x) __initcall(x);

#define __initcall(fn) device_initcall(fn)

#define device_initcall(fn) __define_initcall(fn, 6)

#define __define_initcall(fn, id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" #id ".init"))) = fn

以ltr559.c为例,
这样module_init(ltr559_init)最终定义为:
static initcall_t __initcall_ltr559_init6 __used
__attribute__((__section__(".initcall6.init"))) = ltr559_init

__attribute__((__section__(".initcall6.init") 告诉编译器把ltr559_init放在名为.initcall6.init代码段中. 
即所有传入module_init所有初始化函数都会放在.initcall6.init代码段中. 
这个代码段根据vmlinux.lds.S指定在最终可执行文件的内存布局的第6区域.__initcall6_start是这个区域的起始位置.这个区域的函数由以下函数依次遍历执行:
static void __init do_pre_smp_initcalls(void)
{
initcall_t *fn;

for (fn = __initcall_start; fn < __initcall0_start; fn++)
do_one_initcall(*fn);
}
所有在.initcall6.init区域的函数在第6时间执行. 

core_init被放在第1时间, arch_initcall被放在第3时间,  fs_initcall被放在第5时间. 这些在kernel-3.0/include/linux/init.h中定义, 如下:

#define pure_initcall(fn) __define_initcall(fn, 0)

#define core_initcall(fn) __define_initcall(fn, 1)
#define core_initcall_sync(fn) __define_initcall(fn, 1s)
#define postcore_initcall(fn) __define_initcall(fn, 2)
#define postcore_initcall_sync(fn) __define_initcall(fn, 2s)
#define arch_initcall(fn) __define_initcall(fn, 3)
#define arch_initcall_sync(fn) __define_initcall(fn, 3s)
#define subsys_initcall(fn) __define_initcall(fn, 4)
#define subsys_initcall_sync(fn) __define_initcall(fn, 4s)
#define fs_initcall(fn) __define_initcall(fn, 5)
#define fs_initcall_sync(fn) __define_initcall(fn, 5s)
#define rootfs_initcall(fn) __define_initcall(fn, rootfs)
#define device_initcall(fn) __define_initcall(fn, 6)
#define device_initcall_sync(fn) __define_initcall(fn, 6s)
#define late_initcall(fn) __define_initcall(fn, 7)
#define late_initcall_sync(fn) __define_initcall(fn, 7s)

module_initcall即device_initcall在第6时间, 比较靠后的了.

initcall调用顺序的更多相关文章

  1. AsyncTask内的各个方法调用顺序

    |- AsyncTask内的各个方法调用顺序:|- 首先,用户调用execute方法,启动AsyncTask .然后在execute方法中:|- 首先调用onPreExecute方法,执行初始化操作. ...

  2. C++继承,多重继承,虚继承的构造函数以及析构函数的调用顺序问题

    #include <iostream> using namespace std; class A{ int data_a; public: A(){ data_a = ; cout < ...

  3. UITableView 接口的调用顺序

    ios7启用estimatedHeightForRowAtIndexPath之后的api调用顺序called -[XHYTableViewController tableView:heightForR ...

  4. C++C++中构造函数与析构函数的调用顺序

    http://blog.csdn.net/xw13106209/article/details/6899370 1.参考文献 参考1: C++继承中构造函数.析构函数调用顺序及虚函数的动态绑定 参考2 ...

  5. C++类构造析构调用顺序训练(复习专用)

    //对象做函数参数 //1 研究拷贝构造 //2 研究构造函数,析构函数的调用顺序 //总结 构造和析构的调用顺序 #include "iostream" using namesp ...

  6. Unity3D中关于场景销毁时事件调用顺序的一点记录

    先说一下我遇到的问题,我弄了一个对象池管理多个对象,对象池绑定在一个GameObject上,每个对象在OnBecameInvisible时会进行回收(即移出屏幕就回收),但是当场景切换或停止运行程序时 ...

  7. UIViewController中各方法调用顺序及功能详解

    UIViewController中各方法调用顺序及功能详解 UIViewController中loadView, viewDidLoad, viewWillUnload, viewDidUnload, ...

  8. cocos2d-x 2.x版本中,场景切换各方法调用顺序

    假设从A场景切换到B场景,调用各场景方法的顺序为: 如果没有切换效果(transition),则先调用B的init(),再调用A的onExitTransitionStart(),接着调用A的onExi ...

  9. Java类加载及实例化的调用顺序

    标题起得略拗口,大概意思就是说在一个Java类中,域和构造方法的调用顺序. 1. 没有继承的情况 单独一个类的场景下,初始化顺序为依次为 静态数据,继承的基类的构造函数,成员变量,被调用的构造函数. ...

随机推荐

  1. Linux 服务器IO模型 epoll

    epoll模型 #include <unistd.h> #include <sys/types.h> /* basic system data types */ #includ ...

  2. JAVA OO 第二章知识点

    一.JAVA的基础语法 1.关键字 ①关键字:关键字用于定义该门语言,且这些单词对编译器用特殊的含义,而且不能作为标识符. 2.标识符 标识符:在JAVA中我们备选的单词,包括:类名.方法名.字段.变 ...

  3. 创建用资源管理器打开FTP位置

    FTP快捷方式默认用浏览器打开,而不是用资源管理器打开,管理文件不习惯. 解决方法1:创建桌面快捷方式 新建快捷方式,键入对象位置 %windir%\explorer.exe "ftp:// ...

  4. perl基础:传递hash类型参数

    1 如果是只有一个参数要传,且是hash,最直接想到的办法就是像传其他类型参数一样直接传, 如:   subFuntion(%hash1); 2 如果有多于一个参数要传,这里假设只有一个参数的类型是h ...

  5. Luogu 魔法学院杯-第二弹(萌新的第一法blog)

    虽然有点久远  还是放一下吧. 传送门:https://www.luogu.org/contest/show?tid=754 第一题  沉迷游戏,伤感情 #include <queue> ...

  6. 20145236 GDB调试汇编堆栈过程分析

    GDB调试汇编堆栈过程分析 首先需要输入sudo apt-get install libc6-dev-i386安装一个库才能产生汇编代码,然后输入gcc - g example.c -o exampl ...

  7. Android genymotion安装输入法不兼容

    genymotion 此应用与您的手机不兼容 http://blog.csdn.net/caiwenfeng_for_23/article/details/41692923

  8. Dependency Scope

    Dependency Scope <dependency>中还引入了<scope>,它主要管理依赖的部署.目前<scope>可以使用5个值: * compile,缺 ...

  9. Tomcat批处理文件小结

    Tomcat批处理文件小结 一:嗯,如果你不了解Windows批处理文件,并且想了解一下,请先参看下面的链接资源(我也是因为想了解一下Windows批处理文件是用什么写的?怎么写的?才在园中找的,下面 ...

  10. Java泛型学习笔记 - (七)浅析泛型中通配符的使用

    一.基本概念:在学习Java泛型的过程中, 通配符是较难理解的一部分. 主要有以下三类:1. 无边界的通配符(Unbounded Wildcards), 就是<?>, 比如List< ...