各種initcall的執行先後順序(module_init、postcore_initcall、arch_initcall、subsys_initcall、 fs_initcall)
現在以module_init為例分析initcall在核心中的呼叫順序
在標頭檔案init.h中,有如下定義:
#define module_init(x) __initcall(x);
很明顯,module_init()只是一個面具而已,揭開這個面具,下面藏著的是__initcall()
__initcall()又是何方神聖呢?繼續揭露真相:
#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
最終我們看到的是module_init的真身:__define_initcall(level,fn,id),仔細推敲這個真身,知道這是個巨集,它把傳給module_init的函式名組裝成以__initcall為字首的、以6為字尾的函式名,並把這個函式定義到程式碼段.initcall6.init裡面。
在程式碼段.initcall6.init裡面?這函式躲在這裡幹嘛,啥時候才輪得到它出頭啊!找到有此字串的檔案vmlinux.lds.h,相關程式碼如下所示:
#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)
要命,又是一個陌生的巨集,不過還好的是他樣子看起來還不難看,而且好找規律,看看這是個什麼樣的東西呢?
字串.initcall6.init夾雜在這個巨集的第n行,具體自己數,他們挨個挨個有順序的組成一個整體,此整體又構成一個用大寫字母寫的巨集INITCALLS,從氣勢看這個東西給人牛逼的感覺,這麼神奇,那他在那裡高就呢?
坑爹的,踏破鐵鞋無覓處,得來非常費工夫,他竟然在一個偏僻的vmlinux.lds.S裡面!估計超出來好多童鞋可以接受的範圍吧,這還不算,這還是個彙編檔案!哎,不管怎樣,追根述源順藤摸瓜找到了INITCALLS的孃家,卻發現這是個完全陌生的世界!蛋蛋為此疼了好久,最終還是鼓起來武松打虎的勇氣依然闖了進去。人說絕望之後就會有希望,柳暗之後又是一村,哥哥我滿眼噙著淚水的發現他們是有人情味的!這個激動啊,不是三言兩語可以說得清的,來看看INITCALLS她孃家房子咋樣:
…………省略一大段………….
__initcall_start = .;
INITCALLS
__initcall_end = .;
__con_initcall_start = .;
*(.con_initcall.init)
__con_initcall_end = .;
__security_initcall_start = .;
*(.security_initcall.init)
__security_initcall_end = .;
…………省略一大段……………
是不是覺得這還是有點人道主義的,不會像阿拉伯為或藏文一樣讓你想跳樓吧,來認識一下它的三大姑二大婆吧,對於像__initcall_start = .與__initcall_end = .之類狐假虎威的傢伙咱們初來乍到時吃過他不少虧,印象是相當深刻的,一眼就瞅見它的衰樣了,這裡的小點不就是代表當前地址嗎,一個等號不就是把點代表的當前地址付給了左邊的變數啦,這難不倒已經有兩把刷子的我的,照此看來,估摸著__initcall_start與__initcall_end會有同夥在.c檔案裡面和他們暗通款曲狼狽為奸,待會再好好戲耍它一番,先pass了,接著看看還有啥新鮮的,嗯?沒了,還是回頭吧,沒苦我一般是不會自找來嘗的,甜頭嘛另當別論啦,哈哈哈,
言歸正傳,INITCALLS在__initcall_start = .與__initcall_end = .之間,表示INITCALLS巨集內涵的相關段程式碼順序存放在這裡,module_init所代表段的鑲嵌其中,等候挨個輪到自己被光顧,從INITCALLS的內容看它被訪問的時刻排得還是挺後的,那麼其他的又是何方聖神呢?其看下面分解:
#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)
至此我們應該明白了各個initcall是如何來的以及在程式碼儲存空間上是怎麼組織的了吧,下面來看看核心是什麼時候呼叫它的。
核心啟動流程如下所示
Main()àlinux_main()àstart_uml()àstart_kernel_proc()àstart_kernelàrest_inità kernel_inità do_basic_setupà do_initcalls
do_initcalls的程式碼如下所示
static void __init do_initcalls(void)
{
initcall_t *call;
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();
}
上面的程式碼中,__early_initcall_end在INITCALLS內定義,__initcall_end在檔案vmlinux.lds.S中定義,他們代表的是一些初始化函式的指標陣列起始與結束地址,執行函式do_initcalls時,包含在這各指標數組裡面的函式順序的被呼叫以執行一些必要的初始化工作。至此,明白的各initcall的執行時刻了吧。
---------------------
作者:fenzhikeji
來源:CSDN
原文:https://blog.csdn.net/fenzhikeji/article/details/6860143
版權宣告:本文為博主原創文章,轉載請附上博文連結!