生活随笔
收集整理的這篇文章主要介紹了
ucos中的三种临界区管理机制
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
熟悉ucos,或者讀過Jean.J.Labrosse寫過的ucos書籍的人,一定會知道ucos中著名的臨界去管理宏:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。
同樣是通過關(guān)中斷來保護臨界區(qū),OS_ENTER_CRITICAL/OS_EXIT_CRITICAL一共實現(xiàn)了三種實現(xiàn)方式,如下所示:
[cpp]?view plaincopyprint?
#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()簡單地關(guān)中斷,OS_EXIT_CRITICAL()簡單地開中斷。這種方式雖然簡單高效,但無法滿足嵌套的情況。如果有兩層臨界區(qū)保護,在退出內(nèi)層臨界區(qū)時就會開中斷,使外層的臨界區(qū)也失去保護。雖然ucos的內(nèi)核寫的足夠好,沒有明顯嵌套臨界區(qū)的情況,但誰也無法保證一定沒有,無法保證今后沒有,無法保證在附加的驅(qū)動或什么位置沒有,所以基本上第一種方法是沒有人用的。
?? 第二種方式,OS_ENTER_CRITICAL()會在關(guān)中斷前保存之前的標志寄存器內(nèi)容到堆棧中,OS_EXIT_CRITICAL()從堆棧中恢復之前保存的狀態(tài)。這樣就允許了臨界區(qū)嵌套的情況。但現(xiàn)在看來,這種方法還存在很大的問題,甚至會出現(xiàn)致命的漏洞。
????? 在OS_CRITICAL_METHOD=2的情況下,假設(shè)有如下代碼:
[cpp]?view plaincopyprint?
function_a()?? {?? ?????int?a=(1<<31);?? ?????OS_ENTER_CRITICAL();?? ?????function_b(a);?? ?????OS_EXIT_CRITICAL();?? ??????? }??
?? 會出現(xiàn)什么情況?在我的實驗中,OS_EXIT_CRITICAL()之后,會出現(xiàn)處理器異常。為什么會出現(xiàn)處理起異常,讓我來模擬一下它的匯編代碼。之所以是模擬,并非是我虛構(gòu)數(shù)據(jù),而是因為我實際碰到問題的函數(shù)復雜一些,理解起來就需要更多的代碼。而這個問題是有普遍意義的,所以請允許我來淺顯地揭示這個隱藏的bug。
[cpp]?view plaincopyprint?
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編譯結(jié)果的匯編模擬,無論是否加優(yōu)化選項這一問題都存在。這個問題的起因很簡單,gcc想聰明一點,一次把堆棧降個夠,然后它就可以在棧上隨意放參數(shù)去調(diào)用其他函數(shù)。尤其是在調(diào)用函數(shù)較多的時候,這種做法就更有意義。而且,gcc這種聰明與優(yōu)化選項O好像沒有太大關(guān)系,好像沒有什么能禁止它這么做。但問題是,gcc不知道我們的OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()是操作了堆棧的,我嘗試過使用__asm__ __volatile__("pushfd \n\tcli":::"memory")來通知gcc內(nèi)存數(shù)據(jù)改變了,但顯然gcc不認為堆棧也改變了。于是,OS_ENTER_CRITICAL()保存在棧上的狀態(tài)就被沖掉了,比如被這里調(diào)用參數(shù)a的值。在恢復時,是否會引發(fā)異常,會引發(fā)什么異常,這個就要靠運氣了。但我相信一個人的運氣不會總是那么好的,所以最后別使用OS_CRITICAL_METHOD=2。
??? 第三種,在關(guān)中斷前,使用局部變量保存中斷狀態(tài)。這也是幾乎所有實時操作系統(tǒng)共有的選擇。但ucos是一朵奇葩,為了兼容前兩種方式,OS_ENTER_CRITICAL()/ OS_EXIT_CRITICAL()宏定義并沒有提供傳遞狀態(tài)參數(shù)的功能。所以它的臨界去必須這么用:
[cpp]?view plaincopyprint?
function_a()?? {?? #if?OS_CRITICAL_METHOD?==?3?? ????int?cpu_sr;?? #endif?? ??????int?a?=?1<<31;?? ??????OS_ENTER_CRITICAL();?? ??????function_b(a);?? ??????OS_EXIT_CRITICAL();?? }??
這種代碼怎么看怎么別扭,可能是因為在函數(shù)體內(nèi)加了宏定義吧。然后,第三種方法對同一個函數(shù)體內(nèi)的嵌套臨界區(qū)無法支持,這在一些很長大的函數(shù)中使用時或許會造成一定困擾。
??? 好吧,如果有了問題,就要有解決方案,畢竟我不是為了讓大家對ucos失去信心的。我們可以參考下一般的實時操作系統(tǒng)是如何實現(xiàn)關(guān)中斷臨界區(qū)的,就是以顯式的方式用局部變量保存中斷狀態(tài)。
[cpp]?view plaincopyprint?
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()的可以用匯編更高效地實現(xiàn),也可以選擇只恢復中斷標志的狀態(tài)。這種方法讓我們顯示地管理狀態(tài)保存的情況,我覺得至少要比宏定義清楚多了。
總結(jié)
以上是生活随笔為你收集整理的ucos中的三种临界区管理机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。