加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專(zhuān)業(yè)用戶(hù)
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
    • fs_initcall函數(shù)介紹:
    • fs_initcall函數(shù)調(diào)用的層次:
    • 每個(gè)section空間排布情況是如何的?
    • 內(nèi)核執(zhí)行順序是?
    • 結(jié)語(yǔ)
  • 推薦器件
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

linux內(nèi)核initcall放置在各個(gè)section中函數(shù)執(zhí)行流程

01/08 10:40
3466
閱讀需 22 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

作者:良知猶存,轉(zhuǎn)載授權(quán)以及圍觀:歡迎關(guān)注微信公眾號(hào):羽林君

linux以及嵌入式一些代碼,我們看到core_initcall、device_initcall等等需要鏈接器分配各個(gè)section,并且在啟動(dòng)該模塊時(shí)候執(zhí)行。下面我們?cè)敿?xì)追溯一下執(zhí)行過(guò)程。

fs_initcall函數(shù)介紹:

Linux內(nèi)核中的fs_initcall函數(shù):用于在引導(dǎo)過(guò)程中進(jìn)行文件系統(tǒng)等初始化。

初始化注冊(cè):當(dāng)文件系統(tǒng)模塊被加載時(shí),它使用fs_initcall宏注冊(cè)其初始化函數(shù)。該宏將初始化函數(shù)添加到__initcall_fs部分。

內(nèi)核引導(dǎo)過(guò)程:在引導(dǎo)過(guò)程中,在基本硬件初始化和內(nèi)存設(shè)置之后,內(nèi)核開(kāi)始執(zhí)行初始化函數(shù)。

執(zhí)行fs_initcall函數(shù):fs_initcall函數(shù)按照其注冊(cè)順序依次執(zhí)行。這些函數(shù)初始化各種文件系統(tǒng)并執(zhí)行必要的設(shè)置任務(wù)。

文件系統(tǒng)初始化:每個(gè)fs_initcall函數(shù)負(fù)責(zé)設(shè)置和初始化特定的內(nèi)容。這可能涉及初始化數(shù)據(jù)結(jié)構(gòu)、注冊(cè)文件系統(tǒng)類(lèi)型、準(zhǔn)備緩存和其他相關(guān)任務(wù)。

完成和交接:一旦所有fs_initcall函數(shù)都執(zhí)行完畢,內(nèi)核會(huì)繼續(xù)完成引導(dǎo)過(guò)程,包括啟動(dòng)用戶(hù)空間和初始化設(shè)備。

下面是一個(gè)簡(jiǎn)單的示例代碼,展示了fs_initcall函數(shù)的使用和文件系統(tǒng)初始化的過(guò)程:

#include?<linux/init.h>
#include?<linux/module.h>

static?int?__init?my_filesystem_init(void)?{
????//?執(zhí)行文件系統(tǒng)特定的初始化任務(wù)
????printk(KERN_INFO?"My?Filesystem:?Initializingn");
????//?其他初始化操作...

????return?0;
}

fs_initcall(my_filesystem_init);

MODULE_LICENSE("GPL");

這其中my_filesystem_init函數(shù)被注冊(cè)為fs_initcall函數(shù)。當(dāng)模塊加載時(shí),該初始化函數(shù)將被執(zhí)行,完成特定文件系統(tǒng)的初始化任務(wù)。實(shí)際的文件系統(tǒng)模塊會(huì)包含更多復(fù)雜的初始化邏輯,這個(gè)例子只是用來(lái)展示fs_initcall函數(shù)的基本用法。

fs_initcall函數(shù)調(diào)用的層次:

在Linux內(nèi)核中,fs_initcall宏實(shí)際上是通過(guò)__define_initcall來(lái)定義的。下面是__define_initcall的定義:

