首頁>技術>

通常我們寫程式都是按照這個套路,一個函式一個函式按照順序邏輯一個一個的執行下去。

如果邏輯非常複雜,涉及的模組比較多,那麼這種順序執行的程式碼就會比較臃腫,各模組耦合非常緊密。Linux kernel 中,有各種外設驅動,想按照一個順序邏輯執行下去,幾乎是不可能的,而kenrel 程式碼能有這麼大的程式碼量,大而不亂,把各層次,各模組有效的分離,而大量的程式碼又有邏輯的組織在一起,和這個initcall 有至關重要的作用。

透過模仿這種方式,最後把圖片中main函式程式碼清空,分離這種邏輯,又實現同樣的功能。

如何能實現這樣的功能了,需要一些背景知識:

1,程式程式碼的組織

2,連結指令碼相關的知識。

3,函式指標的應用。

目前的程式編譯出來用到了這些個段,除了.isr_vector也是新增的,其他都是編譯器預設的。

先加段程式碼

當然這還不夠,還需要告訴聯結器(LD) 要把 .initcall 段也連結到程式中,所以也需要這段修改。

這段按8位元組對齊,定義兩個全域性變數,及按0-5順序的連結這些資料,這樣的兩處修改,再來看一下程式各段的情況。

如圖片

已經多出紅色框框為.initcalls段,這段總共是8個位元組,從0x80005a8除開始。

在來看一下具體的這一段的情況,用readelf 工具。

和上面的size工具是匹配的,而綠色框框的地址就是SystemInit(0x08000231,小端模式。)

所以透過attribute及修改連結指令碼,就把函式指標變數放到了.initcall 段中。

那麼如何來呼叫這個函數了,和之前的初始化data段資料類似,遍歷這個段,然後取出這個函式地址,然後強制把段中的地址,轉成函式指標,再直接呼叫即可。

實現的這張圖片,就是從.initcall段中取出函式地址,然後直接呼叫,非常容易把函式的地址及這個函式指標變數的地址搞混。

程式碼這麼修改,需要自動初始化函式的確是可以調到了,但是每次都寫這麼長長的一段static initcall_t __attribute__(( __used__,__section__(".initcall.0.init"))),就是不舒服. linux kernel中透過宏來修改。

這個也一樣。

新增 按照程式邏輯順序執行的一些宏

0,low_level_init 比如放始化系統基本時鐘

1,arch_init 比如放CPU架構d如初始化NVIC的一些初始化。

2,dev_init 外設模組初始化,比 i2c, flash, spi等。

3,board_init 做具體硬體板及的一些設定。

4,os_init 作業系統的一些設定如,檔案系統,網路協議棧等。

5,app_init 最後跑使用者程式。

把自己的程式也做一下修改,用宏代替。這樣子掉呼叫do_initcalls 就會按照0,1-到5的順序執行了。

最後在來看一下initcall 段,

這樣只要在需要自動初始化函式加上類似於dev_init(),app_init() 就可以了,就會自動呼叫到,而不需要main 函式中一個一個的順序執行。 比如i2c控制的初始化放到dev_init 中,下面掛了很多i2c的從裝置,只要分別給個從裝置用app_init 初始化就行,即使來了一個新的,也用這app_init初始化就行,也不需要更改原來的,高度的分離模組間的耦合度。

這樣模擬Linux kenerl 初始化驗證成功,最後上庫。

https://gitee.com/android_life/stm32_freertos_opensource/tree/master/bareos/initcall

14
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 平臺管理後臺與商家選單資源管理:商家角色管理設計