1. 程式人生 > >對於核心中各種xxx_initcall呼叫的分析———linux子系統初始化

對於核心中各種xxx_initcall呼叫的分析———linux子系統初始化

對應核心版本  linux-2.6.32.63, 架構arm(版本較老,但已經屬於2.6以後的版本了)。

一、

xxx_initcall是一系列子系統的初始化入口函式

對應檔案include/linux/init.h

#define early_initcall(fn)           __define_initcall("early",fn,early)

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

對於我們在驅動中常見的module_init入口函式

#define module_init(x)     __initcall(x);

#define __initcall(fn)       device_initcall(fn)

#define device_initcall(fn)                 __define_initcall("6",fn,6)

#define __define_initcall(level,fn,id) \

         static  initcall_t   __initcall_##fn##id  __used \

         __attribute__((__section__(".initcall"level ".init"))) = fn

typedef int (*initcall_t)(void);

 所以對於一個module_init,其展開後即為

module_init(xxxx_init)  展開為

__define_initcall(“6",xxxx_init,6)

__define_initcall(“6",xxxx_init,6)繼續展開

static initcall_t   __initcall_xxxx_init6    __used  __attribute__((__section__(".initcall" “6"".init"))) = xxxx_init

結論一:所以最後可以看到 module_init(xxxx_init)展開後是一個 initcall_t 型別指向xxxx_init的函式指標,並且存放在__section__(".initcall" “6"".init"))段中,其實對於其他的xxx_initcall也是相同的道理,最後解析出來都是一個存放在相應段中的函式指標。這些段中的一系列指標有一定的執行順序,就是前面的一系列early,0,1,1s,2 ..etc數字了。後續來分析下他們是如何被按照順序執行的

二、

include/asm-generic/vmlinux.lds.h  

#define INITCALLS                                                           \

         *(.initcallearly.init)                                                 \                 對應__define_initcall("early",fn,early)

         VMLINUX_SYMBOL(__early_initcall_end)= .;                            \

         *(.initcall0.init)                                                        \                 對應__define_initcall("n",fn,n)

         *(.initcall0s.init)                                                      \               依次類推。。。

         *(.initcall1.init)                                                        \

         *(.initcall1s.init)                                                      \

         *(.initcall2.init)                                                        \

         *(.initcall2s.init)                                                      \

         *(.initcall3.init)                                                        \

         *(.initcall3s.init)                                                      \

         *(.initcall4.init)                                                        \

         *(.initcall4s.init)                                                      \

         *(.initcall5.init)                                                        \

         *(.initcall5s.init)                                                      \

         *(.initcallrootfs.init)                                                        \

         *(.initcall6.init)                                                        \

         *(.initcall6s.init)                                                      \

         *(.initcall7.init)                                                        \

         *(.initcall7s.init)

arch/arm/kernel/vmlinux.lds.S            段分佈  

SECTIONS

{

#ifdef CONFIG_XIP_KERNEL

         .= XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR);

#else

         .= PAGE_OFFSET + TEXT_OFFSET;

#endif

         .text.head: {

                   _stext= .;

                   _sinittext= .;

                   *(.text.head)

         }

         .init: {                         /* Init codeand data                 */

                            INIT_TEXT

                   _einittext= .;

                   __proc_info_begin= .;

                            *(.proc.info.init)

                   __proc_info_end= .;

                   __arch_info_begin= .;

                            *(.arch.info.init)

                   __arch_info_end= .;

                   __tagtable_begin= .;                         //uboot列表段,在parse_tag中解析uboot向kernel傳遞的引數。

                           *(.taglist.init)

                   __tagtable_end= .;

                   .= ALIGN(16);

                  __setup_start = .;

                            *(.init.setup)

                   __setup_end= .;

                   __early_begin= .;

                            *(.early_param.init)

                   __early_end= .;

                   __initcall_start= .;                              //init段,就是前面 INITCALLS ,在具體核心中所在的段

                           INITCALLS

                   __initcall_end= .;

                  __con_initcall_start= .;

                            *(.con_initcall.init)

                   __con_initcall_end= .;

                   __security_initcall_start= .;

                            *(.security_initcall.init)

                   __security_initcall_end= .;

#ifdef CONFIG_BLK_DEV_INITRD,//cpio相關的段, 若核心支援cpio型別的檔案系統,則會把cpio型別的rootfs放在該段中

                   .= ALIGN(32);

                  __initramfs_start= .;

                           usr/built-in.o(.init.ramfs)

                   __initramfs_end= .;

#endif

                   .= ALIGN(PAGE_SIZE);

                  __per_cpu_load= .;

                  __per_cpu_start= .;

                           *(.data.percpu.page_aligned)

                            *(.data.percpu)

                           *(.data.percpu.shared_aligned)

                   __per_cpu_end= .;

#ifndef CONFIG_XIP_KERNEL

                   __init_begin= _stext;

                   INIT_DATA

                   .= ALIGN(PAGE_SIZE);

                   __init_end= .;

#endif

         }

。。。。。。。。

}

結論二:由上面的分析可知, module_init等一系列子系統入口函式,在編譯後會存放在.init段中的__initcall_start__initcall_end地址中,且其順序是按照early,0,1,1s,2 ..依次存放.

三、

關於這些子系統入口函式在核心中的呼叫

init/main.c

kernel_init-->do_basic_setup-->do_initcalls

static void __init do_initcalls(void)
{
initcall_t *call;

/*正好是所有xxx_initcall所在段的起始地址到結束地址*/
for (call = __early_initcall_end; call <__initcall_end; call++)
do_one_initcall(*call);

/* Make sure there is no pending stuff from the initcall sequence */
flush_scheduled_work();
}

總結

1、由結論一可知,這一系列xxx_initcall其實是一系列函式指標.

2、由結論二可以,這系列函式指標在編譯後按照一定的順序存放在.init段中的__initcall_start__initcall_end地址中

3、這一系列指標會在do_initcalls(void)被依次呼叫,依次進入到各個驅動子模組的入口函式中