那曲檬骨新材料有限公司

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

linux內(nèi)核中do_initcalls函數(shù)的執(zhí)行邏輯分析

嵌入式小生 ? 來(lái)源:嵌入式小生 ? 2023-01-13 09:20 ? 次閱讀

一、導(dǎo)讀

linux內(nèi)核啟動(dòng)過程中,會(huì)向終端打印出很多的日志信息,從這些信息中可以得到許多內(nèi)核的行為。如果在啟動(dòng)階段出現(xiàn)了問題,那么很多的提示信息也會(huì)從終端打印出。

這些信息的輸出與具體模塊功能的執(zhí)行都?xì)w功于一個(gè)函數(shù):do_initcalls,本文將主要分析這個(gè)函數(shù)的執(zhí)行邏輯,且從這個(gè)函數(shù)延伸到linux各個(gè)子系統(tǒng)初始化背后的機(jī)制。

本文所有源碼分析基于linux內(nèi)核版本:4.1.15

二、do_initcalls

do_initcalls由do_basic_setup()調(diào)用:

ebe49646-92da-11ed-bfe3-dac502259ad0.png

do_basic_setup()由kernel_init()代表的內(nèi)核init線程函數(shù)間接調(diào)用(在kernel_init_freeable()被調(diào)用)。

在調(diào)用do_basic_setup之前,處理器已經(jīng)被初始化了,CPU子系統(tǒng)已經(jīng)啟動(dòng)并且運(yùn)行,內(nèi)存和進(jìn)程管理也工作正常,但是系統(tǒng)中的設(shè)備還沒有被初始化,故而do_basic_setup正作用于此,本文主要描述do_initcalls,所以不再進(jìn)而分析其他的函數(shù)。

do_initcalls在/init/main.c文件中實(shí)現(xiàn):

