一、前言
在學(xué)RT-Thread時(shí),經(jīng)常能聽(tīng)到這個(gè)詞:自動(dòng)初始化。用起來(lái)也非常容易,一個(gè)宏就解決了,但是原理是什么呢?官網(wǎng)文檔提及到了,
(他們的文檔在這里:https://www.rt-thread.org/document/site/programming-manual/basic/basic/#rt-thread_3),但是寫(xiě)的只是概念層面上的,看完后會(huì)使用但原理還是不太清楚。之前研究過(guò),今天把它總結(jié)下,寫(xiě)出來(lái)分享。
1.1、一般情況的初始化調(diào)用
一般情況下,系統(tǒng)中的初始化會(huì)這樣做,應(yīng)該再熟悉不過(guò)了:
1//偽代碼 2 3voidmain(void) 4{ 5uart_init(); 6led_init(); 7... 8 910while(1)11{12//func113//func214}15}
這樣的顯式調(diào)用初始化函數(shù),有時(shí)可能多達(dá) 十幾到幾十 個(gè),看起來(lái)非常非常繁雜。但是好像沒(méi)啥問(wèn)題,因?yàn)橐呀?jīng)看習(xí)慣了。
1.2 使用自動(dòng)初始化后
舉例一個(gè)自動(dòng)初始化的用法如下:
1//這是led.c文件23voidled_init(void)4{5//省略6}7INIT_APP_EXPORT(led_init)1//這是main.c文件2intmain(void)3{45}
這樣,使用一個(gè)宏,初始化函數(shù)就會(huì)被自動(dòng)初始化,不用在其他地方顯式調(diào)用 led_init() 。代碼瞬間清爽很多。
二、引入
當(dāng)然也不用擔(dān)心一個(gè)初始化必須在另一個(gè)初始化之前的問(wèn)題,因?yàn)檫@里有6個(gè)自動(dòng)初始化等級(jí)可供選擇。
我摳了一張RT-Thread官網(wǎng)文檔的圖,該圖是RT-Thread代碼的啟動(dòng)流程圖,該圖中的藍(lán)色方框部分就是自動(dòng)初始化的6個(gè)等級(jí)以及初始化的先后順序。從圖中可以看出這6部分的初始化是由函數(shù) rt_components_board_init() 與 rt_components_init() 完成的。
在一開(kāi)始的例子中, INIT_APP_EXPORT(led_init) 就位于最后一個(gè)方框的位置,屬于applications init functions。
那么其他等級(jí)分別對(duì)應(yīng)什么宏進(jìn)行初始化的?看下面的表格:
三、自動(dòng)初始化原理
3.1 6個(gè)自動(dòng)初始化宏的定義
查看源碼,這 6 個(gè)宏定義如下:( 不同的段:1 2 3 4 5 6 )
1/*boardinitroutineswillbecalledinboard_init()function*/ 2#defineINIT_BOARD_EXPORT(fn)INIT_EXPORT(fn,"1") 3 4/*pre/device/component/env/appinitroutineswillbecalledininit_thread*/ 5/*componentspre-initialization(puresoftwareinitilization)*/ 6#defineINIT_PREV_EXPORT(fn)INIT_EXPORT(fn,"2") 7/*deviceinitialization*/ 8#defineINIT_DEVICE_EXPORT(fn)INIT_EXPORT(fn,"3") 9/*componentsinitialization(dfs,lwip,...)*/10#defineINIT_COMPONENT_EXPORT(fn)INIT_EXPORT(fn,"4")11/*environmentinitialization(mountdisk,...)*/12#defineINIT_ENV_EXPORT(fn)INIT_EXPORT(fn,"5")13/*appliationinitialization(rtguiapplicationetc...)*/14#defineINIT_APP_EXPORT(fn)INIT_EXPORT(fn,"6")
INIT_EXPORT(fn, level) 表示這個(gè)函數(shù) fn 現(xiàn)在屬于哪個(gè)初始化 level 段, 由 SECTION(".rti_fn."level) 進(jìn)行定義。
1#defineINIT_EXPORT(fn,level)2RT_USEDconstinit_fn_t__rt_init_##fnSECTION(".rti_fn."level)=fn
而 SECTION(x) 是:
1#defineSECTION(x)__attribute__((section(x)))
__attribute__((section("name"))) :將作用的函數(shù)或數(shù)據(jù)放入指定名為"name"的輸入段中。(在不同的編譯器中實(shí)現(xiàn)的方式也有所不同。)
以上就是整個(gè)的宏定義作用就是將函數(shù) fn 的地址賦給一個(gè) __rt_init_fn 的指針,然后放入相應(yīng) level 的數(shù)據(jù)段中。所以函數(shù)使用自動(dòng)初始化宏導(dǎo)出后,這些數(shù)據(jù)段中就會(huì)存儲(chǔ)指向各個(gè)初始化函數(shù)的指針。
舉例:INIT_APP_EXPORT(pin_beep_sample);
1//函數(shù)pin_beep_sample(),使用INIT_APP_EXPORT()進(jìn)行自動(dòng)初始化。 2 3INIT_APP_EXPORT(pin_beep_sample); 4=INIT_EXPORT(pin_beep_sample,"6") 5=constinit_fn_t__rt_init_pin_beep_sampleSECTION(".rti_fn.""6")=pin_beep_sample 6 7/* 8表示把函數(shù)pin_beep_sample的地址賦值給常量函數(shù)指針__rt_init_pin_beep_sample, 9然后放入名稱(chēng)為".rti_fn.6"的數(shù)據(jù)段中。10(其中init_fn_t是一個(gè)函數(shù)指針類(lèi)型,原型為typedef int (*init_fn_t)(void)。)11*/
表示把函數(shù) pin_beep_sample 的地址賦值給常量函數(shù)指針 __rt_init_pin_beep_sample,然后放入名稱(chēng)為 ".rti_fn.6" 的數(shù)據(jù)段中。( 其中 init_fn_t 是一個(gè)函數(shù)指針類(lèi)型,原型為 typedef int (*init_fn_t)(void)。)
注意被自動(dòng)初始化的函數(shù)類(lèi)型為:int (*init_fn_t)(void) ,無(wú)參,int 返回。
3.2 自動(dòng)初始化過(guò)程
那么上面提到,在啟動(dòng)流程中,調(diào)用了兩個(gè)函數(shù) rt_components_board_init() 與 rt_components_init() 就完成了6部分的初始化。從啟動(dòng)流程圖中可以看出:rt_components_board_init() 完成了第 1 段, rt_components_init() 完成了第2 到第6 段。
說(shuō)明:rt_components_board_init()主要board板級(jí)的初始化,調(diào)度器還未啟動(dòng),是在系統(tǒng)起來(lái)之前做的初始化。所以在使用board級(jí)別的初始化時(shí)不要使用系統(tǒng)API,如rt_thread_delay()等。rt_components_init()主要是一些組件的初始化及應(yīng)用初始化,是在main線程中完成的,當(dāng)調(diào)度器啟動(dòng)之后,系統(tǒng)啟動(dòng)開(kāi)始運(yùn)行main線程時(shí)才會(huì)進(jìn)行的初始化。是線程的運(yùn)行環(huán)境。
3.2.1、兩個(gè)函數(shù)的實(shí)現(xiàn)
1、第一個(gè)函數(shù) rt_components_board_init() 的實(shí)現(xiàn):
1voidrt_components_board_init(void) 2{ 3constinit_fn_t*fn_ptr; 4 5for(fn_ptr=&__rt_init_rti_board_start;fn_ptr&__rt_init_rti_board_end;?fn_ptr++) 6????{ 7????????(*fn_ptr)(); 8????} 9#endif10}
非調(diào)試模式下rt_components_board_init():for循環(huán)會(huì)遍歷位于__rt_init_rti_board_start 到 __rt_init_rti_board_end 之間保存的函數(shù)指針,然后依次執(zhí)行這些函數(shù)。
2、第二個(gè)函數(shù) rt_components_init() 的實(shí)現(xiàn):
1voidrt_components_init(void) 2{ 3constinit_fn_t*fn_ptr; 4 5for(fn_ptr=&__rt_init_rti_board_end;fn_ptr&__rt_init_rti_end;?fn_ptr?++) 6????{ 7????????(*fn_ptr)(); 8????} 9#endif10}
非調(diào)試模式下rt_components_init():for循環(huán)會(huì)遍歷位于__rt_init_rti_board_end 到 __rt_init_rti_end 之間保存的函數(shù)指針,然后依次執(zhí)行這些函數(shù) 。
那么 __rt_init_rti_board_start、__rt_init_rti_board_end、__rt_init_rti_end 是啥?
3.2.2 劃分
在系統(tǒng)中,定義了這幾個(gè)空函數(shù):rti_start、rti_board_start、rti_board_end、rti_end。不同的段:0、 0.end 、 1.end 、6.end
1staticintrti_start(void) 2{ 3return0; 4} 5INIT_EXPORT(rti_start,"0"); 6 7staticintrti_board_start(void) 8{ 9return0;10}11INIT_EXPORT(rti_board_start,"0.end");1213staticintrti_board_end(void)14{15return0;16}17INIT_EXPORT(rti_board_end,"1.end");1819staticintrti_end(void)20{21return0;22}23INIT_EXPORT(rti_end,"6.end");
這幾個(gè)函數(shù)的導(dǎo)出,加上上面 6 個(gè)初始化宏的導(dǎo)出,就有了這樣一個(gè)表格:
可以看出,這4個(gè)空函數(shù)所導(dǎo)出的段中間,包含著這6個(gè)初始化宏定義的段,而這6個(gè)段中分別包含著各自宏導(dǎo)出函數(shù)時(shí)的函數(shù)指針。
rt_components_board_init() 完成了第 1 段, rt_components_init() 完成了第2 到第6 段。
1、rt_components_board_init() 完成了第 1 段,也就是初始化了由INIT_BOARD_EXPORT(fn) 的初始化的所有函數(shù),也就是__rt_init_rti_board_start 到 __rt_init_rti_board_end 之間的函數(shù)指針。
2、rt_components_init() 完成了第2 到第6 段,也就是按順序初始化了由 INIT_PREV_EXPORT(fn) 到 INIT_DEVICE_EXPORT(fn) 到 INIT_COMPONENT_EXPORT(fn)、 INIT_ENV_EXPORT(fn)、 INIT_APP_EXPORT(fn)初始化的所有函數(shù),也就是從 __rt_init_rti_board_end 到 __rt_init_rti_end 之間的函數(shù)指針。
所以,當(dāng)你使用自動(dòng)初始化導(dǎo)出宏 去初始化一個(gè)函數(shù)時(shí),是由系統(tǒng)中的這兩個(gè)函數(shù)進(jìn)行遍歷函數(shù)指針執(zhí)行的。
3.2.3、示例
還是上面 INIT_APP_EXPORT(pin_beep_sample); 的例子。
舉例:INIT_APP_EXPORT(pin_beep_sample);
1//函數(shù)pin_beep_sample(),使用INIT_APP_EXPORT()進(jìn)行自動(dòng)初始化。 2 3INIT_APP_EXPORT(pin_beep_sample); 4=INIT_EXPORT(pin_beep_sample,"6") 5=constinit_fn_t__rt_init_pin_beep_sampleSECTION(".rti_fn.""6")=pin_beep_sample 6 7/* 8表示把函數(shù)pin_beep_sample的地址賦值給常量函數(shù)指針__rt_init_pin_beep_sample, 9然后放入名稱(chēng)為".rti_fn.6"的數(shù)據(jù)段中。10(其中init_fn_t是一個(gè)函數(shù)指針類(lèi)型,原型為typedef int (*init_fn_t)(void)。)11*/
表示把函數(shù) pin_beep_sample 的地址賦值給常量函數(shù)指針 __rt_init_pin_beep_sample,然后放入名稱(chēng)為 ".rti_fn.6" 的數(shù)據(jù)段中。( 其中 init_fn_t 是一個(gè)函數(shù)指針類(lèi)型,原型為 typedef int (*init_fn_t)(void)。)
在編譯后的.map文件中可以查看到:
常量函數(shù)指針 __rt_init_pin_beep_sample 位于 .rti_fn.6 段中。
-
初始化
+關(guān)注
關(guān)注
0文章
50瀏覽量
11953 -
RT-Thread
+關(guān)注
關(guān)注
31文章
1305瀏覽量
40386
原文標(biāo)題:RT-Thread 自動(dòng)初始化詳解
文章出處:【微信號(hào):RTThread,微信公眾號(hào):RTThread物聯(lián)網(wǎng)操作系統(tǒng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論