• (转)分析kernel的initcall函数


     
    分析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
  • 相关阅读:
    Ubuntu 装JDK
    U盘文件夹被病毒隐藏,且不能取消解决办法
    wireshark: there are no interfaces on which a capture can be done
    [转]Ubuntu 常用快捷键10个
    恢复被win7覆盖的Ubuntu Grub
    U盘安装Win7 64位
    荣耀3X畅玩版狙击红米note!
    Secret and Whisper
    360 chrome不能登录Google账户
    周鸿祎仍想做手机
  • 原文地址:https://www.cnblogs.com/lihaiping/p/initcall.html
Copyright © 2020-2023  润新知