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的机制,以后会从原理上来讲解这些机制

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