久久ER99热精品一区二区-久久精品99国产精品日本-久久精品免费一区二区三区-久久综合九色综合欧美狠狠

博客專欄

EEPW首頁 > 博客 > 這段代碼不講武德,勸你耗子尾汁

這段代碼不講武德,勸你耗子尾汁

發(fā)布人:魚鷹談單片機 時間:2021-01-06 來源:工程師 發(fā)布文章

動態(tài)數組???

不知道你是否聽說過 C99 有一個動態(tài)數組的特性,也就是說,數組大小可以根據需要動態(tài)的變化。

我們都知道,在 C89 模式下,數組的聲明只能是這樣:

4.png

但到了 C99,數組的大小可以用變量代替,根據需要變化:

3.png

有些人為了嘗鮮或者為了使用方便,可能會在程序中寫上類似的代碼。

一般情況下,代碼運行很正常,沒有一點問題。

但在運行時間需要嚴格控制的情況下,這段代碼就不講武(碼)德了。

防出去了昂

我們都知道,嵌入式系統(tǒng)的一大好處就是運行時間可控,是實時的系統(tǒng),所以它可以做一些高實時性的工作,比如控制、采樣等。

就拿采樣來說,一般都會要求采樣率,比如 100Hz、10Hz,換算到時間單位,就是需要 10 毫秒、100毫秒讀取一次數據,這個數據可能是內部寄存器(比如ADC),也可能是外部的器件通過 I2C、SPI等總線獲取,而一般來說,這些總線的通信時間是穩(wěn)定的、可控的。

但是有一天,你發(fā)現你的 SPI 驅動程序運行時間變得不再可控,它有時50us 完成一次數據的采集,有時需要 1 ms才完成,總之沒有規(guī)律可循,唯一的規(guī)律就是,當系統(tǒng)全面開始工作時,這個時間大部分在 1 ms 以上,只有很少幾次是幾十微秒就完成了執(zhí)行。

在 10 Hz 采樣率下,1 ms 誤差也不算太大,但當在 100 Hz采樣時,時間誤差就是 10%,不可忽略

你仔仔細細的查看了實現代碼,發(fā)現就是簡單的 SPI 通信,基本上都是判斷、賦值操作,還有就是使用循環(huán)等待標志位(使用硬件 SPI),。

(或許你會懷疑循環(huán)等等標志代碼導致了時間的不確定,但我的第一直覺告訴我不是它,因為 SPI 的通信時間是可控的(只要器件正常,從機一定會返回數據),STM32F1 系列的硬件 SPI 通信魚鷹也用了五六年,不應該有問題才對)

這些我全部防出去了昂(甚至魚鷹都考慮到線程執(zhí)行可能受到中斷的影響,特地在問題代碼執(zhí)行期間禁止了中斷)。

沒辦法,我只能停停,放下源碼本身的分析,拿出了殺手锏:《KEIL 下如何準確測量代碼執(zhí)行時間?》開始對問題代碼進行時間測量。

有備而來

經過幾番測量,很快昂,定位到類似下面的代碼:

2.png

發(fā)現竟然在52到57行之間花費了大量時間。就一些局部變量的定義,唯一和傳統(tǒng)寫法不同的是使用了動態(tài)數組,怎么會花費這么多時間?

按照傳統(tǒng)寫法,這里應該使用固定大小的數組。

我大意了啊,沒有閃,當時移植這份代碼的時候就留意到了這個另類寫法,當時還特地看了一下實現,但最終還是栽在了這里。                                                   

這段代碼是亂打(寫)的嗎?他可不是亂打(寫)的,格式清晰、移植方便、還有各種異常處理,明顯有備而來。

來、騙,來、偷襲我這經驗豐富的老同志。

這好嗎?這不好,我勸他耗子尾汁。

尋根問底

事實上,如果對時間要求不是很高的話,這段代碼不會有任何問題,它的基本讀取功能是沒有任何問題的,只是說它的執(zhí)行時間很不穩(wěn)定,有的時候幾十微秒就可以執(zhí)行完畢,有時候可能需要幾毫秒時間,還有極端的可能是直接死機(Hardfault)!

那么動態(tài)數組是如何實現的,或者說它的本質是什么呢?

本質就是使用 malloc 函數申請堆空間(在 rt-thread 中又會調用 rt_malloc),然后在離開函數前使用 free 函數釋放堆空間。

可以查看匯編確認:

1.png5.png

看到這里,你也就知道為什么會出現之前的現象了吧。

系統(tǒng)未完全運行前,很少有線程申請堆空間,所以執(zhí)行時間比較穩(wěn)定,因為它能快速的找到合適的內存塊,一旦系統(tǒng)里所有線程正式工作了,涉及到大量的內存申請與釋放,有大量的內存碎片,也就不容易找到合適的內存,這樣執(zhí)行時間也就不穩(wěn)定了,這對于實時要求高的功能是一個災難。

所以,如果你的功能不要求實時性的話,使用動態(tài)數組是可以的,一旦你的功能要求實時性,那么使用靜態(tài)數組才是更好的選擇(如果使用靜態(tài)數組,一定要注意使用范圍,最好加上斷言機制),如果代碼是在中斷執(zhí)行,rt-thread系統(tǒng)中,則必須使用靜態(tài)數組,否則 rt_malloc 無法正常執(zhí)行(斷言失敗)。

你防住了嗎?

*博客內容為網友個人發(fā)布,僅代表博主個人觀點,如有侵權請聯系工作人員刪除。



關鍵詞:

相關推薦

技術專區(qū)

關閉