do_initcall()函数的说明

1.
在看linux核心代码的时候看到/init/main.c 里面的do_initcalls函数
static void
__initdo_initcalls(void)
742 {
743        initcall_t *call;
744
745         for (call = __initcall_start; call <__initcall_end; call++)
746                do_one_initcall(*call);
747
748        /* Make sure there is no pending stuff from the initcall sequence */
749        flush_scheduled_work();
750 }
static void
__initdo_one_initcall(initcall_t fn)
697 {
698         intcount =preempt_count();
699        ktime_tt0,t1,delta;
700         charmsgbuf[64];
701         intresult;
702
703         if (initcall_debug) {
704                print_fn_descriptor_symbol("calling  %s/n", fn);
705                t0 =ktime_get();
706         }
707
708        result = fn();
709
710         if (initcall_debug) {
711                t1 =ktime_get();
712                delta =ktime_sub(t1,t0);
713
714                print_fn_descriptor_symbol("initcall %s", fn);
715                printk(" returned %d after %Ld msecs/n",result,
716                         (unsigned long long) delta.tv64 >> 20);
717         }
718
719        msgbuf[0] = 0;
720
721         if (result && result != -ENODEV && initcall_debug)
722                sprintf(msgbuf,"error code %d ", result);
723
724         if (preempt_count() != count) {
725                strlcat(msgbuf,"preemption imbalance ", sizeof(msgbuf));
726                preempt_count() =count;
727         }
728         if (irqs_disabled()) {
729                strlcat(msgbuf,"disabled interrupts ", sizeof(msgbuf));
730                local_irq_enable();
731         }
732         if (msgbuf[0]) {
733                print_fn_descriptor_symbol(KERN_WARNING"initcall %s", fn);
734                printk(" returned with %s/n",msgbuf);
735         }
736 }

__initcall_start是在 arch目录中的相关CPU中的vmlinux.lds文件指定,如i386中位于arch/i386/vmlinux.lds中,至于在 __initcall_start和__initcall_end之间的是由函数声明__init指定
2.
察看/arch/i386/vmlinux.lds,发现一段代码
 __initcall_start = .;
 .initcall.init : { *(.initcall.init) }
 __initcall_end = .;
 
跟我找的东西相关
使用info ld,察看相关资料,(最终发现太麻烦,到网上找了一个ld.pdf).发现有这么一
段介绍关于c++地联结构造器的使用,和这段用法相同
其含义时,是让__initcall_start指向代码节.initcall.init的节首,而__initcall_end
指向.initcall.init的节尾。
那么第一段代码从程序逻辑上得到了解释。

3。因为do_initcalls所作的是系统中有关于选择的驱动部分的初始化工作,那么具体是这些
函数指针数据怎样放到了.initcall.init节。
想起来了,还没有使用grep哈哈,在
grep -rn .initcall.init *
发现在include/linux/init.h:83:有这样一个定义
#define __init_call   __attribute__ ((unused,__section__ (".initcall.init")))
娃哈哈哈
终于让我发现了
然后又发现了
#define __initcall(fn) /
static initcall_t __initcall_##fn __init_call = fn

4。问题是什么是__attribute__??,查找man gcc,找到关于__attribute__的定义
`section ("section-name")'
   Normally, the compiler places the code it generates in the `text'
   section. Sometimes, however, you need additional sections, or you
   need certain particular functions to appear in special sections.
   The `section' attribute specifies that a function lives in a
   particular section. For example, the declaration:

     extern void foobar (void) __attribute__ ((section ("bar")));

   puts the function `foobar' in the `bar' section.

   Some file formats do not support arbitrary sections so the
   `section' attribute is not available on all platforms. If you
   need to map the entire contents of a module to a particular
   section, consider using the facilities of the linker instead.
  
他的意思就是使它建造一个在.initcall.init节的指向初始函数的指针

5。问题是##是什么意思?
查阅gcc的man page得知,她是用在可变参数使用宏定义的时候的
在这里也就是建立一个变量名称为所指向的函数的名称,并且前面加上
__initcall_.

6.然后看看成果
在/include/linux/init.c中有发现
#define module_init(x) __initcall(x);
看看很多驱动中都有类似
module_init(usb_init);
module_exit(usb_exit);
的代码,哈哈,这下明白了。


分析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);    &szlig;从这里知道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本身有一定的执行顺序,因此如果你的驱动依赖于特定的执行顺序的话需要考虑到这一点。


initcall机制原理及实践

说明:以下内容基于linux2.4.0

一、initcall机制原理
    在linux初始化的过程中,内核采用了一种initcall的机制,它利用gcc的扩展功能以及ld的连接控制脚本实现了在内核初始化的过程中通过简单的循环就实现了相关驱动的初始化。核心代码的/init/main.c里面有do_initcalls函数如下:
static void __init do_initcalls(void)
{
  initcall_t *call;
  call = &__initcall_start;
  do {
   (*call)();
   call++;
  } while (call < &__initcall_end);
  /* Make sure there is no pending stuff from the initcall sequence */
  flush_scheduled_tasks();
}
其中__initcall_start和__initcall_end在源码中并无定义,只是在include/linux/init.h中申明为外部变量:
           extern initcall_t __initcall_start, __initcall_end;
i386平台下,连接控制脚本为vmlinux.lds, 这2各变量的定义是在/arch/i386/vmlinux.lds中,代码如下:
         __initcall_start = .;
           .initcall.init : { *(.initcall.init) }
         __initcall_end = .;
其含义是指示连接程序让__initcall_start指向代码节.initcall.init的节首,而__initcall_end指向.initcall.init的节尾。
       则在内核中,只要把需要初始化调用的函数的指针放在__initcall_start和__initcall_end之间的节内,函数就会在内核初始化时被调用。
类似的内核引用的外部变量还有:…
二、实践:
实际过程分如下几步:       
1、编写主程序doinitcall.c:代码如下并有详细注释:
#include <stdio.h>
typedef int (*initcall_t)(void); /*定义函数指针*/
extern initcall_t __initcall_start, __initcall_end; /*申明外部变量,在ld的脚本文件中定义*/
#define __initcall(fn) /
static initcall_t __initcall_##fn __init_call = fn
#define __init_call      __attribute__ ((unused,__section__ ("function_ptrs")))
#define module_init(x) __initcall(x);
/*上述宏定义名为"__initcall_函数名"的函数指针,且将函数指针放在function_ptrs节
这个函数指针,指向fn(fn函数则放在code_segment节中)*/
#define __init __attribute__ ((__section__ ("code_segment")))
static int __init /*函数放在code_segment节*/
my_init1 (void)
{
       printf ("my_init () #1/n");
       return 0;
}
static int __init
my_init2 (void)
{
       printf ("my_init () #2/n");
       return 0;
}
module_init (my_init1);/*定义要被调用的函数指针并放到指定的节中*/
module_init (my_init2);
void
do_initcalls (void)
{
       initcall_t *call_p; 定义函数指针变量
       call_p = &__initcall_start;/*获取节首址*/
       do {
              fprintf (stderr, "call_p: %p/n", call_p);
              (*call_p)();
              ++call_p;/*32位机器上,函数指针占4bytes,增加一次就是指针便宜4bytes*/
       } while (call_p < &__initcall_end);
}
int
main (void)
{
       fprintf (stderr, "in main()/n");
       do_initcalls (); /*调用*/
       return 0;
}
2、导出默认的连接控制脚本文件:保存为linker.lds
       通过命令gcc -Wl,--verbose可以获得默认的连接控制脚本, 即选择 "=======..."之间的文本,保存为linker.lds文件
3、在linker.lds文件中增加本例需要控制的语句:
       将下面
       /*定义__initcall_start符号为当前位置,即.代表当前位置*/
    __initcall_start = .;
    function_ptrs   : { *(function_ptrs) }
    __initcall_end = .;
    /*上述3行代码代表function_ptrs节位于__initcall_start和__initcall_end之间*/
    code_segment    : { *(code_segment) }
    这段代码copy到linker.lds文件的
           __bss_start = .;
语句之前。
完整的linker.lds文件如下:
/* Script for -z combreloc: combine and sort reloc sections */
OUTPUT_FORMAT("elf32-i386", "elf32-i386",
              "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)
SEARCH_DIR("/usr/i386-redhat-linux/lib"); SEARCH_DIR("/usr/lib"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib");
/* Do we need any of these for elf?
   __DYNAMIC = 0;    */
SECTIONS
{
/* Read-only sections, merged into text segment: */
. = 0x08048000 + SIZEOF_HEADERS;
.interp          : { *(.interp) }
.hash            : { *(.hash) }
.dynsym          : { *(.dynsym) }
.dynstr          : { *(.dynstr) }
.gnu.version     : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rel.dyn         :
    {
      *(.rel.init)
      *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
      *(.rel.fini)
      *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
      *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
      *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
      *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
      *(.rel.ctors)
      *(.rel.dtors)
    *(.rel.got)
      *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
    }
.rela.dyn        :
    {
      *(.rela.init)
      *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
      *(.rela.fini)
      *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
     *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
      *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
      *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
      *(.rela.ctors)
      *(.rela.dtors)
      *(.rela.got)
      *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
    }
.rel.plt         : { *(.rel.plt) }
.rela.plt        : { *(.rela.plt) }
.init            :
{
    KEEP (*(.init))
} =0x90909090
.plt             : { *(.plt) }
.text            :
{
    *(.text .stub .text.* .gnu.linkonce.t.*)
    /* .gnu.warning sections are handled specially by elf32.em. */
    *(.gnu.warning)
} =0x90909090
.fini            :
{
    KEEP (*(.fini))
} =0x90909090
PROVIDE (__etext = .);
PROVIDE (_etext = .);
PROVIDE (etext = .);
.rodata          : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1         : { *(.rodata1) }
.eh_frame_hdr : { *(.eh_frame_hdr) }
.eh_frame        : ONLY_IF_RO { KEEP (*(.eh_frame)) }
.gcc_except_table    : ONLY_IF_RO { *(.gcc_except_table) }
/* Adjust the address for the data segment. We want to adjust up to
     the same address within the page on the next page up. */
. = ALIGN (0x1000) - ((0x1000 - .) & (0x1000 - 1)); . = DATA_SEGMENT_ALIGN (0x1000, 0x1000);
/* Ensure the __preinit_array_start label is properly aligned. We
     could instead move the label definition inside the section, but
     the linker would then create the section even if it turns out to
     be empty, which isn't pretty. */
. = ALIGN(32 / 8);
PROVIDE (__preinit_array_start = .);
.preinit_array      : { *(.preinit_array) }
PROVIDE (__preinit_array_end = .);
PROVIDE (__init_array_start = .);
.init_array      : { *(.init_array) }
PROVIDE (__init_array_end = .);
PROVIDE (__fini_array_start = .);
.fini_array     : { *(.fini_array) }
PROVIDE (__fini_array_end = .);
.data            :
{
    *(.data .data.* .gnu.linkonce.d.*)
    SORT(CONSTRUCTORS)
}
.data1           : { *(.data1) }
.tdata           : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
.tbss            : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
.eh_frame        : ONLY_IF_RW { KEEP (*(.eh_frame)) }
.gcc_except_table    : ONLY_IF_RW { *(.gcc_except_table) }
.dynamic         : { *(.dynamic) }
.ctors           :
{
    /* gcc uses crtbegin.o to find the start of
       the constructors, so we make sure it is
       first. Because this is a wildcard, it
       doesn't matter if the user does not
       actually link against crtbegin.o; the
       linker won't look for a file to match a
       wildcard. The wildcard also means that it
       doesn't matter which directory crtbegin.o
       is in. */
    KEEP (*crtbegin*.o(.ctors))
    /* We don't want to include the .ctor section from
       from the crtend.o file until after the sorted ctors.
       The .ctor section from the crtend file contains the
       end of ctors marker and it must be last */
    KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
}
.dtors           :
{
    KEEP (*crtbegin*.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*(.dtors))
}
.jcr             : { KEEP (*(.jcr)) }
.got             : { *(.got.plt) *(.got) }
_edata = .;
PROVIDE (edata = .);
/*定义__initcall_start符号为当前位置,即.代表当前位置*/
__initcall_start = .;
function_ptrs    : { *(function_ptrs) }
__initcall_end = .;
/*上述3行代码代表function_ptrs节位于__initcall_start和__initcall_end之间*/
code_segment     : { *(code_segment) }
__bss_start = .;
.bss             :
{
   *(.dynbss)
   *(.bss .bss.* .gnu.linkonce.b.*)
   *(COMMON)
   /* Align here to ensure that the .bss section occupies space up to
      _end. Align after .bss to ensure correct alignment even if the
      .bss section disappears because there are no input sections. */
   . = ALIGN(32 / 8);
}
. = ALIGN(32 / 8);
_end = .;
PROVIDE (end = .);
. = DATA_SEGMENT_END (.);
/* Stabs debugging sections. */
.stab           0 : { *(.stab) }
.stabstr        0 : { *(.stabstr) }
.stab.excl      0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index     0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment        0 : { *(.comment) }
/* DWARF debug sections.
     Symbols in the DWARF debugging sections are relative to the beginning
     of the section so we begin them at 0. */
/* DWARF 1 */
.debug           0 : { *(.debug) }
.line            0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info      0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev    0 : { *(.debug_abbrev) }
.debug_line      0 : { *(.debug_line) }
.debug_frame     0 : { *(.debug_frame) }
.debug_str       0 : { *(.debug_str) }
.debug_loc       0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
/DISCARD/ : { *(.note.GNU-stack) }
}
4、编译程序
       命令:
gcc -Tlinker.lds -o doinitcall doinitcall.c
       其中:
-T选项告诉ld要用的连接控制脚本文件,做为链接程序的依据。格式如下:
              -T commandfile
              --script=commandfile
5、执行程序
       可以看到如下结果:
[zhouys@Cluster1 zhouys]$ ./doinitcall       
       in main()
       call_p: 0x804961c
       my_init () #1
       call_p: 0x8049620
       my_init () #2
三、参考资料:
       1Understanding The Linux Kernel Initcall Mechanism
       2、linux2.4.0源码


linux内核中关于网络初始化过程中do_basic_setup等的问题
linux网络初始化时,initmain.c中的start_kernel()函数中调用kernel_thread启动了init进程,该进程对应于 initmain.c中的init函数。init函数中调用do_basic_setup()函数,这个函数又调用了netsocket.c中的 sock_init()函数。
do_basic_setup()的功能:
总线初始化(比如pci_init())
网络初始化(初始化网络数据结构,sock_init();2.4.17中 sock_init()只是初始化了网络数据结构,真正的协议初始化是运行do_initcalls()时自动加载各个协议模块的)
创建事件管理核心线程(start_context_thread())
再加载任何模块(用do_initcalls()启动任何使用__initcall标识的函数(方便核心研发者添加启动函数))
#define module_init(x) __initcall(x);这句在linuxinit.h中,通过这么定义,使得系统初始化到调用initmain.c中的 do_initcalls时自动加载了模块x(进入x初始化)。而在af_inet.c中有module_init(inet_init);所以也包括初始化inet模块了。呵呵
在2.6.14.2中就比较容易理解了。看
static void __init do_basic_setup(void)
{
/* drivers will send hotplug events */
init_workqueues();
usermodehelper_init();
driver_init();
#ifdef CONFIG_SYSCTL
sysctl_init();
#endif
/* Networking initialization needs a process context */
sock_init();
do_initcalls();
}
主要根据代码看的,比较仓促,因为这不是我现在要做的主要工作,来不及周详整理了,先这样记下好了,有异议的朋友欢迎留言讨论,呵呵。

内核启动时,设备及驱动初始化的实现
Uboot完成系统的引导并将Linux内核拷贝到内存之后,bootm -> do_bootm_linux()跳转到kernel的起始位置;
       压缩过的kernel入口在arch/arm/boot/compressed/head.S,它将调用函数 decompress_kernel()<./arch/arm/boot/compressed/misc.c>解压,打印 “Uncompressing Linux...”,调用gunzip(),打印"done, booting the kernel."
       然后call_kernel,执行解压后的kernel,经linux/arch/arm/kernel/head.S调用start_kernel转入体系结构无关的通用C代码,在start_kernel()中完成了一系列系统初始化,设备及驱动的注册即在此时完成:

<./init/main.c>-------------------------

asmlinkage void __init start_kernel(void)
{
char * command_line;
extern struct kernel_param __start___param[], __stop___param[];

···········································································
printk(KERN_NOTICE "Kernel command line: %s/n", saved_command_line);
                                                          //打印内核命令行
parse_early_param();
parse_args("Booting kernel", command_line, __start___param,
     __stop___param - __start___param,
     &unknown_bootoption);
                                                        //解析由BOOT传递的启动参数
···········································································

/* Do the rest non-__init'ed, we're now alive */
rest_init();
}

start_kernel()中的函数rest_init()将创建第一个核心线程kernel_thread(init, NULL, CLONE_FS | CLONE_SIGHAND),调用init()函数:

static int init(void * unused)-------------------
{
                ·······················
                 do_basic_setup();
                ······················

/*
  * We try each of these until one succeeds.
  *
  * The Bourne shell can be used instead of init if we are
  * trying to recover a really broken machine.
  */
if (execute_command) { //判断在启动时是否指定了init参数
                                      //如果指定则执行用户init进程,成功将不会返回
  run_init_process(execute_command);
  printk(KERN_WARNING "Failed to execute %s.  Attempting "
     "defaults.../n", execute_command);
}

               /*   如果没有指定init启动参数,则查找下面的目录init进程,成功将不会返回,否则打印出错信息   */
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");

panic("No init found.  Try passing init= option to kernel.");

}

继而调用函数do_basic_setup()(此时与体系结构相关的部分已经初始化完了,现在开始初始化设备了):

/*
* Ok, the machine is now initialized. None of the devices
* have been touched yet, but the CPU subsystem is up and
* running, and memory and process management works.
*
* Now we can finally start doing some real work..
*/
static void __init do_basic_setup(void)-----------------
{
/* drivers will send hotplug events */
init_workqueues();
usermodehelper_init();
driver_init();     //建立设备模型子系统

#ifdef CONFIG_SYSCTL
sysctl_init();
#endif

/* Networking initialization needs a process context */
sock_init();

do_initcalls();   //系统初始化(包括设备,文件系统,内核模块等)
}
<./drivers/base/init.c>-------------------------
/**
* driver_init - initialize driver model.
*
* Call the driver model init functions to initialize their
* subsystems. Called early from init/main.c.
*/

void __init driver_init(void)
{
/* These are the core pieces */
devices_init();
                       <./drivers/base/core.c>-------------
                                  int __init devices_init(void)
                                  {
                   return subsystem_register(&devices_subsys);
                                  }
                        -----------------------
buses_init();
classes_init();
firmware_init();

/* These are also core pieces, but must come after the
  * core core pieces.
  */
platform_bus_init();
system_bus_init();
cpu_dev_init();
memory_dev_init();
attribute_container_init();
}
---------------------------
extern initcall_t __initcall_start[], __initcall_end[];

static void __init do_initcalls(void)
{
initcall_t *call;
int count = preempt_count();

for (call = __initcall_start; call < __initcall_end; call++) {
                ··················
                (*call)();     //调用一系列初始化函数
               ···················
}
---------------------------
      __initcall_start和__initcall_end界定了存放初始化函数指针区域的起始地址,即从__initcall_start开始到 __initcall_end结束的区域中存放了指向各个初始化函数的函数指针。 由 (*call)()完成各个部分的初始化工作,且便于扩充。具体实现如下:
<./arch/arm/kernel/vmlinux.lds.S>-----------------
  __initcall_start = .;
   *(.initcall1.init)
   *(.initcall2.init)
   *(.initcall3.init)
   *(.initcall4.init)
   *(.initcall5.init)
   *(.initcall6.init)
   *(.initcall7.init)
  __initcall_end = .;

<./include/linux/init.h>---------------------

#ifndef MODULE     /*    如果驱动模块静态编译进内核   */

  ···············································

/* 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)
                                           //此处初始化了设备
                                           /*----eg:arch_initcall(at91sam9261_device_init)---
                                               static int __init at91sam9261_device_init(void)
                                               {
                                                 at91_add_device_udc();
                                                 at91_add_device_dm9000();
                                                 armebs3_add_input_buttons();
                                                 return platform_add_devices(at91sam9261_devices, ARRAY_SIZE(at91sam9261_devices));
                                                }
                                        ------------------------*/

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


  /**
* module_init() - driver initialization entry point
* @x: function to be run at kernel boot time or module insertion
*
* module_init() will either be called during do_initcalls (if
* builtin) or at module insertion time (if a module).  There can only
* be one per module.
*/
#define module_init(x) __initcall(x);
                                       //静态编译的驱动模块作为device_initcall在内核启动就被do_initcalls

/**
* module_exit() - driver exit entry point
* @x: function to be run when driver is removed
*
* module_exit() will wrap the driver clean-up code
* with cleanup_module() when used with rmmod when
* the driver is a module.  If the driver is statically
* compiled into the kernel, module_exit() has no effect.
* There can only be one per module.
*/
#define module_exit(x) __exitcall(x);

#else /* MODULE    如果驱动模块动态加载入内核   */

  ···············································

/* Each module must use one module_init(), or one no_module_init */
#define module_init(initfn)     /
static inline initcall_t __inittest(void)  /
{ return initfn; }     /
int init_module(void) __attribute__((alias(#initfn)));
     //insmod 是通过系统调用sys_init_module(const char *name_user, struct module *mod_user)
     //将动态驱动模块载入到内核空间

/* This is only required if you want to be unloadable. */
#define module_exit(exitfn)     /
static inline exitcall_t __exittest(void)  /
{ return exitfn; }     /
void cleanup_module(void) __attribute__((alias(#exitfn)));

你可能感兴趣的:(do_initcall()函数的说明)