ucos中的三種臨界區管理機制

來源:互聯網
上載者:User

熟悉ucos,或者讀過Jean.J.Labrosse寫過的ucos書籍的人,一定會知道ucos中著名的臨界去管理宏:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。

同樣是通過關中斷來保護臨界區,OS_ENTER_CRITICAL/OS_EXIT_CRITICAL一共實現了三種實現方式,如下所示:

#if OS_CRITICAL_METHOD == 1#define OS_ENTER_CRITICAL() __asm__("cli")#define OS_EXIT_CRITICAL() __asm__("sti")#endif#if OS_CRITICAL_METHOD == 2#define OS_ENTER_CRITICAL() __asm__("pushf \n\t cli")#define OS_EXIT_CRITICAL() __asm__("popf")#endif#if OS_CRITICAL_METHOD == 3#define OS_ENTER_CRITICAL() (cpu_sr = OSCPUSaveSR())#define OS_EXIT_CRITICAL() (OSCPURestoreSR(cpu_sr))#endif

   第一種方式,OS_ENTER_CRITICAL()簡單地關中斷,OS_EXIT_CRITICAL()簡單地開中斷。這種方式雖然簡單高效,但無法滿足嵌套的情況。如果有兩層臨界區保護,在退出內層臨界區時就會開中斷,使外層的臨界區也失去保護。雖然ucos的核心寫的足夠好,沒有明顯嵌套臨界區的情況,但誰也無法保證一定沒有,無法保證今後沒有,無法保證在附加的驅動或什麼位置沒有,所以基本上第一種方法是沒有人用的。

   第二種方式,OS_ENTER_CRITICAL()會在關中斷前儲存之前的標誌寄存器內容到堆棧中,OS_EXIT_CRITICAL()從堆棧中恢複之前儲存的狀態。這樣就允許了臨界區嵌套的情況。但現在看來,這種方法還存在很大的問題,甚至會出現致命的漏洞。

      在OS_CRITICAL_METHOD=2的情況下,假設有如下代碼:

function_a(){     int a=(1<<31);     OS_ENTER_CRITICAL();     function_b(a);     OS_EXIT_CRITICAL();     }

   會出現什麼情況?在我的實驗中,OS_EXIT_CRITICAL()之後,會出現處理器異常。為什麼會出現處理起異常,讓我來類比一下它的彙編代碼。之所以是類比,並非是我虛構資料,而是因為我實際碰到問題的函數複雜一些,理解起來就需要更多的代碼。而這個問題是有普遍意義的,所以請允許我來淺顯地揭示這個隱藏的bug。

function_a:     push ebp     mov ebp, esp     sub esp, 8     mov 4(esp), 0x80000000     pushfd     cli     mov edi, 4(esp)     mov (esp), edi     call function_b    popfd    mov esp, ebp    ret

    這是參照了gcc編譯結果的彙編類比,無論是否加最佳化選項這一問題都存在。這個問題的起因很簡單,gcc想聰明一點,一次把堆棧降個夠,然後它就可以在棧上隨意放參數去調用其他函數。尤其是在調用函數較多的時候,這種做法就更有意義。而且,gcc這種聰明與最佳化選項O好像沒有太大關係,好像沒有什麼能禁止它這麼做。但問題是,gcc不知道我們的OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()是操作了堆棧的,我嘗試過使用__asm__ __volatile__("pushfd
\n\tcli":::"memory")來通知gcc記憶體資料改變了,但顯然gcc不認為堆棧也改變了。於是,OS_ENTER_CRITICAL()儲存在棧上的狀態就被衝掉了,比如被這裡調用參數a的值。在恢複時,是否會引發異常,會引發什麼異常,這個就要靠運氣了。但我相信一個人的運氣不會總是那麼好的,所以最後別使用OS_CRITICAL_METHOD=2。

    第三種,在關中斷前,使用局部變數儲存中斷狀態。這也是幾乎所有即時作業系統共有的選擇。但ucos是一朵奇葩,為了相容前兩種方式,OS_ENTER_CRITICAL()/ OS_EXIT_CRITICAL()宏定義並沒有提供傳遞狀態參數的功能。所以它的臨界去必須這麼用:

function_a(){#if OS_CRITICAL_METHOD == 3    int cpu_sr;#endif      int a = 1<<31;      OS_ENTER_CRITICAL();      function_b(a);      OS_EXIT_CRITICAL();}

這種代碼怎麼看怎麼彆扭,可能是因為在函數體內加了宏定義吧。然後,第三種方法對同一個函數體內的嵌套臨界區無法支援,這在一些很長大的函數中使用時或許會造成一定困擾。

    好吧,如果有了問題,就要有解決方案,畢竟我不是為了讓大家對ucos失去信心的。我們可以參考下一般的即時作業系統是如何?關中斷臨界區的,就是以顯式的方式用局部變數儲存中斷狀態。

int int_lock(){   int cpu_sr;    __asm__ __volatile__("pushfd \n\t pop %0\n\t cli":"=r"(cpu_sr));    return cpu_sr;}void int_unlock(int cpu_sr){     __asm__ __volatile__("push %0\n\t popfd"::"r"(cpu_sr));}function_a(){   int a, cpu_sr;   a=1<<31;   cpu_sr = int_lock();   function_b(a);   int_unlock(cpu_sr);}

   int_lock()和int_unlock()的可以用彙編更高效地實現,也可以選擇只恢複中斷標誌的狀態。這種方法讓我們顯示地管理狀態儲存的情況,我覺得至少要比宏定義清楚多了。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.