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