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