自動初始化機制是指初始化函數不需要被顯式調用,只需要在函數定義處通過宏定義的方式進行申明,就會在系統啟動過程中被執行。這篇文章就來探索一下其中的奧秘, 簡單理解其原理!
|知識點補充
__attribute__((section(x)))是GNU C的一個特色之一,它可以用于將變量或函數放置在指定的段中。例如,你可以使用__attribute__((section(".my_section")))將變量或函數放置在名為my_section的段中。這對于嵌入式系統編程和操作系統內核編程非常有用。
__attribute__((used))是GCC編譯器提供的一個特性,用于告訴編譯器在目標文件中保留一個靜態變量或函數,即使它沒有被引用。這樣可以避免鏈接器刪除未使用的節,或者確保某些特定的變量或函數被輸出。
__attribute__((unused))是GCC編譯器提供的一個特性,用于告訴編譯器某個變量或函數可能未被使用,從而避免編譯器產生未使用變量或函數的警告。在變量或函數前加上__attribute__((unused))即可使用該特性。
__attribute__((aligned(n)))是GCC編譯器提供的一個特性,用于設置變量、類型、函數的對齊方式。它的作用是告訴編譯器在分配內存空間時,要求以n個字節為邊界。
__attribute__((weak))是GCC編譯器提供的一個特性,用于聲明或定義一個弱符號(weak symbol)。弱符號是指在鏈接時,如果存在同名的強符號(strong symbol),則會被強符號覆蓋。
| 原理研究
深入研究了一下, 發現這樣使用宏真的很奇妙, 這里就簡單介紹一下原理:
export.h文件
#ifndef__EXPORT_H #define__EXPORT_H #defineEXPORT_USED__attribute__((used)) #defineEXPORT_SECTION(x)__attribute__((section(x))) typedefint(*export_init_fn_t)(void); #defineEXPORT_INIT_EXPORT(fn,level) EXPORT_USEDconstexport_init_fn_t__export_call_##fnEXPORT_SECTION(".export_call."level)=fn //板級初始化順序1 #defineEXPORT_BOARD_INIT(fn)EXPORT_INIT_EXPORT(fn,"1") //設備初始化順序3 #defineEXPORT_DEVICE_INIT(fn)EXPORT_INIT_EXPORT(fn,"2") //組件初始化順序4 #defineEXPORT_COMPONENT_INIT(fn)EXPORT_INIT_EXPORT(fn,"3") //環境初始化順序5 #defineEXPORT_ENV_INIT(fn)EXPORT_INIT_EXPORT(fn,"4") //APP初始化順序6 #defineEXPORT_APP_INIT(fn)EXPORT_INIT_EXPORT(fn,"5") voidexport_components_init(void); #endif
export.c文件
#include"export.h" #include"stdio.h" staticinttest_0_start(void) { return0; } EXPORT_INIT_EXPORT(test_0_start,"0"); staticinttest_0_0(void) { return0; } EXPORT_INIT_EXPORT(test_0_0,"0"); staticinttest_0_1(void) { return0; } EXPORT_INIT_EXPORT(test_0_1,"0"); staticinttest_0_end(void) { return0; } EXPORT_INIT_EXPORT(test_0_end,"0.end"); staticinttest_1_start(void) { return0; } EXPORT_INIT_EXPORT(test_1_start,"1"); staticinttest_1_0(void) { return0; } EXPORT_INIT_EXPORT(test_1_0,"1"); staticinttest_1_1(void) { return0; } EXPORT_INIT_EXPORT(test_1_1,"1"); staticinttest_1_end(void) { return0; } EXPORT_INIT_EXPORT(test_1_end,"1.end"); //自動初始化(在main函數調用) voidexport_components_init(void) { printf("pfn1:%p ",&__export_call_test_0_start); printf("pfn2:%p ",&__export_call_test_0_0); printf("pfn3:%p ",&__export_call_test_0_1); printf("pfn4:%p ",&__export_call_test_0_end); printf("pfn5:%p ",&__export_call_test_1_start); printf("pfn6:%p ",&__export_call_test_1_0); printf("pfn7:%p ",&__export_call_test_1_1); printf("pfn8:%p ",&__export_call_test_1_end); volatileconstexport_init_fn_t*pfn; for(pfn=&__export_call_test_0_start;pfn&__export_call_test_1_end;?pfn++) ????{ ????????printf("%p ",?pfn); ????????//?(*pfn)(); ????} }
結果輸出:
pfn1:08000c50 pfn2:08000c54 pfn3:08000c58 pfn4:08000c5c pfn5:08000c60 pfn6:08000c64 pfn7:08000c68 pfn8:08000c6c 08000c50 08000c54 08000c58 08000c5c 08000c60 08000c64 08000c68
過程分析:
這個測試代碼片段主要定義和使用了兩個段,每個段定義了開始和結束,并且在開始和結束間插入了若干個函數,通過觀察地址的變化會發現, 它們是按規律遞增的,就可以使用遍歷來調用指針指向的函數, 從而實現自動初始化外設的目的.
細節分析:
//定義一個函數指針 typedefint(*export_init_fn_t)(void); //宏定義 #defineEXPORT_INIT_EXPORT(fn,level) EXPORT_USEDconstexport_init_fn_t__export_call_##fnEXPORT_SECTION(".export_call."level)=fn //假設調用 EXPORT_INIT_EXPORT(test_1_0,"1"); //一頓操作后,內存就存在了一個export_init_fn_t__export_call_test_1_0存放在".export_call."的輸入段中,并指定其屬于第一級初始化段 //就可以通過指針調用指針指向的函數來調用指定的函數,實現自動化初始化
|EventOS的EXPORT
這個先待定, 后續有時間再移植,export需要參考elab,涉及到assertcommonexportlog,感興趣的讀者可以參考:
鏈接//gitee.com/event-os/eventos/tree/dev_df/examples/stm32g070
使用了export機制可以讓代碼變得更加簡潔,感興趣的讀者可以在理解原理后進行完善和優化.
審核編輯:湯梓紅
-
GCC
+關注
關注
0文章
110瀏覽量
25317 -
開源
+關注
關注
3文章
3645瀏覽量
43666 -
函數
+關注
關注
3文章
4377瀏覽量
64546 -
編譯器
+關注
關注
1文章
1659瀏覽量
50053 -
宏定義
+關注
關注
0文章
51瀏覽量
9206
原文標題:開源探索|EventOS之自動初始化
文章出處:【微信號:玩轉單片機,微信公眾號:玩轉單片機】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
RT-Thread自動初始化詳解

求藍牙協議棧初始化和調度機制資料?
USART初始化結構體詳解
RT-Thread自動初始化機制簡介
EasyFlash+ulog自動初始化的問題與解決辦法介紹
手機模塊初始化向導
ds1302時鐘芯片初始化,自動決定DS1302是否需要初始化程序
RT-Thread自動初始化機制

評論