首頁>Club>
10
回覆列表
  • 1 # 使用者5983173103434

    題主的大意就是主程式與中斷函式,或者多執行緒之間,同時讀寫訪問一個全域性變數,發生打架衝突該怎麼解決。

    問題的產生小例子

    在分析之前,我們先看看一個ARM程式小例子,主程式不停地對變數a加1計數,外部中斷如果發生,會對變數a清0,重新加1計數,具體C程式碼如下:

    對應的彙編程式碼:

    在這裡,整個主迴圈就在做一個簡單運算操作a=a+1,這個C語句會被編譯為3條彙編指令:

    這三條指令分別對應:指令1讀->指令2改->指令3寫,如果a初始值=100,那while主迴圈的流程如圖1所示。

    如果主迴圈在第2條指令執行完,第3條指令未執行的時候發生外部中斷,準備把a清零重新計數,那圖1的流程將會被打斷為圖2所示。

    然後就日了鬼了,中斷對變數a清零結束後,主程式接著執行指令3又將變數a置為101,這與我們的設計初衷嚴重違背,這就是題主所提到的問題:主程式與中斷程式同時訪問全域性變數打架啦,而且是主程式打敗了中斷程式,中斷清零完全沒有鳥用!

    其實,不僅指令2和3之間不能觸發中斷,指令1和指令2之間也存在類似的問題,所以為了保證系統正常工作,必須確保主迴圈中的a=a+1編譯後的彙編指令1,2和3絕對不能被打斷,這稱之為原子操作,就是不能再細分的最小操作環節。

    解決方案很簡單

    如果看明白了上面說的小例子,那解決辦法其實挺簡單的,就是對共用的全域性變數的操作,要想盡一切辦法不被打斷即可,也就是確保操作的原子性:

    CPU層面解決這個問題

    那就是直接設計單指令執行這個操作,比如發明這樣一條指令ADD1完成a=a+1操作:

    這個看起來,要CPU設計工程師,難度有點大。。。

    中斷層面解決這個問題

    中斷層面解決這個問題,就是操作前關閉總中斷,操作後,再開總中斷,保證這個操作永遠不被打斷。

    作業系統層面解決這個問題

    如果有作業系統,那解決的辦法就更方便了,可以中斷玩法,也可以用多執行緒間各種各樣的鎖機制,都可以搞定。

    所以,看場合了。

    如果這個操作非常關鍵而且通用的話,建議直接CPU層面支援,比如ARM的Bitband,如果就是簡單的前後臺系統,while+中斷,那就老老實實用中斷玩法,如果上了作業系統,可以用中斷,也可以用各種鎖去玩。

    volatile注意

    在宣告全域性變數a的時候,這裡用了關鍵詞volatile,如果一個變數會被多個執行緒任務或者中斷用到的話,務必要用volatile這個詞修飾,這意味著,編譯器對這個變數的操作不做任何緩衝和編譯最佳化,每次都是從變數原始地址載入進行讀寫操作。

    上面的小例子,如果去掉volatile,則會出現編譯器過度最佳化

    的問題:

    一般如下場景下要加volatile:

    1、中斷服務程式中修改的供其它程式檢測的變數需要加volatile;

    2、多工環境下各任務間共享的標誌或變數應該加volatile;

    3、儲存器對映的硬體暫存器通常也要加volatile說明,因為每次對它的讀寫都可能有不同意義;

  • 中秋節和大豐收的關聯?
  • 如何將數碼相機照的黑白照片變成彩?