#define?__define_initcall(fn,?id)?
????static?initcall_t?__initcall_##fn##id?__used?
????__attribute__((__section__(".initcall"?#id?".init")))?=?fn

這段代碼展示了__define_initcall的定義方式。在這里,__define_initcall宏創(chuàng)建了一個(gè)靜態(tài)的initcall_t類(lèi)型變量,并將其放置在特定的.initcall節(jié)(section)中。這樣,在內(nèi)核初始化時(shí),這些函數(shù)就會(huì)按照其在源代碼中出現(xiàn)的順序被依次調(diào)用。

fs_initcall實(shí)際上是通過(guò)__define_initcall宏來(lái)實(shí)現(xiàn)的,它們共同構(gòu)成了Linux內(nèi)核中初始化調(diào)用機(jī)制的一部分。

fs_initcall函數(shù)被放置的section的位置

在Linux內(nèi)核中,.initcall節(jié)(section)是通過(guò)鏈接腳本(linker script)定義的。鏈接腳本指定了可執(zhí)行文件的內(nèi)存布局,包括代碼、數(shù)據(jù)和其他段的放置位置。

對(duì)于.initcall節(jié)(section),它通常由鏈接腳本中的一些規(guī)則來(lái)定義。這個(gè)節(jié)用于存放初始化函數(shù)的地址,以便在內(nèi)核啟動(dòng)時(shí)按照順序執(zhí)行這些初始化函數(shù)。

具體的定義可能會(huì)因內(nèi)核版本和架構(gòu)而異,但通??梢栽趦?nèi)核源代碼的arch/<architecture>/kernel/vmlinux.lds.S或類(lèi)似的文件中找到相關(guān)的鏈接腳本定義。在這些文件中,我們可以看到下面的內(nèi)容:

.initcall.init?:?{
????INIT_CALLS
}

文件位置:linux-xxxarcharmkernelvmlinux.lds.S

文件位置:linux-xxxincludeasm-genericvmlinux.lds.h

???????????__initcall_start?=?.;
???????????.initcall.init?:?{
?????????????????*(.initcall1.init)
????????????????...
?????????????????*(.initcall7.init)
?????????????????}
???????????__initcall_end?=?.;

上面是INIT_CALLS對(duì)應(yīng)的函數(shù),*(.initcall##level##.init),這個(gè)函數(shù)就對(duì)應(yīng)了__define_initcall宏里面的__section__(".initcall" #id ".init"),繼續(xù)查看fs_initcall,對(duì)應(yīng)的level就是這個(gè)部分__define_initcall(fn, 5)的5。

上述示例中的INIT_CALLS通常會(huì)包含對(duì).initcall節(jié)(section)的定義,規(guī)定了將哪些符號(hào)放入該節(jié)中。這些定義可能會(huì)隨著不同的內(nèi)核版本和架構(gòu)而有所不同,但其基本思想是相似的:將初始化函數(shù)的地址放入特定的節(jié)(section)中,以便在啟動(dòng)時(shí)按順序執(zhí)行這些函數(shù)。

__define_initcall這個(gè)宏也是可以設(shè)置多個(gè)初始化函數(shù),并將它們放置在不同的.initcall節(jié)(section)中。

假設(shè)我們有兩個(gè)初始化函數(shù):init_function_1init_function_2,我們可以使用上述宏定義來(lái)將它們分別放置在不同的.initcall節(jié)(section)中。

//?定義多個(gè)初始化函數(shù)
static?void?__init?init_function_1(void)?{
????//?初始化函數(shù)1的內(nèi)容
}

static?void?__init?init_function_2(void)?{
????//?初始化函數(shù)2的內(nèi)容
}

//?使用?__define_initcall?宏定義來(lái)設(shè)置多個(gè)函數(shù)
__define_initcall(init_function_1,?1);
__define_initcall(init_function_2,?2);

在這個(gè)例子中,init_function_1被放置在.initcall1.init節(jié)(section)中,而init_function_2則被放置在.initcall2.init節(jié)(section)中。這樣,在內(nèi)核啟動(dòng)時(shí),這些函數(shù)就會(huì)按照其在源文件中出現(xiàn)的順序依次被調(diào)用。

通過(guò)使用帶有不同標(biāo)識(shí)符的宏定義,可以將多個(gè)初始化函數(shù)放置在不同的.initcall節(jié)(section)中,從而實(shí)現(xiàn)按順序執(zhí)行多個(gè)初始化函數(shù)的目的。

以af_inet.c里面的fs_initcall(inet_init);fs_initcall(ipv4_offload_init);介紹放置的情況:怎么在section放置的

在這個(gè)例子中,fs_initcall宏用于將inet_initipv4_offload_init函數(shù)放置在.initcall.init節(jié)(section)中。這樣,在內(nèi)核啟動(dòng)時(shí),這些函數(shù)就會(huì)按照其在源文件中出現(xiàn)的順序依次被調(diào)用。

下面是簡(jiǎn)化版本的代碼:

//?定義要初始化的函數(shù)
static?void?__init?inet_init(void)?{
????//?inet_init的初始化內(nèi)容
}

static?void?__init?ipv4_offload_init(void)?{
????//?ipv4_offload_init的初始化內(nèi)容
}

//?使用?fs_initcall?宏將函數(shù)放置在?.initcall.init?節(jié)(section)中
fs_initcall(inet_init);
fs_initcall(ipv4_offload_init);

上述代碼,inet_initipv4_offload_init函數(shù)會(huì)被放置在.initcall.init節(jié)(section)中,以便在內(nèi)核啟動(dòng)時(shí)按照其在源文件中出現(xiàn)的順序依次被調(diào)用。

通過(guò)這個(gè)例子,我們明白了如何使用fs_initcall宏將這兩個(gè)函數(shù)放置在.initcall.init節(jié)(section)中.

每個(gè)section空間排布情況是如何的?

還是上面inet_initipv4_offload_init函數(shù)來(lái)介紹,由于fs_initcall宏使用了__attribute__((__section__(".initcall.init"))),這將導(dǎo)致這些函數(shù)被放置在.initcall.init節(jié)(section)中。這樣,在鏈接時(shí),這些函數(shù)的地址將按照其在源文件中出現(xiàn)的順序排布在該特定的節(jié)(section)內(nèi)。

這些函數(shù)位于可執(zhí)行文件的內(nèi)存中的某個(gè)位置,它們的排布情況如下所示:

|---------------------|
|?????.text?section???|
|---------------------|
|????...?other?sections?...???|
|---------------------|
|??.initcall.init?section??|
|---------------------|
|???????inet_init??????|
|---------------------|
|??ipv4_offload_init???|
|---------------------|
|????...?other?functions?...???|
|---------------------|
|???????.data?section??|
|---------------------|
|??????...?other?sections?...????|
|---------------------|

在這個(gè)示例中,.initcall.init節(jié)(section)包含了inet_initipv4_offload_init函數(shù),它們會(huì)按照它們?cè)谠创a中出現(xiàn)的順序排布在該節(jié)(section)中。這樣,在內(nèi)核啟動(dòng)時(shí),這些函數(shù)就會(huì)按照它們?cè)?code>.initcall.init節(jié)(section)中的排布順序依次被調(diào)用。

這里section的大小是隨機(jī)按照大小自動(dòng)分配還是需要開(kāi)發(fā)者設(shè)置好

在一般情況下,.initcall.init這樣的特殊節(jié)(section)的大小是由鏈接器自動(dòng)分配的,而不是由開(kāi)發(fā)者手動(dòng)設(shè)置的。當(dāng)鏈接器處理可執(zhí)行文件時(shí),它會(huì)根據(jù)各個(gè)節(jié)(section)中的內(nèi)容以及鏈接腳本中的規(guī)則來(lái)確定每個(gè)節(jié)(section)的大小和排布。

對(duì)于.initcall.init節(jié)(section),其大小將取決于其中包含的初始化函數(shù)的數(shù)量和大小。鏈接器會(huì)根據(jù)這些函數(shù)的地址和大小來(lái)動(dòng)態(tài)地分配空間,以便容納所有的初始化函數(shù)。

因此,開(kāi)發(fā)者通常無(wú)需手動(dòng)設(shè)置.initcall.init節(jié)(section)的大小。相反,鏈接器會(huì)根據(jù)實(shí)際情況自動(dòng)進(jìn)行分配,確保所有的初始化函數(shù)都能被正確地安置在這個(gè)特定的節(jié)(section)中,并且在內(nèi)核啟動(dòng)時(shí)按照順序被調(diào)用。

如何自己設(shè)置section的大小

在一般情況下,開(kāi)發(fā)者通常不需要手動(dòng)設(shè)置節(jié)(section)的大小。鏈接器會(huì)根據(jù)鏈接腳本中的規(guī)則和可執(zhí)行文件中各個(gè)部分的大小自動(dòng)進(jìn)行分配。

如果我們有特殊需求,希望手動(dòng)設(shè)置某個(gè)節(jié)(section)的大小,可以通過(guò)鏈接腳本來(lái)實(shí)現(xiàn)。在鏈接腳本中,我們可以定義節(jié)(section)的起始位置、大小以及其他屬性。

以下簡(jiǎn)單的模板,在鏈接腳本中手動(dòng)設(shè)置一個(gè)名為.my_section的節(jié)(section)的大?。?/p>

.my_section : {
    /* 定義節(jié)(section)的起始位置 */
    start = .;
    /* 設(shè)置節(jié)(section)的大小為固定值(例如0x1000)*/
    input_section(.text);
    input_section(.data);
    /* 其他內(nèi)容... */
    end = .;
} > RAM

在這個(gè)示例中,.my_section節(jié)(section)被手動(dòng)設(shè)置為包含.text.data節(jié)(section)的內(nèi)容,并且其大小被設(shè)置為固定值。當(dāng)鏈接器處理可執(zhí)行文件時(shí),它將按照這些規(guī)則來(lái)分配空間并確定這個(gè)特定節(jié)(section)的大小。

需要注意的是,手動(dòng)設(shè)置節(jié)(section)的大小可能需要對(duì)鏈接腳本和鏈接過(guò)程有深入的了解,因此在大多數(shù)情況下,開(kāi)發(fā)者無(wú)需手動(dòng)設(shè)置節(jié)(section)的大小,而是依賴(lài)于鏈接器自動(dòng)進(jìn)行分配。

這個(gè)是我實(shí)際應(yīng)用的一款芯片的鏈接修改:

FUN?0x400?(0x10000-0x400)
{
????;cpu.o?(+RO)
????xlib.a?(+RO)
}

上面這部分我使用的鏈接腳本中一部分內(nèi)容。為FUN的節(jié)(section)中,它的起始地址為0x10000,大小為0x400。

在這個(gè)節(jié)(section)中包含了兩個(gè)文件:cpu.oxlib.a,它們都被標(biāo)記為只讀(Read-Only)。鏈接器會(huì)將這兩個(gè)文件的只讀部分放置在由FUN定義的地址范圍內(nèi)。

鏈接腳本用于指導(dǎo)鏈接器如何組織可執(zhí)行文件的各個(gè)部分,包括節(jié)(section)的排布和屬性。這個(gè)片段也是屬于鏈接腳本的一部分,這個(gè)里面鏈接器會(huì)將cpu.oxlib.a的只讀部分放置在從0x10000開(kāi)始、大小為0x400的范圍內(nèi)。

只是一個(gè)demo示例,如果進(jìn)一步操作這個(gè)鏈接腳本,我們要參考特定的鏈接器文檔以及相關(guān)的目標(biāo)平臺(tái)和工具鏈的文檔,以確保正確地設(shè)置節(jié)(section)的屬性和排布。芯片之間區(qū)別挺大的。

內(nèi)核執(zhí)行順序是?

介紹完了section片段,我們?cè)賮?lái)說(shuō)一下,這些函數(shù)的初始化位置以及執(zhí)行順序。

