【原創】ucos信號量的操作及原理


信號量的操作及原理

 
1.OSSemCreate創建信號量semaphore
    在使用信號量之前,要先用 OSSemCreate 創建一個信號量,並通過返回的合法事件結構體指針使用信號量。
  1. OS_EVENT *OSSemCreate(INT16U cnt)
  2. {
  3. #if OS_CRITICAL_METHOD == 3 /* 原理請查看http://blog.csdn.net/liuhui_8989/article/details/8783323 */
  4. OS_CPU_SR cpu_sr;
  5. #endif
  6. OS_EVENT *pevent;
  7. if(OSIntNesting>0){/* 不能在中斷內創建信號量 */
  8. return((OS_EVENT *)0);/* 直接返回0 */
  9. }
  10. OS_ENTER_CRITICAL();
  11. pevent =OSEventFreeList;/* 獲取空閑的事件控制塊 */
  12. if(OSEventFreeList!=(OS_EVENT *)0){/* 將OSEventFreeList指向下一個事件控制塊 */
  13. OSEventFreeList=(OS_EVENT *)OSEventFreeList->OSEventPtr;
  14. }
  15. OS_EXIT_CRITICAL();
  16. if(pevent !=(OS_EVENT *)0){/* Get an event control block */
  17. pevent->OSEventType= OS_EVENT_TYPE_SEM;
  18. pevent->OSEventCnt= cnt;/* 設置計數器的初值 */
  19. pevent->OSEventPtr=(void*)0;/* Unlink from ECB free list */
  20. OS_EventWaitListInit(pevent);/* 初始化事件控制塊中任務等待表為0 */
  21. }
  22. return(pevent);
  23. }
    簡而言之,如果沒有了空閑的事件控制塊或者是在中斷內創建信號里,則返回無效的事件控制塊0;否則返回類型為信號量,任務等待表OSEventGrp和OSEventTbl[]為0,且已設置了計數器初值的事件控制塊指針。這樣便成功地創建了一個信號量。這里要注意,使用OSSemCreate函數返回的指針前,要檢驗是否為有效的指針。
    cnt的值至少為0。
    創建了信號量之后,便可以對信號量進行如下操作了,申請、釋放、刪除、查詢信號量。
 
2. 信號量的申請和釋放
  1. 申請:voidOSSemPend(OS_EVENT *pevent, INT16U timeout, INT8U *err)
  2. 釋放:INT8U OSSemPost(OS_EVENT *pevent)
     OSSemPend函數有三個參數,
     第一個是一個指向事件控制塊的指針,該值為 OSSemCreate返回值。
     第二個是一個等待時間值(至少為0),如果信號量目前被占用,則無法立即申請到信號量,調用該函數的任務將被掛起,如果等待時間值為0,則一直被掛起直到信號量被釋放為止( OSSemPost能夠在釋放信號量的同時,恢復等待信號量的任務 ),如果等待時間值大於0,則在超時時間過后,由OSTineTick恢復為就緒狀態的任務。
    第三個為一個指向錯誤代碼的指針,該值作為函數返回值使用。
     OS_NO_ERR                           函數調用成功,獲得了信號量。
    OS_TIMEOUT                         在規定的時間內沒有申請到信號量
     OS_ERR_EVENT_TYPE         事件類型錯誤,不是信號量
    OS_ERR_PEND_ISR               不能在中斷內申請信號量
    OS_ERR_PEVENT_NULL      pevent指針無效
 
下面講解一下函數內部原理:
無標題.png
 
    如果信號量的計數器值大於0,則將其減1,表示又有一個任務占用,並直接返回。
    如果信號量的計數器值為0,表示信號量已被其他任務占用,此時任務控制塊中的狀態標志是等待信號狀態以及就緒的,因為使用的是按位或操作,保留了原有的就緒狀態標志。
    之后調用了OS_EventTaskWait(pevent); 此時作了3件事:
    (1)OSTCBCur->OSTCBEventPtr = pevent;將事件控制塊指針保存於任務控制塊中
    (2)去除任務在任務就緒表的就緒狀態,注意沒有包括任務控制塊中的狀態標志
    (3)設置事件控制塊中的任務等待表
    至此任務被掛起!通過OS_Sched運行其他任務去了。
    接下來的結果取決於,信號量是否在規定的等待時間內被釋放。
    在當前任務被掛起,而運行其他任務的同時,每個時鍾節拍都會運行OSTimeTick中斷函數,此函數會遍歷所有任務,如果任務控制塊中的狀態標志為就緒的,且Dly等待值不為0,則將Dly減1,如果減1后剛好為0,則在任務就緒表中恢復該任務的就緒狀態!
    如果該就緒狀態的任務恢復運行,此時任務控制塊的狀態標志仍為OS_STAT_SEM,運行OS_EventTO,做的事剛好和 OS_EventTaskWait相反。
     (1)OSTCBCur->OSTCBEventPtr = 0;
    (2)設置任務控制塊中的狀態標志為就緒狀態(去除 OS_STAT_SEM狀態
    (3)去除事件控制塊中的任務等待表
    此時返回OS_TIMEOUT。
    但是如果在等待時間未過去,其他任務釋放了信號量, OSSemPost能夠在釋放信號量的同時,恢復等待信號量的任務。等待信號量的任務恢復運行,此時任務控制塊的狀態標志不包含 OS_STAT_SEM了,所以函數直接跳過第二個if語句,返回OS_NO_ERR。
 
釋放信號量過程:
    函數OSSemPost在對信號量的計數器操作之前,首先檢查任務等待表中是否還有其他等待該信號的任務,如果沒有,就把計數器加1,如果有,則調用OS_EventTaskRdy將任務等待表中最高優先級的任務設為就緒狀態,並調用OSSched調度任務。
 
3. 應用
3.1申請函數和釋放函數在同一任務中成對出現
main:
pevent = OSSemCreate(1);
task1:
OSSemPend(pevent, 0, err);
....
OSSemPost(pevent);
task2:
OSSemPend(pevent, 0, err);
....
OSSemPost(pevent);
當一個任務沒有釋放信號量,另一個任務在申請信號量時只能掛起直到信號量釋放。
3.2 應用程序中有一個函數Fun(),如果想使任務M必須經過Y任務允許才能調用函數一次,可以使用信號量
main:
pevent = OSSemCreate(0);
task1:
OSSemPend(pevent, 0, err);
Fun();
task2:
OSSemPost(pevent);
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM