VxWorks任務間同步的機制以及通信方式
概念普及:
- 資源刪除保護:進程A在拿到一個資源的信號量以後,進程A被刪除,這時資源將不能被訪問,因此需要對資源進行保護;
- 優先級反轉:有三個進程A,B,C。並且進程的優先級A>B>C,這時A在等待一個C掌握的資源,這時A因爲等待資源而阻塞,C執行,但是C的優先級小於B,因此B開始執行。這時A爲了等待資源,會等待B釋放資源以後,C獲取執行權,C釋放資源;
- 死鎖 :A任務和B任務分別佔有a資源和b資源,並且A想要獲取b資源,B想要獲取A資源,這種情況就會發生死鎖;
1、共享內存+信息同步實現通信
- 中斷鎖(直接關閉中斷,禁止任務調度和接收外部事件);
- 優先級鎖。禁止任務調度,但是會響應中斷;
- 信號量
- 二值信號量
- 互斥信號量
- 計數信號量
PS:二值信號量和互斥信號量都是隻有兩種狀態,區別在於互斥信號量用於單個任務對共享資源的鎖定,二值信號量類似於一個標誌位,在兩個任務之間負責動作的同步。例如:
void TaskA(){
get_resource();
發送二值信號量;
}
void TaskB(){
等待二值信號量;
use_resource();
}
- 信號(兼容linux中的信號),當任務接受到一個來自於外界的信號,任務處理函數會停止下來去執行信號處理函數(這點很像單片機中的硬件中斷)
2、使用Vxworks提供的任務通信機制
- 消息隊列
- 管道
- 網絡Socket
3、任務交互實例
(1)二值信號量
SEM_ID semId1; //創建一個信號量ID
void test_task(void) //新任務測試
{
int i=0;
semBTake(semId1,WAIT_FOREVER);
printf("task A take sem\n");
for(;i<100;i++){
num++;
taskDelay(1);
}
}
void test_task1(void) //新任務測試
{
int i =0;
printf("task B take sem\n");
for(;i<3;i++){
printf("i have reveived action\n");
}
semBGive(semId1);
printf("B: i have reveived action\n");
}
//二值信號量測試,任務A製造資源,這裏是把數加到100,任務B等待A的操作。
int main()
{
semId1 = semBCreate (SEM_Q_PRIORITY, SEM_EMPTY); //創建信號量,第一個參數是設置優先級信號量,也可設置成FIFO的,具體查看頭文件介紹。第二個參數是設置信號量的初始值。
taskid = taskSpawn("tDemo",TASK_PR,VX_NO_STACK_FILL,4000,(FUNCPTR)test_task,0,0,0,0,0,0,0,0,0,0);
taskid = taskSpawn("tDemo2",TASK_PR,VX_NO_STACK_FILL,4000,(FUNCPTR)test_task1,0,0,0,0,0,0,0,0,0,0);
if(semId1 == NULL){
printf("error for create sem\n");
}
while (1)
{
taskDelay (sysClkRateGet()); /* wait here until done */
}
return 0;
}
(2)互斥信號量
先作做一個不對共享資源保護的實驗:
void test_task3(void) //新任務測試,二元信號量測試
{
int i=0;
printf("task A \n");
for(;i<100;i++){
num++;
taskDelay(1);
printf("num=%d\n",num);
}
}
void test_task4(void) //新任務測試,二元信號量測試
{
int i=0;
printf("task B \n");
for(;i<100;i++){
num++;
taskDelay(1);
}
}
int main()
{
semId1 = semBCreate (SEM_Q_PRIORITY, SEM_EMPTY); //創建信號量
//taskid = taskSpawn("tDemo",TASK_PR,VX_NO_STACK_FILL,4000,(FUNCPTR)test_task,0,0,0,0,0,0,0,0,0,0);
//taskid = taskSpawn("tDemo2",TASK_PR,VX_NO_STACK_FILL,4000,(FUNCPTR)test_task1,0,0,0,0,0,0,0,0,0,0);
taskid = taskSpawn("tDemo3",TASK_PR,VX_NO_STACK_FILL,4000,(FUNCPTR)test_task3,0,0,0,0,0,0,0,0,0,0);
taskid = taskSpawn("tDemo4",91,VX_NO_STACK_FILL,4000,(FUNCPTR)test_task4,0,0,0,0,0,0,0,0,0,0);
if(semId1 == NULL){
printf("error for create sem\n");
}
while (1)
{
taskDelay (sysClkRateGet()); /* wait here until done */
}
return 0;
}
可以看到,來兩個任務會交叉修改數字,有時候這不是我們想要的效果。
void test_task3(void) //新任務測試,二元信號量測試
{
int i=0;
printf("task A \n");
semMTake(semId1,WAIT_FOREVER);
for(;i<100;i++){
num++;
taskDelay(1);
printf("num=%d\n",num);
}
semMGive(semId1);
}
void test_task4(void) //新任務測試,二元信號量測試
{
int i=0;
printf("task B \n");
semMTake(semId1,WAIT_FOREVER);
for(;i<100;i++){
num++;
taskDelay(1);
}
semMGive(semId1);
}
int main()
{
//semId1 = semBCreate (SEM_Q_PRIORITY, SEM_EMPTY); //創建信號量
semId1 = semMCreate(SEM_Q_PRIORITY|SEM_DELETE_SAFE); //優先級 | 安全刪除
//taskid = taskSpawn("tDemo",TASK_PR,VX_NO_STACK_FILL,4000,(FUNCPTR)test_task,0,0,0,0,0,0,0,0,0,0);
//taskid = taskSpawn("tDemo2",TASK_PR,VX_NO_STACK_FILL,4000,(FUNCPTR)test_task1,0,0,0,0,0,0,0,0,0,0);
taskid = taskSpawn("tDemo3",TASK_PR,VX_NO_STACK_FILL,4000,(FUNCPTR)test_task3,0,0,0,0,0,0,0,0,0,0);
taskid = taskSpawn("tDemo4",91,VX_NO_STACK_FILL,4000,(FUNCPTR)test_task4,0,0,0,0,0,0,0,0,0,0);
if(semId1 == NULL){
printf("error for create sem\n");
}
while (1)
{
taskDelay (sysClkRateGet()); /* wait here until done */
}
return 0;
}
加入互斥信號量以後,問題解決。
3、消息隊列
void test_task5(void) //新任務測試,消息填充任務
{
int i=0;
NODE_STRUCT *buf=(NODE_STRUCT *)malloc(sizeof(NODE_STRUCT));
buf->a=0;
buf->b=0;
for(;i<30;i++){
buf->a = i;
buf->b = 2*i;
msgQSend(msgQueueId,buf,sizeof(NODE_STRUCT),WAIT_FOREVER,0);
printf("send Qmsg num = %d\n",i);
}
}
void test_task6(void) //新任務測試,消息讀取
{
int i=0;
NODE_STRUCT *buf=(NODE_STRUCT *)malloc(sizeof(NODE_STRUCT));
for(;i<15;i++){
msgQReceive(msgQueueId,buf,sizeof(NODE_STRUCT),WAIT_FOREVER);
printf("receive msg num = %d a = %d b=%d\n",i,buf->a,buf->b);
taskDelay(1);
}
}
int main()
{
msgQueueId = msgQCreate(25,sizeof(NODE_STRUCT),MSG_Q_FIFO); //隊列長隊20,消息長度,屬性爲先進先出
taskid = taskSpawn("tDemo5",TASK_PR,VX_NO_STACK_FILL,4000,(FUNCPTR)test_task5,0,0,0,0,0,0,0,0,0,0);
taskid = taskSpawn("tDemo6",91,VX_NO_STACK_FILL,4000,(FUNCPTR)test_task6,0,0,0,0,0,0,0,0,0,0);
if(semId1 == NULL){
printf("error for create sem\n");
}
while (1)
{
taskDelay (sysClkRateGet()); /* wait here until done */
}
return 0;
}
這段代碼中會存在一個BUG,就是兩個任務同時使用了標準輸出資源,這是會發生資源競爭,但是這裏只是爲了說明消息隊列,因此這裏只是提一下。
下面是實驗結果:
可以看出,a任務首先進行消息隊列的填充,當消息隊列的長隊大於創建時定義的最大消息隊列長隊,任務就會被阻塞。這個時候,任務B取得執行權,開始讀取隊列中的數據。這個時候,A任務發現隊列非滿,就會在時間片平衡的條件下,和B任務輪流執行。當所有消息都發送完成以後,A任務退出。這時就剩下B任務。B任務讀取完成以後,結束。
Vx中常用的任務交互機制先介紹到這裏,這裏是從使用角度來驗證VX的機制,以後會從原理上來講解這些機制