上面我們介紹了vmlinux.lds.S中的INIT_CALLS就是我們定義好的那些函數(shù),那他們?cè)趺幢徽{(diào)用的呢
在Linux內(nèi)核啟動(dòng)過(guò)程中,INIT_CALLS(包括subsys_initcall,fs_initcall,device_initcall等)會(huì)在不同的階段被執(zhí)行。這些初始化調(diào)用是通過(guò)鏈接器腳本和特定的內(nèi)核宏來(lái)安排的。

具體來(lái)說(shuō),INIT_CALLS的執(zhí)行時(shí)機(jī)如下:

    1. 在內(nèi)核啟動(dòng)的早期階段,

start_kernel

    1. 函數(shù)會(huì)調(diào)用

rest_init

    1. 。在

rest_init

    1. 中,會(huì)觸發(fā)

do_basic_setup

    1. 函數(shù)的執(zhí)行,其中包括對(duì)文件系統(tǒng)的基本設(shè)置。

    1. 在do_basic_setup
    1. 函數(shù)中,會(huì)調(diào)用do_initcalls
    1. 函數(shù)。

  1. 在do_initcalls函數(shù)中,各種初始化函數(shù)會(huì)按照鏈接器腳本中的順序被執(zhí)行。

  2. fs_initcall函數(shù)是其中之一。

    看到了執(zhí)行過(guò)程,其中是按照各個(gè)level進(jìn)行調(diào)用的,而__define_initcall(level,fn)的作用就是指示編譯器把一些初始化函數(shù)的指針(即:函數(shù)起始地址)按照順序放置一個(gè)名為 .initcall.init 的section中,這個(gè)section又被分成了n個(gè)子section,它們按順序排列。在內(nèi)核初始化階段,這些放置到這個(gè)section中的函數(shù)指針將供do_initcalls() 按順序依次調(diào)用,來(lái)完成相應(yīng)初始化。

    而函數(shù)指針?lè)胖玫降淖觭ection由宏定義的level確定,對(duì)應(yīng)level較小的子section位于較前面。而位于同一個(gè)子section內(nèi)的函數(shù)指針順序不定,將由編譯器按照編譯的順序隨機(jī)指定。同理,如果我們想先執(zhí)行一些定義的函數(shù),那就可以把它們放置于level比較小的定義中。

    結(jié)語(yǔ)

    這就是我自己對(duì)于linux內(nèi)核initcall放置在各個(gè)section中函數(shù)執(zhí)行流程的一些分享。如果大家有更好的想法,也歡迎大家加我好友交流。

推薦器件

更多器件
器件型號(hào) 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊(cè) ECAD模型 風(fēng)險(xiǎn)等級(jí) 參考價(jià)格 更多信息
HFBR-2542ETZ 1 Avago Technologies FIBER OPTIC RECEIVER, THROUGH HOLE MOUNT, ROHS COMPLIANT, PLASTIC, PACKAGE-8/6

ECAD模型

下載ECAD模型
$84.95 查看
KSZ8721BLI-TR 1 Microchip Technology Inc DATACOM, ETHERNET TRANSCEIVER, PQFP48
$3.1 查看
XPDV4121R-WF-FP 1 Finisar Corporation Photodiode Detector
暫無(wú)數(shù)據(jù) 查看

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

一個(gè)程序員,喜歡寫(xiě)文章,還喜歡打籃球,也喜歡吉他鋼琴的駁雜之人。日常更新自己,分享包括但不限于C/C++、嵌入式、物聯(lián)網(wǎng)、Linux等編程學(xué)習(xí)筆記,同時(shí),公眾號(hào)內(nèi)包含大量的學(xué)習(xí)資源。歡迎關(guān)注,一同交流學(xué)習(xí),共同進(jìn)步!