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

LINUX内核中的xx_initcall初始化标号

2013-11-28 18:01 281 查看
From: csdn 800th http://blog.csdn.net/thl789/article/details/6581146
LINUX内核中有很多的初始化指示标志postcore_initcall(), arch_initcall(), subsys_initcall(), device_initcall(), etc. 这些起什么作用呢?查阅源代码(android4.1-kernel3.4)并搜索网上相关文章,对此做一总结。

初始化标号

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

/* initcalls are now grouped by functionality into separate
* subsections. Ordering inside the subsections is determined
* by link order.
* For backwards compatibility, initcall() puts the call in
* the device init subsection.
*
* The `id' arg to __define_initcall() is needed so that multiple initcalls
* can point at the same handler without causing duplicate-symbol build errors.
*/

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

/*
* Early initcalls run before initializing SMP.
*
* Only for built-in code, not modules.
*/
#define early_initcall(fn)        __define_initcall("early",fn,early)

/*
* A "pure" initcall has no dependencies on anything else, and purely
* initializes variables that couldn't be statically initialized.
*
* This only exists for built-in code, not for modules.
*/
#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)

#define __initcall(fn) device_initcall(fn)

#define __exitcall(fn) \
static exitcall_t __exitcall_##fn __exit_call = fn

#define console_initcall(fn) \
static initcall_t __initcall_##fn \
__used __section(.con_initcall.init) = fn

#define security_initcall(fn) \
static initcall_t __initcall_##fn \
__used __section(.security_initcall.init) = fn

struct obs_kernel_param {
const char *str;
int (*setup_func)(char *);
int early;
};


__define_initcall

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

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


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

typedef int (*initcall_t)(void);


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

所以__define_initcall的含义是:

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

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

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

3. 放置.initcall.init SECTION

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

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

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

#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) = .;


INIT_CALLS中定义的SECTION都是在arch/xxx/kernel/vmlinux.lds.S中放在.init段的。

.init.data : {
......
INIT_CALLS
......
}


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

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

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

extern initcall_t __initcall_start[];
extern initcall_t __initcall0_start[];
extern initcall_t __initcall1_start[];
extern initcall_t __initcall2_start[];
extern initcall_t __initcall3_start[];
extern initcall_t __initcall4_start[];
extern initcall_t __initcall5_start[];
extern initcall_t __initcall6_start[];
extern initcall_t __initcall7_start[];
extern initcall_t __initcall_end[];

static initcall_t *initcall_levels[] __initdata = {
__initcall0_start,
__initcall1_start,
__initcall2_start,
__initcall3_start,
__initcall4_start,
__initcall5_start,
__initcall6_start,
__initcall7_start,
__initcall_end,
};

static void __init do_initcalls(void)
{
int level;

for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
do_initcall_level(level);
}

static void __init do_initcall_level(int level)
{
extern const struct kernel_param __start___param[], __stop___param[];
initcall_t *fn;

strcpy(static_command_line, saved_command_line);
parse_args(initcall_level_names[level],
static_command_line, __start___param,
__stop___param - __start___param,
level, level,
repair_env_string);

for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
do_one_initcall(*fn);
}


*******Done*******
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: