|
分析kernel的initcall函数
| | | 分析 kernel 的 initcall 函数
Author: Dongas
Data: 08-07-15
先来看看这些 initcall 函数的声明:
/* 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.
*/
#define __define_initcall(level,fn) /
static initcall_t __initcall_##fn __attribute_used__ /
__attribute__((__section__(".initcall" level ".init"))) = fn
#define core_initcall(fn) __define_initcall("1",fn)
#define postcore_initcall(fn) __define_initcall("2",fn)
#define arch_initcall(fn) __define_initcall("3",fn)
#define subsys_initcall(fn) __define_initcall("4",fn)
#define fs_initcall(fn) __define_initcall("5",fn)
#define device_initcall(fn) __define_initcall("6",fn)
#define late_initcall(fn) __define_initcall("7",fn)
#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 /
__attribute_used__ __attribute__((__section__(".con_initcall.init")))=fn
#define security_initcall(fn) /
static initcall_t __initcall_##fn /
__attribute_used__ __attribute__((__section__(".security_initcall.init"))) = fn
#define module_init(x) __initcall(x);
ß
从这里知道 module_init 的等级为 6 ,相对靠后
#define module_exit(x) __exitcall(x);
可以发现这些 *_initcall(fn) 最终都是通过 __define_initcall(level,fn) 宏定义生成的。
__define_initcall 宏定义如下:
#define __define_initcall(level,fn) /
static initcall_t __initcall_##fn __attribute_used__ /
__attribute__((__section__(".initcall" level ".init"))) = fn
这句话的意思为定义一个 initcall_t 型的初始化函数,函数存放在 .initcall”level”.init section 内。 .initcall”level”.init section 定义在 vmlinux.lds 内。
/* arch/arm/kernel/vmlinux.lds */
……
__initcall_start = .;
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
__initcall_end = .;
……
正好包括了上面 init.h 里定义的从 core_initcall 到 late_initcall 等 7 个 level 等级的 .initcall”level”.init section. 因此通过不同的 *_initcall 声明的函数指针最终都会存放不同 level 等级的 .initcall”level”.init section 内。这些不同 level 的 section 按 level 等级高低依次存放。
下面我们再来看看,内核是什么时候调用存储在 .initcall”level”.init section 内的函数的。
内核是通过 do_initcalls 函数循环调用执行 initcall.init section 内的函数的,流程如下:
start_kernel -> rest_init -> kernel_thread -> init -> do_basic_setup -> do_initcalls
这里要分析两个函数 : kernel_thread 和 do_initcalls ,这两个函数都定义在 init/main.c 内
1) kernel_thread
1.static void noinline rest_init(void)
2. __releases(kernel_lock)
3.{
4. system_state = SYSTEM_BOOTING_SCHEDULER_OK;
5.
6. kernel_thread(init, NULL, CLONE_FS | CLONE_SIGHAND);
7. numa_default_policy();
8. unlock_kernel();
9.
10. /*
11. * The boot idle thread must execute schedule()
12. * at least one to get things moving:
13. */
14. __preempt_enable_no_resched();
15. schedule();
16. preempt_disable();
17.
18. /* Call into cpu_idle with preempt disabled */
19. cpu_idle();
20.}
第 6 行通过 kernel_thread 创建一个内核线程执行 init 函数。(其实这里创建的即
Linux 的1号进程 (init 进程 ), 为 linux 中所有其他进程的父进程,有兴趣的可以自己查资料)
2) do_initcalls
1.static void __init do_initcalls(void)
2.{
3. initcall_t *call;
4. int count = preempt_count();
5.
6. for (call = __initcall_start; call < __initcall_end; call++) {
7. ……
8. result = (*call)();
9. ……
10. }
11.}
其中 , initcall_t 类型如下:
typedef int (*initcall_t)(void);
__initcall_start 和 __initcall_end 定义在 vmlinux.lds 内,表示 initcall section 的起始和结束地址。
/* arch/arm/kernel/vmlinux.lds */
……
__initcall_start = .;
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
__initcall_end = .;
……
因此,上面 6-10 行代码的作用为按 initcall level 等级的顺序,依次循环调用预先存储在 initcall section 内的所有各个级别的初始化函数。这样, kernel 的 initcall 函数的原理我们就搞清楚了。
最后要注意的是 rest_init 是在 start_kernel 函数内最后部分才被调用执行的, rest_init 前包含了 kernel 一系列的初始化工作。另外,这些不同 level 等级的 initcall.init section 本身有一定的执行顺序,因此如果你的驱动依赖于特定的执行顺序的话需要考虑到这一点。
|
|
|
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理