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

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

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

一文詳解RT-Thread自動初始化

RTThread物聯(lián)網(wǎng)操作系統(tǒng) ? 來源:陳翠 ? 2019-07-21 10:17 ? 次閱讀

一、前言

在學RT-Thread時,經(jīng)常能聽到這個詞:自動初始化。用起來也非常容易,一個宏就解決了,但是原理是什么呢?官網(wǎng)文檔提及到了,

(他們的文檔在這里:https://www.rt-thread.org/document/site/programming-manual/basic/basic/#rt-thread_3),但是寫的只是概念層面上的,看完后會使用但原理還是不太清楚。之前研究過,今天把它總結(jié)下,寫出來分享。

1.1、一般情況的初始化調(diào)用

一般情況下,系統(tǒng)中的初始化會這樣做,應(yīng)該再熟悉不過了:

1//偽代碼 2 3voidmain(void) 4{ 5uart_init(); 6led_init(); 7... 8 910while(1)11{12//func113//func214}15}

這樣的顯式調(diào)用初始化函數(shù),有時可能多達 十幾到幾十 個,看起來非常非常繁雜。但是好像沒啥問題,因為已經(jīng)看習慣了。

1.2 使用自動初始化后

舉例一個自動初始化的用法如下:

1//這是led.c文件23voidled_init(void)4{5//省略6}7INIT_APP_EXPORT(led_init)1//這是main.c文件2intmain(void)3{45}

這樣,使用一個宏,初始化函數(shù)就會被自動初始化,不用在其他地方顯式調(diào)用 led_init() 。代碼瞬間清爽很多。

二、引入

當然也不用擔心一個初始化必須在另一個初始化之前的問題,因為這里有6個自動初始化等級可供選擇。

我摳了一張RT-Thread官網(wǎng)文檔的圖,該圖是RT-Thread代碼的啟動流程圖,該圖中的藍色方框部分就是自動初始化的6個等級以及初始化的先后順序。從圖中可以看出這6部分的初始化是由函數(shù) rt_components_board_init() 與 rt_components_init() 完成的。

在一開始的例子中, INIT_APP_EXPORT(led_init) 就位于最后一個方框的位置,屬于applications init functions。

那么其他等級分別對應(yīng)什么宏進行初始化的?看下面的表格:

三、自動初始化原理

3.1 6個自動初始化宏的定義

查看源碼,這 6 個宏定義如下:( 不同的段: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) 表示這個函數(shù) fn 現(xiàn)在屬于哪個初始化 level 段, 由 SECTION(".rti_fn."level) 進行定義。

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"的輸入段中。(在不同的編譯器中實現(xiàn)的方式也有所不同。)

以上就是整個的宏定義作用就是將函數(shù) fn 的地址賦給一個 __rt_init_fn 的指針,然后放入相應(yīng) level 的數(shù)據(jù)段中。所以函數(shù)使用自動初始化宏導(dǎo)出后,這些數(shù)據(jù)段中就會存儲指向各個初始化函數(shù)的指針。

舉例:INIT_APP_EXPORT(pin_beep_sample);

1//函數(shù)pin_beep_sample(),使用INIT_APP_EXPORT()進行自動初始化。 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然后放入名稱為".rti_fn.6"的數(shù)據(jù)段中。10(其中init_fn_t是一個函數(shù)指針類型,原型為typedef int (*init_fn_t)(void)。)11*/

表示把函數(shù) pin_beep_sample 的地址賦值給常量函數(shù)指針 __rt_init_pin_beep_sample,然后放入名稱為 ".rti_fn.6" 的數(shù)據(jù)段中。( 其中 init_fn_t 是一個函數(shù)指針類型,原型為 typedef int (*init_fn_t)(void)。)

注意被自動初始化的函數(shù)類型為:int (*init_fn_t)(void) ,無參,int 返回。

3.2 自動初始化過程

那么上面提到,在啟動流程中,調(diào)用了兩個函數(shù) rt_components_board_init() 與 rt_components_init() 就完成了6部分的初始化。從啟動流程圖中可以看出:rt_components_board_init() 完成了第 1 段, rt_components_init() 完成了第2 到第6 段。

說明:rt_components_board_init()主要board板級的初始化,調(diào)度器還未啟動,是在系統(tǒng)起來之前做的初始化。所以在使用board級別的初始化時不要使用系統(tǒng)API,如rt_thread_delay()等。rt_components_init()主要是一些組件的初始化及應(yīng)用初始化,是在main線程中完成的,當調(diào)度器啟動之后,系統(tǒng)啟動開始運行main線程時才會進行的初始化。是線程的運行環(huán)境。

3.2.1、兩個函數(shù)的實現(xiàn)

1、第一個函數(shù) rt_components_board_init() 的實現(xiàn):

1voidrt_components_board_init(void) 2{ 3constinit_fn_t*fn_ptr; 4 5for(fn_ptr=&__rt_init_rti_board_start;fn_ptr

非調(diào)試模式下rt_components_board_init():for循環(huán)會遍歷位于__rt_init_rti_board_start 到 __rt_init_rti_board_end 之間保存的函數(shù)指針,然后依次執(zhí)行這些函數(shù)。

2、第二個函數(shù) rt_components_init() 的實現(xiàn):

1voidrt_components_init(void) 2{ 3constinit_fn_t*fn_ptr; 4 5for(fn_ptr=&__rt_init_rti_board_end;fn_ptr

非調(diào)試模式下rt_components_init():for循環(huán)會遍歷位于__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)中,定義了這幾個空函數(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");

這幾個函數(shù)的導(dǎo)出,加上上面 6 個初始化宏的導(dǎo)出,就有了這樣一個表格:

可以看出,這4個空函數(shù)所導(dǎo)出的段中間,包含著這6個初始化宏定義的段,而這6個段中分別包含著各自宏導(dǎo)出函數(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ǎo)出宏 去初始化一個函數(shù)時,是由系統(tǒng)中的這兩個函數(shù)進行遍歷函數(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()進行自動初始化。 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然后放入名稱為".rti_fn.6"的數(shù)據(jù)段中。10(其中init_fn_t是一個函數(shù)指針類型,原型為typedef int (*init_fn_t)(void)。)11*/

表示把函數(shù) pin_beep_sample 的地址賦值給常量函數(shù)指針 __rt_init_pin_beep_sample,然后放入名稱為 ".rti_fn.6" 的數(shù)據(jù)段中。( 其中 init_fn_t 是一個函數(shù)指針類型,原型為 typedef int (*init_fn_t)(void)。)

在編譯后的.map文件中可以查看到:

常量函數(shù)指針 __rt_init_pin_beep_sample 位于 .rti_fn.6 段中。

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

    關(guān)注

    0

    文章

    49

    瀏覽量

    11814
  • RT-Thread
    +關(guān)注

    關(guān)注

    31

    文章

    1261

    瀏覽量

    39837

原文標題:RT-Thread 自動初始化詳解

文章出處:【微信號:RTThread,微信公眾號:RTThread物聯(lián)網(wǎng)操作系統(tǒng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    RT-Thread自動初始化詳解

    我們知道,在寫裸機程序時,當我們完成硬件初始化后,就需要在主函數(shù)中進行調(diào)用。當我們使用RT-Thread后,完全不需要這樣做了,我們可以將硬件等自動初始化
    的頭像 發(fā)表于 06-25 21:38 ?1.1w次閱讀
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>自動</b><b class='flag-5'>初始化</b><b class='flag-5'>詳解</b>

    RT-Thread qemu mps2-an385 bsp移植制作 :系統(tǒng)運行篇

    前面已經(jīng)讓 RT-Thread 進入了 entry 入口函數(shù),并且 調(diào)整 鏈接腳本,自動初始化與 MSH shell 的符號已經(jīng)預(yù)留, 進入了 RT-Thread
    的頭像 發(fā)表于 11-14 12:27 ?758次閱讀
    <b class='flag-5'>RT-Thread</b> qemu mps2-an385 bsp移植制作 :系統(tǒng)運行篇

    RT-thread初始化過程是怎樣進行的

    RT-thread初始化過程是怎樣進行的?擴展補丁Sub和super的作用是什么?如何去使用它們呢?
    發(fā)表于 11-29 07:42

    如何對RT-Thread系統(tǒng)進行初始化

    RT-Thread是如何啟動的?如何對RT-Thread系統(tǒng)進行初始化呢?
    發(fā)表于 11-30 07:54

    為什么RT-Thread要采用這種復(fù)雜的方式來進行自動初始化操作呢

    在分析之前首先查閱 RT-Thread 的官方文檔RT-Thread 自動初始化機制,根據(jù)官方文檔的講述在 RTT 源碼中共使用了 6 中
    發(fā)表于 04-06 17:49

    RT-Thread自動初始化機制簡介

    RT-Thread 的時鐘管理以時鐘節(jié)拍為基礎(chǔ),時鐘節(jié)拍是 RT-Thread 操作系統(tǒng)中最小的RT-Thread 自動初始化機制時鐘單位。
    發(fā)表于 04-06 18:08

    RT-Thread系統(tǒng)自動初始化機制簡介

    RT-Thread 自動初始化機制1、自動初始化機制簡介在系統(tǒng)啟動流程圖中,有兩個函數(shù):rt_c
    發(fā)表于 04-12 17:43

    RT-Thread系統(tǒng)初始化與啟動流程詳細描述

    時, 該函數(shù)將初始化系統(tǒng)的定時器線程。void rt_application_init( )創(chuàng)建用戶線程由此創(chuàng)建個用戶main()線程,而 main()函數(shù)是RT-Thread的用戶
    發(fā)表于 08-25 15:15

    RT-Thread自動初始化原理分析

    ;}這里我們直接就可以使用 printf 進行打印,而沒有進行些其它的初始化,參考這個思路引出了 RT-Thread自動初始化
    發(fā)表于 12-05 14:17

    RT-Thread學習筆記 --(3)RT-Thread自動初始化機制分析

    相信不少工程師在閱讀RT-Thread相關(guān)源代碼的時候,都會經(jīng)??吹饺缦聢D所示的宏定義,按照宏定義的命名來理解,這些宏定義似乎都是對些...
    發(fā)表于 01-25 18:55 ?1次下載
    <b class='flag-5'>RT-Thread</b>學習筆記 --(3)<b class='flag-5'>RT-Thread</b><b class='flag-5'>自動</b><b class='flag-5'>初始化</b>機制分析

    RT-Thread全球技術(shù)大會:如何使用組件以及自動初始化流程

    RT-Thread全球技術(shù)大會:如何使用組件和自動初始化流程 ? ? ? ? ? 審核編輯:彭靜
    的頭像 發(fā)表于 05-27 15:16 ?890次閱讀
    <b class='flag-5'>RT-Thread</b>全球技術(shù)大會:如何使用組件以及<b class='flag-5'>自動</b><b class='flag-5'>初始化</b>流程

    RT-Thread自動初始化機制

    ??在分析之前首先查閱 RT-Thread 的官方文檔 [RT-Thread 自動初始化機制](https://www.rt-thread.
    的頭像 發(fā)表于 06-17 08:52 ?2504次閱讀
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>自動</b><b class='flag-5'>初始化</b>機制

    rt-thread 優(yōu)化系列(六)啟動流程重構(gòu)

    去年此時,筆者剛接觸 rt-thread 的時候,被它的初始化過程深深折服了。第次打開rt-thread 的項目,竟然沒找到多線程在
    的頭像 發(fā)表于 07-04 15:30 ?1692次閱讀
    <b class='flag-5'>rt-thread</b> 優(yōu)化系列(六)啟動流程重構(gòu)

    rt-thread線程棧初始化參數(shù)分析

    RT-Thread 在線程初始化的代碼內(nèi)有初始化線程堆棧的代碼
    的頭像 發(fā)表于 08-14 16:50 ?1564次閱讀
    <b class='flag-5'>rt-thread</b>線程棧<b class='flag-5'>初始化</b>參數(shù)分析

    RT-Thread使用經(jīng)驗分享:鏈表未初始化造成死機

    最近在開發(fā)調(diào)試基于RT-Thread 的驅(qū)動時,遇到個比較奇怪的死機問題,后來經(jīng)過步步排查,終于發(fā)現(xiàn)是驅(qū)動的鏈表節(jié)點沒有初始化造成的死機
    的頭像 發(fā)表于 10-08 14:49 ?843次閱讀
    <b class='flag-5'>RT-Thread</b>使用經(jīng)驗分享:鏈表未<b class='flag-5'>初始化</b>造成死機