精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

linux內核中do_initcalls函數的執行邏輯分析

嵌入式小生 ? 來源:嵌入式小生 ? 2023-01-13 09:20 ? 次閱讀

一、導讀

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

這些信息的輸出與具體模塊功能的執行都歸功于一個函數:do_initcalls,本文將主要分析這個函數的執行邏輯,且從這個函數延伸到linux各個子系統初始化背后的機制。

本文所有源碼分析基于linux內核版本:4.1.15

二、do_initcalls

do_initcalls由do_basic_setup()調用:

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

do_basic_setup()由kernel_init()代表的內核init線程函數間接調用(在kernel_init_freeable()被調用)。

在調用do_basic_setup之前,處理器已經被初始化了,CPU子系統已經啟動并且運行,內存和進程管理也工作正常,但是系統中的設備還沒有被初始化,故而do_basic_setup正作用于此,本文主要描述do_initcalls,所以不再進而分析其他的函數。

do_initcalls在/init/main.c文件中實現:

staticvoid__initdo_initcalls(void)
{
intlevel;

for(level=0;level

函數中內容比較少,是一個for循環結構,循環的對象是initcall_levels數組,該數組用于描述初始化調用的級別,定義如下:

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數組中的元素為initcall_t類型的指針,回到do_initcalls()函數中,該函數的核心操作是:按順序從__initcall0_start開始,到__initcall_end結束的節段(稱為初始化調用段)中取出不同段之間的函數,并執行。

存在這幾個初始化調用段之間的函數都是內核中各個模塊的初始化函數,而這些函數是如何加入到初始化調用段中的呢?又是如何設置調用級別的,會在后文中描述到。

在do_initcalls()函數中,會根據initcall_levels初始化調用級別的數量調用do_initcall_level(),該函數實現如下:

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

從上述代碼可見,在函數的最后也是一個for循環結構,該循環的操作對象為函數指針,且會將對應的函數指針傳遞到do_one_initcall中,在該函數執行函數指針所指向的函數:

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

三、構造section并添加函數

(3-1)構造初始化調用section

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

本文以ARM32架構為例

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

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

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

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

而在內核的makefile中有以下語句:

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

用于指定構建linux內核鏡像時所使用的鏈接腳本,基于此,則會構造好初始化調用section。

當初始化調用section構造完成后,是如何向該section中添加函數的呢?繼續往下看。

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

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

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

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

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

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

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

core_initcall(show_info);

經過層層宏替換后,本質上則變成:

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

四、總結

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

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

linux內核系統非常龐大,各個子系統也非常多,他們的初始化函數從源碼上是不需要在內核啟動過程中去主動調用的,從設計上這一點也不現實,隨著內核功能的增加,越來越復雜的驅動程序,從而linux內核基于編譯器section技術,設計了初始化調用機制,將各個模塊的初始化與linux內核啟動主線分離。

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





審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 處理器
    +關注

    關注

    68

    文章

    19178

    瀏覽量

    229201
  • Arch
    +關注

    關注

    0

    文章

    18

    瀏覽量

    9638
  • LINUX內核
    +關注

    關注

    1

    文章

    316

    瀏覽量

    21619

原文標題:驚呆了,linux內核中的這機制 | do_initcalls

文章出處:【微信號:嵌入式小生,微信公眾號:嵌入式小生】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    Linux內核自解壓過程分析

    uboot完成系統引導以后,執行環境變量bootm的命令;即,將Linux內核調入內存并調用do
    的頭像 發表于 12-08 14:00 ?876次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>自解壓過程<b class='flag-5'>分析</b>

    linux內核do_fork函數創建新進程

    前面已經談了內核加載與系統引導過程,下面我們來看看內核do_fork() 函數是如何創建一個新的進程的。
    發表于 08-06 08:40

    Linux內核源碼之我見——內核源碼的分析方法

    ——找不到main函數。對于簡單的demo程序,我們可以從頭至尾的分析代碼的含義,但是分析內核代碼這招就徹底失效了,因為沒有人能把Linux
    發表于 05-11 07:00

    簡單分析linux內核的結構體使用方法

    所謂linux驅動編程可以理解為linux內核的編程。既然在內核編程那就必須要符合內核邏輯和各
    發表于 01-19 08:26

    邏輯代數與邏輯函數

    邏輯代數與邏輯函數:本章主要討論分析和設計數字邏輯功能的數學。首先介紹邏輯代數
    發表于 09-01 09:11 ?0次下載

    Linux內核GPIO操作函數的詳解分析

    本文檔的主要內容詳細介紹的是Linux內核GPIO操作函數的詳解分析免費下載。
    發表于 01-22 16:58 ?28次下載

    Linux內核熱補丁安全隱患的探索

    修復的函數替換掉內核存在問題的函數從而達到修復目的。 函數替換的思想比較簡單,就是在執行
    的頭像 發表于 10-11 11:54 ?1713次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>熱補丁安全隱患的探索

    Linux內核系統調用概述及實現原理

    本文介紹了系統調用的一些實現細節。首先分析了系統調用的意義,它們與庫函數和應用程序接口(API)有怎樣的關系。然后,我們考察了Linux內核如何實現系統調用,以及
    的頭像 發表于 05-14 14:11 ?2186次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>系統調用概述及實現原理

    linux內核啟動過程會執行用戶空間的init進程

    linux內核啟動過程的后期,在kernel_init()函數代表的init線程,會嘗試執行用戶空間的init進程
    的頭像 發表于 10-14 09:12 ?1135次閱讀

    Linux內核模塊參數傳遞與sysfs文件系統

    Linux應用開發,為使應用程序更加靈活地執行用戶的預期功能,我們有時候會通過命令行傳遞一些參數到main函數,使得代碼
    發表于 06-07 16:23 ?2086次閱讀

    Linux內核SoftIrq源代碼分析

    我們在分析linux內核中斷剖析時,簡單的聊了一下SOFTIRQ, 而沒有進行深入分析. Linux內核
    發表于 06-23 15:22 ?549次閱讀

    萬千設備,linux內核如何知道?

    linux內核設備的注冊由device_register()函數完成,這個函數linux設備驅動模型的核心
    的頭像 發表于 07-12 08:52 ?806次閱讀
    萬千設備,<b class='flag-5'>linux</b><b class='flag-5'>內核</b>如何知道?

    Linux內核如何使用結構體和函數指針?

    我將結合具體的Linux內核驅動框架代碼來展示Linux內核如何使用結構體和函數指針。
    的頭像 發表于 09-06 14:17 ?996次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>如何使用結構體和<b class='flag-5'>函數</b>指針?

    Linux驅動是如何掛載的

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

    bootm命令的執行流程

    Bootm命令用來從memory啟動內核,bootm命令的執行流程如下圖所示。 在串口終端輸入bootm命令后,執行do_bootm函數來完
    的頭像 發表于 12-04 17:33 ?1111次閱讀
    bootm命令的<b class='flag-5'>執行</b>流程