vxvorks學習3(任務同步)

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的機制,以後會從原理上來講解這些機制

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章