Wind核心中有二進位訊號量、計數訊號量和互斥訊號量三種類型,為了使應用程式具有可移植性,還提供了POSIX(可移植作業系統介面)訊號量。在Vxorks作業系統中,訊號量是實現任務同步的主要手段,也是解決任務同步問題的最佳選擇。
關於互斥的實現:
使用二進位訊號量可以很方便的實現互斥,互斥是指多任務在訪問臨界資源時具有排他性。為使多個任務互斥訪問臨界資源,只需要為該資源設定一個訊號量,相當於一個令牌,哪個任務拿到這個令牌即有權使用該資源。把訊號量設為可用,然後將需要資源的任務的臨界代碼置於semTake()和SemGive()之間即可。
註明:
- 互斥中的訊號量與任務優先順序的關係:任務的調度還是按照任務優先順序進行,但是在使用臨界資源的時候只有一個任務獲得訊號量,也就是說還是按照任務優先順序來獲得訊號量從而訪問資源。只有當前使用資源的任務釋放訊號量SemGive(),其他任務按照優先順序獲得訊號量。
- 訊號量屬性中的參數為:SEM_Q_PRIORITY。而且在建立訊號量的時候必須把訊號量置為滿的SEM_FULL。即訊號量可用。
- 註:訊號量屬性指定任務阻塞在訊號量後的各任務排隊的隊列類型 (sem_q_priority 按照優先順序排隊 或者sem_q_fifo 按照先來先服務的順序排隊) ,sem_delete_safe安全刪除屬性,允許任務優先順序安全倒置sem_inversion_safe
基本實現互斥模型:
SEM_ID semMutex;
semMutex = semBCreate(SEM_Q_PRIORITY, SEM_FULL);
task(void)
{
semTake(semMutex,WAIT_FOREVER);//得到訊號量,即相當於得到使用資源的令牌
//臨界區,某一個時刻只能由一個任務訪問;
semGive(semMutex);
}
關於任務同步的實現:
同步即任務按照一定順序先後執行,為了實現任務A和B的同步,只需要讓任務A和B共用一個訊號量,並設初始值為空白,即不可用,將semGive()置於任務A之後,而在任務B之前插入semTake()即可.
說明:
- 還是討論和優先順序的關係。由於訊號量初始化為空白,不可用,所以可能使得優先順序反轉,即高優先順序任務B在等待低優先順序任務A釋放訊號量。只有執行了訊號量釋放語句semGive()後任務B得到訊號量才能執行。
- 屬性參數的設定為SEM_Q_FIFO,SEM_EMPTY;
實現模式參考:
SEM_ID semSync;
semSync = semBCreate(SEM_Q_FIFO,SEM_EMPTY);
taskA(void)
{
……….
semGive(semSync); //訊號量釋放,有效。
}
taskB(void)
{
semTake(semSync,WAIT_FOREVER); //等待訊號量。
……..
}
使用訊號量注意事項:
- 用途不同,訊號量屬性和初始值不同;
- 互斥訪問資源使,semTake()和semGive()必須成對出現,且先後順序不能顛倒;
- 避免刪除那些其他任務正在請求的訊號量。
應用:
1、 確保任務優先順序不反轉:
SEM_ID semFs;
SEM_ID semFss;
SEM_ID semFex;
semFs = semBCreate (SEM_Q_FIFO , SEM_EMPTY);
semFss = semBCreate (SEM_Q_FIFO , SEM_EMPTY);
semFex = semBCreate (SEM_Q_FIFO , SEM_EMPTY); //建立三個訊號量
void t_imaGet(void)
{
printf("a ";
semGive(semFs); //釋放訊號量
}
void t_imaJud(void)
{
semTake(semFs,WAIT_FOREVER); //確保優先順序不反轉。
printf("jj ";
semGive(semFss);
}
void t_imaPro(void)
{
semTake(semFss,WAIT_FOREVER);
printf("rr";
semGive(semFex);
}
void t_imaExc(void)
{
semTake(semFex,WAIT_FOREVER);
printf("Y";
}
void start(void)
{
int tGetId,tJudId,tProId,tExcId;
tGetId = taskSpawn("tPget",200,0,1000,(FUNCPTR)t_imaGet,1,0,0,0,0,0,0,0,0,0);
tJudId = taskSpawn("tPjud",201,0,1000,(FUNCPTR)t_imaJud,3,0,0,0,0,0,0,0,0,0);
tProId = taskSpawn("tPpro",202,0,1000,(FUNCPTR)t_imaPro,3,0,0,0,0,0,0,0,0,0);
tExcId = taskSpawn("tPexc",203,0,1000,(FUNCPTR)t_imaExc,3,0,0,0,0,0,0,0,0,0);
}
以上例子雖然定了各個任務的優先順序,但加上訊號量可以實現同步,而且防止優先順序反轉出現。
2、 也可以實現反轉。有興趣的可以將以上優先順序改一下試試看。