在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(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)過(guò)程中,會(huì)向終端打印出很多的日志信息,從這些信息中可以得到許多內(nèi)核的行為。如果在啟動(dòng)階段出現(xiàn)了問(wè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è)備還沒(méi)有被初始化,故而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,
};

從上述代碼可見(jiàn),initcall_levels數(shù)組中的元素為initcall_t類型的指針,回到do_initcalls()函數(shù)中,該函數(shù)的核心操作是:按順序從__initcall0_start開(kāi)始,到__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

從上述代碼可見(jiàn),在函數(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ù)指針類型的變量并命名為_(kāi)_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中則是通過(guò)這一點(diǎn)實(shí)現(xiàn)。例如:如果有以下類似的代碼:

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

core_initcall(show_info);

經(jīng)過(guò)層層宏替換后,本質(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è)模塊的初始化,正是通過(guò)使用__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)過(guò)程中去主動(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ù)指針?lè)胖玫侥膫€(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)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 處理器
    +關(guān)注

    關(guān)注

    68

    文章

    19409

    瀏覽量

    231194
  • 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)核自解壓過(guò)程分析

    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>自解壓過(guò)程<b class='flag-5'>分析</b>

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

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

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

    ——找不到main函數(shù)。對(duì)于簡(jiǎn)單的demo程序,我們可以從頭至尾的分析代碼的含義,但是分析內(nèi)核代碼這招就徹底失效了,因?yàn)闆](méi)有人能把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)核存在問(wèn)題的函數(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)過(guò)程會(huì)執(zhí)行用戶空間的init進(jìn)程

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

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

    Linux應(yīng)用開(kāi)發(fā),為使應(yīng)用程序更加靈活地執(zhí)行用戶的預(yù)期功能,我們有時(shí)候會(huì)通過(guò)命令行傳遞一些參數(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, 而沒(méi)有進(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 ?1081次閱讀
    <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>流程
    主站蜘蛛池模板: 美女被艹视频网站 | seetube18日本第一次 | 99久久国产免费 - 99久久国产免费 | 乱码中文字幕人成在线 | 亚洲欧美色中文字幕 | 天天透天天干 | 亚洲美女黄视频 | 久久午夜神器 | 天堂资源地址在线 | 成年男人永久免费看片 | 日本免费黄色片 | 中国videos偷窥 | 欧美一区二区三区影院 | 淫www| 色日本视频 | 男男污肉高h坐便器调教 | 成人看片免费无限观看视频 | 511韩国理论片在线观看 | www.天天操.com | 狠狠做深爱婷婷综合一区 | 伊人久久大香线蕉综合电影 | 久久福利影视 | 婷婷欧美| 日韩日韩 | 日本视频一区二区三区 | 69xxx日本| 狠狠色噜噜综合社区 | 国模在线| 一级毛片免费不卡在线视频 | 激情婷婷网 | 欧美成人观看免费全部完小说 | 欧美一区二区三区在线观看 | 福利视频一区二区牛牛 | 五月天在线婷婷 | 欧美亚洲三级 | 欧美瑟瑟 | 欧美特级生活片 | 亚洲一区二区三区四区在线观看 | 欧美色图综合 | 特级黄一级播放 | 激情婷婷网 |