您的位置:首页 > 运维架构 > Linux

LINUX内核中的xx_initcall初始化标号

2012-09-25 10:10 351 查看
LINUX内核中有很多的初始化指示标志postcore_initcall(), arch_initcall(), subsys_initcall(), device_initcall(), etc. 这些起什么作用呢?查阅源代码(android goldfish-2.6.29)并搜索网上相关文章,对此做一总结。

初始化标号

先看这些宏的定义(定义在文件include/linux/init.h中)

[cpp]
view plaincopyprint?

#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) 所以,驱动对应的加载的优先级为6。

在上面的不同的优先级中,数字越小,优先级越高。同一等级的优先级的驱动,加载顺序是链接过程决定的,结果是不确定的,我们无法去手动设置谁先谁后。不同等级的驱动加载的顺序是先优先级高,后优先级低,这是可以确定的。所以,像我们之前在驱动中用:

module_init(i2c_dev_init);

module_init(as352x_afe_init);

module_init(as352x_afe_i2c_init);

所以,大家都是同一个优先级去初始化,最后这些驱动加载的顺序,可以查看在根目录下,生成的system.map:

。。。。。

c00197f4 t __initcall_default_rootfsrootfs

c00197f8 t __initcall_timer_init_sysfs6

c00197fc t __initcall_clock_dev_init6

__define_initcall

这些宏都用到了__define_initcall(),再看看它的定义(同样定义在文件include/linux/init.h中)

[cpp]
view plaincopyprint?

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

这其中initcall_t是函数指针,原型如下,

[cpp]
view plaincopyprint?

typedef int (*initcall_t)(void);

typedef int (*initcall_t)(void);


而属性 __attribute__((__section__())) 则表示把对象放在一个这个由括号中的名称所指代的section中。

所以__define_initcall的含义是:

1) 声明一个名称为__initcall_##fn的函数指针;

2) 将这个函数指针初始化为fn;

3) 编译的时候需要把这个函数指针变量放置到名称为 ".initcall" level ".init"的section中。

3. 放置.initcall.init SECTION

明确了__define_initcall的含义,就知道了是分别将这些初始化标号修饰的函数指针放到各自的section中的。

SECTION“.initcall”level”.init”被放入INITCALLS(include/asm-generic/vmlinux.lds.h)

[cpp]
view plaincopyprint?

#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)

__initcall_start和__initcall_end以及INITCALLS中定义的SECTION都是在arch/xxx/kernel/vmlinux.lds.S中放在.init段的。

[plain]
view plaincopyprint?

SECTIONS
{
.init : {
__initcall_start = .;
INITCALLS
__initcall_end = .;
}
}

4. 初始化.initcallxx.init里的函数

而这些SECTION里的函数在初始化时被顺序执行(init内核线程->do_basic_setup()[main.c#778]->do_initcalls())。

程序(init/main.c文件do_initcalls()函数)如下,do_initcalls()把.initcallXX.init中的函数按顺序都执行一遍。

[cpp]
view plaincopyprint?

for (call = __early_initcall_end; call < __initcall_end; call++)
do_one_initcall(*call);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: