日B视频 亚洲,啪啪啪网站一区二区,91色情精品久久,日日噜狠狠色综合久,超碰人妻少妇97在线,999青青视频,亚洲一区二卡,让本一区二区视频,日韩网站推荐

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

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

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

嵌入式2---在單片機(jī)里實(shí)現(xiàn)module_init機(jī)制

jf_49463572 ? 來源:27熊熊嵌入式 ? 作者:27熊熊嵌入式 ? 2026-05-04 11:24 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

嵌入式2---在單片機(jī)里實(shí)現(xiàn)module_init機(jī)制

很多朋友在寫單片機(jī)程序時(shí),常會遇到這樣的問題:所有模塊的初始化函數(shù)(比如LED初始化、串口初始化、傳感器初始化),都要手動(dòng)在main函數(shù)里一一調(diào)用,不僅代碼混亂、維護(hù)麻煩,而且新增或刪除模塊時(shí),還要修改main函數(shù),違背了“高內(nèi)聚、低耦合”的原則。

其實(shí)在Linux系統(tǒng)中,module_init機(jī)制的核心思想也是一樣的,Linux內(nèi)核本身就是高度模塊化的設(shè)計(jì)——驅(qū)動(dòng)開發(fā)者只需通過module_init宏注冊驅(qū)動(dòng)初始化函數(shù),無需修改內(nèi)核核心代碼,內(nèi)核啟動(dòng)時(shí)會自動(dòng)遍歷所有注冊的初始化函數(shù),完成驅(qū)動(dòng)加載;當(dāng)模塊卸載時(shí),通過module_exit宏注冊的退出函數(shù)會被自動(dòng)調(diào)用,這也是Linux驅(qū)動(dòng)“熱插拔”特性的基礎(chǔ)之一。

wKgZPGn1bo6AJ-NQAADtuhYLDSQ802.png ?

module_init機(jī)制,簡單說就是「自動(dòng)注冊、統(tǒng)一初始化」—— 每個(gè)模塊的初始化函數(shù),通過宏定義“注冊”到系統(tǒng)中,程序啟動(dòng)后,自動(dòng)遍歷所有注冊的初始化函數(shù)并執(zhí)行。

可能有朋友會說:“單片機(jī)沒有操作系統(tǒng)(比如Linux)里的module_init宏,怎么實(shí)現(xiàn)?” 其實(shí)原理很簡單,核心就是利用「編譯器特性」和「函數(shù)指針數(shù)組」,手動(dòng)模擬出這一機(jī)制。今天就以STM32為例來介紹如何實(shí)現(xiàn)這一功能;

我們先分析一下linux 中module_init宏

定位到Linux內(nèi)核源碼中的include/linux/init.h,可以看到有如下代碼:

#ifndef MODULE// 省略#definemodule_init(x) __initcall(x);// 省略#else
#definemodule_init(initfn)  intinit_module(void) __attribute__((alias(#initfn)));// 省略#endif

第一種情況:靜態(tài)編譯(#ifndefMODULE)。當(dāng)MODULE未定義時(shí),module_init(x)被宏定義為__initcall(x)。__initcall是Linux內(nèi)核中用于標(biāo)記靜態(tài)初始化函數(shù)的宏,被該宏標(biāo)記的函數(shù)會被放入內(nèi)核的.initcall段中。其核心源碼同樣位于include/linux/init.h中,相關(guān)定義如下(只粘貼了相關(guān)的內(nèi)容):

//...typedefint(*initcall_t)(void);//...#define__initcall(fn) device_initcall(fn)//...#definedevice_initcall(fn)   __define_initcall(fn, 6)//...#define__initcall(fn)  staticinitcall_t __initcall_##fn __used   __attribute__((__section__(".initcall.init"), __cold__)) = fn;//...#definedevice_initcall(fn)   __define_initcall(fn, 6)// ...#define__define_initcall(fn, id)  staticinitcall_t __initcall_##fn##id __used   __attribute__((__section__(".initcall"#id ".init"), __cold__)) = fn;

源碼解析:__initcall宏的核心作用,是通過編譯器__attribute__((__section__))指令,將初始化函數(shù)fn放入內(nèi)核的.initcall相關(guān)段中(不同優(yōu)先級對應(yīng)不同子段)。其中__used屬性確保函數(shù)不被編譯器優(yōu)化刪除,__cold__屬性標(biāo)記該函數(shù)為冷函數(shù)(僅啟動(dòng)時(shí)調(diào)用,優(yōu)化編譯)。被該宏標(biāo)記的函數(shù),會被內(nèi)核啟動(dòng)流程自動(dòng)遍歷執(zhí)行,從而完成靜態(tài)模塊的初始化。這種方式下,模塊會被直接編譯到內(nèi)核鏡像中,內(nèi)核啟動(dòng)時(shí)就完成初始化,無法動(dòng)態(tài)卸載。

而內(nèi)核啟動(dòng)時(shí),正是通過do_initcalls函數(shù)來遍歷并執(zhí)行所有注冊的靜態(tài)初始化函數(shù),該函數(shù)源碼位于內(nèi)核init/main.c中,核心實(shí)現(xiàn)如下:

staticvoid__initdo_initcalls(void){ intlevel;
 for(level =0; level < ARRAY_SIZE(initcall_levels) -?1; level++)    do_initcall_level(level);}

函數(shù)解析:do_initcalls函數(shù)是靜態(tài)初始化的“總?cè)肟凇?,其核心邏輯是?/span>優(yōu)先級順序遍歷所有初始化層級。其中:

  • initcall_levels是一個(gè)數(shù)組,存儲了前文提到的不同優(yōu)先級.initcall段(如.initcall0.init、.initcall1.init等)的起始地址,對應(yīng)pure_initcall、core_initcall等不同優(yōu)先級的初始化宏。

  • ARRAY_SIZE(initcall_levels) - 1用于獲取優(yōu)先級層級總數(shù),避免越界。

  • do_initcall_level(level)是底層執(zhí)行函數(shù),其核心實(shí)現(xiàn)(簡化版,貼合內(nèi)核源碼邏輯)如下,傳入優(yōu)先級level后,會遍歷該優(yōu)先級下所有注冊的初始化函數(shù)并依次調(diào)用,確保高優(yōu)先級的初始化函數(shù)(如內(nèi)核核心模塊)先執(zhí)行,低優(yōu)先級函數(shù)(如設(shè)備驅(qū)動(dòng))后執(zhí)行:

staticvoid__initdo_initcall_level(intlevel){ // 省略:參數(shù)校驗(yàn)、打印調(diào)試信息等冗余代碼  initcall_t *fn;// 定義函數(shù)指針,用于遍歷當(dāng)前優(yōu)先級的初始化函數(shù)
 // 遍歷當(dāng)前優(yōu)先級level對應(yīng)的.initcall段:從當(dāng)前層級起始地址,到下一層級起始地址 for(fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)    do_one_initcall(*fn);// 調(diào)用單個(gè)初始化函數(shù),完成該函數(shù)的執(zhí)行}

函數(shù)解析:do_initcall_leveldo_initcalls函數(shù)的底層執(zhí)行載體,專門負(fù)責(zé)處理單個(gè)優(yōu)先級層級的初始化,核心邏輯拆解如下:

  • initcall_t *fn定義與前文一致的初始化函數(shù)指針,用于遍歷當(dāng)前優(yōu)先級下的所有初始化函數(shù)。

  • initcall_levels[level]獲取當(dāng)前優(yōu)先級level對應(yīng)的.initcall段起始地址(比如level=1對應(yīng).core_initcall宏注冊的函數(shù)所在段起始);initcall_levels[level+1]則是下一個(gè)優(yōu)先級層級的起始地址,兩者構(gòu)成當(dāng)前優(yōu)先級的地址范圍,確保只遍歷當(dāng)前層級的函數(shù),不跨層級執(zhí)行。

  • do_one_initcall(*fn)真正執(zhí)行單個(gè)初始化函數(shù)的接口,傳入當(dāng)前遍歷到的函數(shù)指針(*fn即具體的初始化函數(shù)),完成該模塊的初始化;該函數(shù)內(nèi)部會做函數(shù)合法性校驗(yàn)、執(zhí)行狀態(tài)判斷等,確保初始化過程穩(wěn)定。

do_initcalls函數(shù)的層級遍歷,高優(yōu)先級先執(zhí)行,滿足內(nèi)核啟動(dòng)時(shí)“先核心、后外設(shè)”的初始化邏輯。

第二種情況:動(dòng)態(tài)加載(#else分支)。當(dāng)MODULE被定義時(shí),module_init(initfn)會通過__attribute__((alias(#initfn)))定義一個(gè)別名函數(shù)init_module,該別名指向我們自己編寫的初始化函數(shù)initfn。此時(shí)模塊會被編譯為.ko(內(nèi)核模塊)文件,后續(xù)可通過insmod命令動(dòng)態(tài)加載到內(nèi)核中——內(nèi)核加載模塊時(shí),會自動(dòng)調(diào)用init_module函數(shù)(即我們的初始化函數(shù));卸載模塊時(shí),通過rmmod命令調(diào)用module_exit注冊的退出函數(shù),實(shí)現(xiàn)模塊的熱插拔,這也是Linux驅(qū)動(dòng)動(dòng)態(tài)開發(fā)的核心方式。

1.機(jī)制原理

module_init機(jī)制的核心,本質(zhì)是把所有模塊的初始化函數(shù)地址,存到一個(gè)數(shù)組里,程序啟動(dòng)后,循環(huán)調(diào)用這個(gè)數(shù)組里的所有函數(shù)。

用到兩個(gè)關(guān)鍵知識點(diǎn)

  1. 函數(shù)指針:可以理解為“存放函數(shù)地址的變量”,通過函數(shù)指針,我們可以間接調(diào)用函數(shù)(比如void (*init_func)(void); 就是一個(gè)指向“無參數(shù)、無返回值”初始化函數(shù)的指針)。

  2. 編譯器section特性:我們可以通過編譯器指令,將所有注冊的初始化函數(shù)指針,集中存放在指定的內(nèi)存區(qū)域(section),后續(xù)只需找到這個(gè)區(qū)域的起始和結(jié)束地址,就能遍歷所有函數(shù)。

流程:模塊注冊 → 函數(shù)指針存入指定section → 程序啟動(dòng) → 遍歷section中的函數(shù)指針 → 依次調(diào)用(完成所有模塊初始化)。

下面我們講實(shí)現(xiàn)方式:

我使用的是keil

我把我的代碼貼出來

.h文件

typedefint(*initcall_t)(void);#define__init__attribute__((unused))#definemodule_init(fn) constinitcall_t __initcall_##fn __attribute__((section(".__initcall.0.b"), used)) = fn
.c文件
staticconstinitcall_t__initcall_sentinel_start  __attribute__((used,section(".__initcall.0.a"))) =0;
staticconstinitcall_t__initcall_sentinel_end  __attribute__((used,section(".__initcall.0.c"))) =0;

voidmodule_init_call(void){
constinitcall_t*call_start = &__initcall_sentinel_start; constinitcall_t*call_end = &__initcall_sentinel_end;while(call_start < call_end){if(*call_start)(*call_start)();call_start++;}}

在初始化函數(shù)下面添加 module_init宏如:

int test_init(void){return0;}module_init(test_init);

編譯之后我們在.map文件里可以看到

wKgZPGn1bo6ACjmDAATYOUBAzQA904.png

初始化函數(shù)被放在的相應(yīng)的區(qū)域里面

module_init_call函數(shù)放在了Reset_Handler,每次上電啟動(dòng)的時(shí)候就會調(diào)用

Reset_Handler  PROC        EXPORT Reset_Handler      [WEAK]   IMPORT SystemInit   IMPORT __mainIMPORT module_init_call        LDR  R0, =0xE000ED88  ; 使能浮點(diǎn)運(yùn)算 CP10,CP11        LDR  R1,[R0]        ORR  R1,R1,#(0xF <20)        STR  R1,[R0]        LDR  R0, =SystemInit        BLX  R0LDR  R0, =module_init_call        BLX  R0        LDR  R0, =__main        BX   R0        ENDP
大家可以根據(jù)不同的優(yōu)先級設(shè)置多個(gè)數(shù)組,跟linux一個(gè)區(qū)遍歷整個(gè)區(qū)域去做初始化參考鏈接:https://blog.csdn.net/weixin_37571125/article/details/78665184

https://zhuanlan.zhihu.com/p/615272622

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

    關(guān)注

    6078

    文章

    45591

    瀏覽量

    673960
  • 嵌入式
    +關(guān)注

    關(guān)注

    5210

    文章

    20680

    瀏覽量

    337362
  • Linux
    +關(guān)注

    關(guān)注

    88

    文章

    11822

    瀏覽量

    219600
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點(diǎn)推薦

    linux內(nèi)核使用鏈接腳本模仿module_init機(jī)制實(shí)戰(zhàn)

    編寫過設(shè)備驅(qū)動(dòng)就會經(jīng)常碰到module_init這個(gè)宏來定義驅(qū)動(dòng)入口函數(shù)。這個(gè)宏定義了一個(gè)函數(shù)指針指向我們的驅(qū)動(dòng)入口函數(shù),等到上電的時(shí)候就將這些一個(gè)個(gè)的函數(shù)指針拿出來調(diào)用,那么各個(gè)驅(qū)動(dòng)得到加載。特別
    發(fā)表于 06-16 10:42 ?1249次閱讀
    linux內(nèi)核使用鏈接腳本模仿<b class='flag-5'>module_init</b><b class='flag-5'>機(jī)制</b>實(shí)戰(zhàn)

    單片機(jī)嵌入式Internet技術(shù)的Web應(yīng)用實(shí)現(xiàn)

    精簡TCP/IP協(xié)議棧的實(shí)現(xiàn),對數(shù)據(jù)的逐層打包、封幀、傳送等流程[2];二是單片機(jī)Web應(yīng)用服務(wù)的實(shí)現(xiàn)。 由單片機(jī)組成的
    發(fā)表于 11-24 18:10

    什么是嵌入式單片機(jī)?嵌入式單片機(jī)詳情匯總

    嵌入式單片機(jī),即嵌入式微控制器,指以微控制器為核心控制單元的嵌入到對象體系中的專用計(jì)算機(jī)系統(tǒng),是應(yīng)用十分廣泛的一種嵌入式系統(tǒng)結(jié)構(gòu)。
    發(fā)表于 11-13 09:39 ?6873次閱讀

    詳解嵌入式Linux設(shè)備驅(qū)動(dòng)篇module_init

    linux就是這樣做的,對只需要初始化運(yùn)行一次的函數(shù)都加上__init屬性。kernel初始化后期,釋放所有這些函數(shù)代碼所占的內(nèi)存空間。它是怎么做到的呢?看過module_init
    的頭像 發(fā)表于 04-18 14:50 ?6093次閱讀

    linux驅(qū)動(dòng)的入口函數(shù)module_init的加載和釋放

    幾乎每個(gè)linux驅(qū)動(dòng)都有個(gè)module_init(與module_exit的定義Init.h (/include/linux) 中)。沒錯(cuò),驅(qū)動(dòng)的加載就靠它。為什么需要這樣一個(gè)宏?
    發(fā)表于 05-05 14:43 ?6154次閱讀
    linux驅(qū)動(dòng)的入口函數(shù)<b class='flag-5'>module_init</b>的加載和釋放

    單片機(jī)嵌入式的轉(zhuǎn)化

    提到單片機(jī)很多人都很覺得不陌生,大街小巷上面電子產(chǎn)品都用到。近幾年隨著嵌入式的發(fā)展,做單片機(jī)的一幫家伙突然覺得大禍臨頭一般發(fā)現(xiàn)自己熟悉掌握的單片機(jī)慢慢被
    發(fā)表于 09-22 10:13 ?1574次閱讀

    到底什么是嵌入式? 什么是單片機(jī)?

    到底什么是嵌入式?什么是單片機(jī)?
    的頭像 發(fā)表于 02-25 16:13 ?1.6w次閱讀

    怎么學(xué)習(xí)嵌入式單片機(jī)

    關(guān)于怎么學(xué)習(xí)嵌入式單片機(jī),我從自身學(xué)生經(jīng)歷、工作經(jīng)驗(yàn)和對于嵌入式單片機(jī)學(xué)習(xí)的建議三個(gè)方面回答:
    發(fā)表于 07-15 17:37 ?1483次閱讀

    單片機(jī)嵌入式區(qū)別

    單片機(jī)嵌入式芯片平臺片上資源價(jià)格應(yīng)用場景不同開發(fā)模式技術(shù)特征芯片平臺主流單片機(jī)平臺:51、PIC、STM32、AVR、MSP430等主流嵌入式平臺:ARM(最廣泛)、PPC(老美、歐
    發(fā)表于 10-20 14:21 ?4次下載
    <b class='flag-5'>單片機(jī)</b>與<b class='flag-5'>嵌入式</b>區(qū)別

    單片機(jī)or嵌入式linux

    最近很多童鞋投票并咨詢?nèi)绾螐?b class='flag-5'>單片機(jī)轉(zhuǎn)做嵌入式Linux開發(fā)。看來讀者圈中做單片機(jī),RTOS的不少。盡管我目前從事Linux/Android方面的嵌入式開發(fā)工作,但是讀書的時(shí)候也有5年左
    發(fā)表于 11-01 16:26 ?17次下載
    <b class='flag-5'>單片機(jī)</b>or<b class='flag-5'>嵌入式</b>linux

    單片機(jī)嵌入式的子類

    。嵌入式系統(tǒng)設(shè)計(jì)的第一步是結(jié)合具體的應(yīng)用,綜合考慮系統(tǒng)對成本、性能、可擴(kuò)展性、開發(fā)周期等各個(gè)方面的要求,確定系統(tǒng)的主控器件,并以之為核心搭建系統(tǒng)硬件平臺。2 硬件組成上的區(qū)別單片機(jī)
    發(fā)表于 11-04 09:06 ?14次下載
    <b class='flag-5'>單片機(jī)</b>是<b class='flag-5'>嵌入式</b>的子類

    單片機(jī)到底是不是嵌入式?

    01 問題很多同學(xué)一直糾結(jié):我是學(xué)單片機(jī)呢還是學(xué)嵌入式呢?還有人說單片機(jī)也是嵌入式,到底對不對?嵌入式
    發(fā)表于 11-04 11:21 ?14次下載
    <b class='flag-5'>單片機(jī)</b>到底是不是<b class='flag-5'>嵌入式</b>?

    單片機(jī)嵌入式的區(qū)別

    ,價(jià)格低,應(yīng)用領(lǐng)域大多為小家電,終端設(shè)備。 嵌入式片上資源豐富,價(jià)格高,應(yīng)用領(lǐng)域廣泛,基本可以適用于任何領(lǐng)域。開發(fā)模式 單片機(jī)一般都是裸機(jī)開發(fā),程序規(guī)模較小,只有比較高端的芯片上才會使用RTOS
    發(fā)表于 11-15 12:36 ?15次下載
    <b class='flag-5'>單片機(jī)</b>和<b class='flag-5'>嵌入式</b>的區(qū)別

    1.單片機(jī)嵌入式的關(guān)系(3)

    單片機(jī)嵌入式的關(guān)系什么是單片機(jī)?什么是嵌入式單片機(jī)嵌入式的核心差異
    發(fā)表于 12-01 15:51 ?16次下載
    1.<b class='flag-5'>單片機(jī)</b>和<b class='flag-5'>嵌入式</b>的關(guān)系(3)

    IAR 實(shí)現(xiàn)類linux驅(qū)動(dòng)模塊框架module_init(init_fun)

    其實(shí)在單片機(jī)上也能使用類linux驅(qū)動(dòng)模塊框架module_init(init_fun),從而給驅(qū)動(dòng)管理提供了新的方式。boot.icf文件/*###ICF### ...
    發(fā)表于 12-03 13:36 ?0次下載
    IAR <b class='flag-5'>實(shí)現(xiàn)</b>類linux驅(qū)動(dòng)模塊框架<b class='flag-5'>module_init</b>(<b class='flag-5'>init</b>_fun)
    六盘水市| 固安县| 原平市| 洛扎县| 通海县| 宁国市| 车险| 玛沁县| 济南市| 科尔| 徐汇区| 田东县| 洛南县| 界首市| 宜都市| 体育| 仙桃市| 尖扎县| 邢台市| 长治市| 丽水市| 桓台县| 吉安市| 浦江县| 嵊泗县| 广昌县| 涿鹿县| 朝阳区| 札达县| 江津市| 萍乡市| 文安县| 瑞丽市| 社旗县| 三河市| 荣成市| 康马县| 河西区| 胶南市| 玉溪市| 龙里县|