分析kernel的initcall函数 | |
|
|
来源: ChinaUnix博客 日期: 2008.07.19 21:24 (共有条评论) 我要评论 | |
分析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 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本身有一定的执行顺序,因此如果你的驱动依赖于特定的执行顺序的话需要考虑到这一点。 本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/60011/showart_1086523.html |