staticvoid__initdo_initcalls(void)
{
intlevel;

for(level=0;level

函數(shù)中內(nèi)容比較少,是一個(gè)for循環(huán)結(jié)構(gòu),循環(huán)的對(duì)象是initcall_levels數(shù)組,該數(shù)組用于描述初始化調(diào)用的級(jí)別,定義如下:

externinitcall_t__initcall_start[];
externinitcall_t__initcall0_start[];
externinitcall_t__initcall1_start[];
externinitcall_t__initcall2_start[];
externinitcall_t__initcall3_start[];
externinitcall_t__initcall4_start[];
externinitcall_t__initcall5_start[];
externinitcall_t__initcall6_start[];
externinitcall_t__initcall7_start[];
externinitcall_t__initcall_end[];

staticinitcall_t*initcall_levels[]__initdata={
__initcall0_start,
__initcall1_start,
__initcall2_start,
__initcall3_start,
__initcall4_start,
__initcall5_start,
__initcall6_start,
__initcall7_start,
__initcall_end,
};

從上述代碼可見,initcall_levels數(shù)組中的元素為initcall_t類型的指針,回到do_initcalls()函數(shù)中,該函數(shù)的核心操作是:按順序從__initcall0_start開始,到__initcall_end結(jié)束的節(jié)段(稱為初始化調(diào)用段)中取出不同段之間的函數(shù),并執(zhí)行。

存在這幾個(gè)初始化調(diào)用段之間的函數(shù)都是內(nèi)核中各個(gè)模塊的初始化函數(shù),而這些函數(shù)是如何加入到初始化調(diào)用段中的呢?又是如何設(shè)置調(diào)用級(jí)別的,會(huì)在后文中描述到。

在do_initcalls()函數(shù)中,會(huì)根據(jù)initcall_levels初始化調(diào)用級(jí)別的數(shù)量調(diào)用do_initcall_level(),該函數(shù)實(shí)現(xiàn)如下:

staticvoid__initdo_initcall_level(intlevel)
{
initcall_t*fn;

strcpy(initcall_command_line,saved_command_line);
parse_args(initcall_level_names[level],
initcall_command_line,__start___param,
__stop___param-__start___param,
level,level,
&repair_env_string);

for(fn=initcall_levels[level];fn

從上述代碼可見,在函數(shù)的最后也是一個(gè)for循環(huán)結(jié)構(gòu),該循環(huán)的操作對(duì)象為函數(shù)指針,且會(huì)將對(duì)應(yīng)的函數(shù)指針傳遞到do_one_initcall中,在該函數(shù)執(zhí)行函數(shù)指針?biāo)赶虻暮瘮?shù):

ec54107a-92da-11ed-bfe3-dac502259ad0.png

三、構(gòu)造section并添加函數(shù)

(3-1)構(gòu)造初始化調(diào)用section

在linux內(nèi)核中,不同架構(gòu)(ARCH)下的kernel目錄中,都會(huì)有一個(gè)名為vmlinux.lds.S的鏈接腳本,初始化調(diào)用section的構(gòu)造則在這個(gè)鏈接腳本中完成。

本文以ARM32架構(gòu)為例

在/arch/arm/kernel/vmlinux.lds.S中的鏈接腳本中,.init.data輸出節(jié)段則需要INIT_CALLS作為輸入節(jié)段:

ec7ceb1c-92da-11ed-bfe3-dac502259ad0.png

INIT_CALLS定義在/include/asm-generic/vmlinux.lds.h文件中:

eca878e0-92da-11ed-bfe3-dac502259ad0.png

而在內(nèi)核的makefile中有以下語(yǔ)句:

LDFLAGS_vmlinux+=-Tarch/$(ARCH)/kernel/vmlinux.lds.s

用于指定構(gòu)建linux內(nèi)核鏡像時(shí)所使用的鏈接腳本,基于此,則會(huì)構(gòu)造好初始化調(diào)用section。

當(dāng)初始化調(diào)用section構(gòu)造完成后,是如何向該section中添加函數(shù)的呢?繼續(xù)往下看。

(3-2)向section中添加函數(shù)

向section中添加函數(shù)的本質(zhì)操作則是__define_initcall(),定義如下:

ecd103aa-92da-11ed-bfe3-dac502259ad0.png

并且linux內(nèi)核基于__define_initcall()封裝出了多個(gè)宏定義接口,供內(nèi)核中各個(gè)模塊使用,接口如下:

ece83110-92da-11ed-bfe3-dac502259ad0.png

__define_initcall()宏定義的本質(zhì)則是定義一個(gè)initcall_t函數(shù)指針類型的變量并命名為__initcall_##fn##id,其中fn為賦值給該變量的函數(shù)名稱,id為初始化調(diào)用級(jí)別,然后將fn賦值給該變量。接著就是最為重要的技術(shù)點(diǎn):使用__attribute__將該變量加入到命名為"initcall##id.init"的section中,其中id為初始化調(diào)用級(jí)別,所以將fn添加到初始化調(diào)用section中則是通過這一點(diǎn)實(shí)現(xiàn)。例如:如果有以下類似的代碼:

staticvoid__initshow_info(void)
{
printk("I'miriczhao
")
}

core_initcall(show_info);

經(jīng)過層層宏替換后,本質(zhì)上則變成:

staticinitcall_t__initcall_core_initcall1__used
__attribute__((__section__(".initcall1.init")))=show_info;

四、總結(jié)

綜上,linux內(nèi)核中使用基于__define_initcall封裝出的多個(gè)接口API初始化內(nèi)核的各個(gè)模塊,使用這些API接口會(huì)將指定的函數(shù)放到名稱為.initcall##id.init的section中,id為初始化調(diào)用級(jí)別,內(nèi)核中定義了14種調(diào)用級(jí)別:分別為1~7和1s~7s(linux 3.0后增加的擴(kuò)展)。這些調(diào)用級(jí)別是按照先后順序依次排列的。

(4-1)linux內(nèi)核中,對(duì)于內(nèi)核的各個(gè)模塊的初始化,正是通過使用__define_initcall()的衍生宏定義API將初始化函數(shù)放置到__initcall##id.initsection中,不同模塊的初始化函數(shù)按照調(diào)用級(jí)別順序排列。在內(nèi)核啟動(dòng)階段,這些放置到這個(gè)section中的函數(shù)指針將被do_initcalls()按順序依次調(diào)用,進(jìn)而完成各個(gè)模塊的初始化操作。

linux內(nèi)核系統(tǒng)非常龐大,各個(gè)子系統(tǒng)也非常多,他們的初始化函數(shù)從源碼上是不需要在內(nèi)核啟動(dòng)過程中去主動(dòng)調(diào)用的,從設(shè)計(jì)上這一點(diǎn)也不現(xiàn)實(shí),隨著內(nèi)核功能的增加,越來(lái)越復(fù)雜的驅(qū)動(dòng)程序,從而linux內(nèi)核基于編譯器section技術(shù),設(shè)計(jì)了初始化調(diào)用機(jī)制,將各個(gè)模塊的初始化與linux內(nèi)核啟動(dòng)主線分離。

(4-2)當(dāng)使用基于__define_initcall封裝出的多個(gè)API接口時(shí),函數(shù)指針放置到哪個(gè)子section由具體的宏定義API接口的level參數(shù)確定,較小的level參數(shù)對(duì)應(yīng)的函數(shù)指針則被放置在前面。而位于同一個(gè)子section內(nèi)的函數(shù)指針順序不定,由編譯器按照編譯的順序隨機(jī)指定。所以,如果一個(gè)模塊的初始化函數(shù)想要越早被調(diào)用執(zhí)行,則需要有較小的調(diào)用級(jí)別。





審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 處理器
    +關(guān)注

    關(guān)注

    68

    文章

    19409

    瀏覽量

    231190
  • Arch
    +關(guān)注

    關(guān)注

    0

    文章

    18

    瀏覽量

    9689
  • LINUX內(nèi)核
    +關(guān)注

    關(guān)注

    1

    文章

    316

    瀏覽量

    21744

原文標(biāo)題:驚呆了,linux內(nèi)核中的這機(jī)制 | do_initcalls

文章出處:【微信號(hào):嵌入式小生,微信公眾號(hào):嵌入式小生】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Linux內(nèi)核自解壓過程分析

    uboot完成系統(tǒng)引導(dǎo)以后,執(zhí)行環(huán)境變量bootm的命令;即,將Linux內(nèi)核調(diào)入內(nèi)存并調(diào)用do
    的頭像 發(fā)表于 12-08 14:00 ?1000次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>自解壓過程<b class='flag-5'>分析</b>

    linux內(nèi)核do_fork函數(shù)創(chuàng)建新進(jìn)程

    前面已經(jīng)談了內(nèi)核加載與系統(tǒng)引導(dǎo)過程,下面我們來(lái)看看內(nèi)核do_fork() 函數(shù)是如何創(chuàng)建一個(gè)新的進(jìn)程的。
    發(fā)表于 08-06 08:40

    Linux內(nèi)核源碼之我見——內(nèi)核源碼的分析方法

    ——找不到main函數(shù)。對(duì)于簡(jiǎn)單的demo程序,我們可以從頭至尾的分析代碼的含義,但是分析內(nèi)核代碼這招就徹底失效了,因?yàn)闆]有人能把Linux
    發(fā)表于 05-11 07:00

    簡(jiǎn)單分析linux內(nèi)核的結(jié)構(gòu)體使用方法

    所謂linux驅(qū)動(dòng)編程可以理解為linux內(nèi)核的編程。既然在內(nèi)核編程那就必須要符合內(nèi)核邏輯和各
    發(fā)表于 01-19 08:26

    邏輯代數(shù)與邏輯函數(shù)

    邏輯代數(shù)與邏輯函數(shù):本章主要討論分析和設(shè)計(jì)數(shù)字邏輯功能的數(shù)學(xué)。首先介紹邏輯代數(shù)
    發(fā)表于 09-01 09:11 ?0次下載

    Linux內(nèi)核GPIO操作函數(shù)的詳解分析

    本文檔的主要內(nèi)容詳細(xì)介紹的是Linux內(nèi)核GPIO操作函數(shù)的詳解分析免費(fèi)下載。
    發(fā)表于 01-22 16:58 ?28次下載

    Linux內(nèi)核熱補(bǔ)丁安全隱患的探索

    修復(fù)的函數(shù)替換掉內(nèi)核存在問題的函數(shù)從而達(dá)到修復(fù)目的。 函數(shù)替換的思想比較簡(jiǎn)單,就是在執(zhí)行
    的頭像 發(fā)表于 10-11 11:54 ?1794次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>熱補(bǔ)丁安全隱患的探索

    Linux內(nèi)核系統(tǒng)調(diào)用概述及實(shí)現(xiàn)原理

    本文介紹了系統(tǒng)調(diào)用的一些實(shí)現(xiàn)細(xì)節(jié)。首先分析了系統(tǒng)調(diào)用的意義,它們與庫(kù)函數(shù)和應(yīng)用程序接口(API)有怎樣的關(guān)系。然后,我們考察了Linux內(nèi)核如何實(shí)現(xiàn)系統(tǒng)調(diào)用,以及
    的頭像 發(fā)表于 05-14 14:11 ?2263次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>系統(tǒng)調(diào)用概述及實(shí)現(xiàn)原理

    linux內(nèi)核啟動(dòng)過程會(huì)執(zhí)行用戶空間的init進(jìn)程

    linux內(nèi)核啟動(dòng)過程的后期,在kernel_init()函數(shù)代表的init線程,會(huì)嘗試執(zhí)行用戶空間的init進(jìn)程
    的頭像 發(fā)表于 10-14 09:12 ?1210次閱讀

    Linux內(nèi)核模塊參數(shù)傳遞與sysfs文件系統(tǒng)

    Linux應(yīng)用開發(fā),為使應(yīng)用程序更加靈活地執(zhí)行用戶的預(yù)期功能,我們有時(shí)候會(huì)通過命令行傳遞一些參數(shù)到main函數(shù),使得代碼
    發(fā)表于 06-07 16:23 ?2193次閱讀

    Linux內(nèi)核SoftIrq源代碼分析

    我們?cè)?b class='flag-5'>分析linux內(nèi)核中斷剖析時(shí),簡(jiǎn)單的聊了一下SOFTIRQ, 而沒有進(jìn)行深入分析. Linux內(nèi)核
    發(fā)表于 06-23 15:22 ?639次閱讀

    萬(wàn)千設(shè)備,linux內(nèi)核如何知道?

    linux內(nèi)核設(shè)備的注冊(cè)由device_register()函數(shù)完成,這個(gè)函數(shù)linux設(shè)備驅(qū)動(dòng)模型的核心
    的頭像 發(fā)表于 07-12 08:52 ?899次閱讀
    萬(wàn)千設(shè)備,<b class='flag-5'>linux</b><b class='flag-5'>內(nèi)核</b>如何知道?

    Linux內(nèi)核如何使用結(jié)構(gòu)體和函數(shù)指針?

    我將結(jié)合具體的Linux內(nèi)核驅(qū)動(dòng)框架代碼來(lái)展示Linux內(nèi)核如何使用結(jié)構(gòu)體和函數(shù)指針。
    的頭像 發(fā)表于 09-06 14:17 ?1078次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>如何使用結(jié)構(gòu)體和<b class='flag-5'>函數(shù)</b>指針?

    Linux驅(qū)動(dòng)是如何掛載的

    | --- >do_initcalls | --- >do_initcall_level | --- >do_one_initcall 注意,這里就是驅(qū)動(dòng)的初始化和驅(qū)動(dòng)模塊的加載。 我們知道在 rest_
    的頭像 發(fā)表于 09-28 16:48 ?1082次閱讀
    <b class='flag-5'>Linux</b>驅(qū)動(dòng)是如何掛載的

    bootm命令的執(zhí)行流程

    Bootm命令用來(lái)從memory啟動(dòng)內(nèi)核,bootm命令的執(zhí)行流程如下圖所示。 在串口終端輸入bootm命令后,執(zhí)行do_bootm函數(shù)來(lái)完
    的頭像 發(fā)表于 12-04 17:33 ?1228次閱讀
    bootm命令的<b class='flag-5'>執(zhí)行</b>流程
    太阳城百家乐官网外挂| 专栏| 百家乐庄闲点| 网上百家乐官网游戏下载| 总统线上娱乐城| 凯斯百家乐的玩法技巧和规则| 大上海百家乐官网娱乐城| 百家乐官网重要心态| 大发888娱乐送体验金| 百家乐15人桌布| 百家乐官网送钱平台| 百家乐官网破解策略| 皇冠足球投注网| 大发888下载不了| 百家乐香港六合彩| V博百家乐官网的玩法技巧和规则| 百家乐官网网址讯博网| 德州扑克发牌顺序| 富二代百家乐的玩法技巧和规则 | 网络棋牌游戏平台| 澳门玩百家乐的玩法技巧和规则| 做生意进门风水| 百家乐官网赌场走势图| 汤原县| 瑞丰国际开户| 全讯网ceo| 网络百家乐现金游戏哪里的信誉好啊| 杨公24水口| 百家乐官网任你博娱乐平台| 百家乐官网有没有攻略| 世界十大博彩公司| 大发888大发888娱乐游戏| 太原百家乐的玩法技巧和规则| 百家乐游戏平台排名| 蓝盾百家乐官网娱乐场开户注册 | 网络百家乐官网真假| 网上百家乐官网哪里| 六合彩摇奖结果| 大发888 备用6222.com| 威尼斯人娱乐场 老品牌值得您信赖| 百家乐斗地主|