先看這些宏的定義(定義在文件include/linux/init.h中)
#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)
這些宏都用到了__define_initcall(),再看看它的定義(一樣定義在文件include/linux/init.h中)
這其中
initcall_t
是函數指針,原型以下,
typedef int (*initcall_t)(void);
而編譯器宏
__attribute__((__section__()))
則表示把對象放在一個這個由括號中的名稱所指代的section中。
__define_initcall("6",fn,6) 則是把fn的地址放到.
initcall
6
.
init
這個selection中
另外,注意這裏
static
的做用,只是當前編譯單元可見,從全局來看的
__initcall_##fn##id
完整名字,是要加當前編譯文件名(或稱當前編譯單位名)做爲前綴的
因此__define_initcall的含義是:
1)
聲明一個名稱爲__initcall_##fn的函數指針;
2)
將這個函數指針初始化爲fn;
3)
編譯的時候須要把這個函數指針變量放置到名稱爲
".
initcall
"
level
"
.init
"的section中
#define
INITCALLS
\
*(.initcallearly.init) \
VMLINUX_SYMBOL(
__early_initcall_end
)
=
.
; \
*(.initcall0.init) \
*(.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)
VMLINUX_SYMBOL(
__early_initcall_end)
=
.
;
這裏的
.
是一個特殊的符號,它是定位器,一個位置指針,指向程序地址空間內的某位置(或某section內的偏移,若是它在SECTIONS命令內的某section描述內),該符號只能在SECTIONS命令內使用。
這句命令的意思,是對
__early_initcall_end
變量賦值爲
當前section內的偏移
*(
.initcall7s.init
)
表示的是
全部輸入文件
中的名爲
.initcall7s.init
的
SECTIONS
__initcall_start
和
__initcall_end
以及
INITCALLS
中定義的SECTION都是在arch/xxx/kernel/vmlinux.lds.S中放在
.init
段的。
SECTIONS
{
.
init
: {
__initcall_start
=
.
;
INITCALLS
__initcall_end
=
.
;
}
}
.
init
只是用於對
SECTIONS
進行分類
__initcall_start
=
.
;
和
__initcall_end
=
.
;
表示分別對變量
__initcall_start
和
__initcall_end
賦值爲
當前section內的偏移。
而這些
SECTION
裏的函數在初始化時被順序執行(init內核線程->
do_basic_setup()
[main.c#778]->
do_initcalls()
)。
程序(
init/main.c
文件
do_initcalls()
函數)以下,
do_initcalls()
把
XX.initcallXX.init
中的函數按順序都執行一遍。
for
(call =
__early_initcall_end
; call <
_
_initcall_end
; call++)
do_one_initcall(*call);