在VxWorks中实现基于消息队列的C/S通信主要用到了Wind内核中的消息队列API,msgQLib。
这个库里面的发送消息和接受消息的API分别为:
STATUS msgQSend(MSG_Q_ID msgQId, char * buffer, UNIT nBytes, int timeout, int priority) // 当有任务正在等待某个消息的到来,则被发送的消息立即提交给第一个等待的任务;否则,消息插入消息队列,当消息队列满时,任务被阻塞 int msgQReceive(MSG_Q_ID msgQId, char * buffer, UNIT maxNBytes, int timeout) // 当有消息在消息队列上,第一个消息被接受;否则调用者被阻塞,进入目标队列的任务队列排队(基于优先级/FIFO)
程序设计很简单,但是在设计程序的时候还要明确:Wind内核默认采用基于优先级的抢占式调度(Priority-based preemptive scheduling)。
什么意思呢,系统中每个任务都拥有一个优先级,任意时刻,内核将CPU分给处于就绪态的优先级最高的任务运行,这是“基于优先级的”;一旦内核发现有一个比当前正在运行的任务优先级高的任务就绪,内核立即保存当前任务的上下文,切换到这个高优先级任务的上下文中运行,这是“抢占”,在接下去的程序修改客户端和服务端的优先级导致的运行结果不一致,可以非常直观地看出这一调度策略的运行。
但是这种任务调度策略有缺点啊。假如现在有多个相同优先级的任务要共享一台处理器时,如果某个正在执行的任务永远不被阻塞,那么这个任务将一直独占处理器(大家优先级一致,谁也无法抢占),这样其他相同优先级的任务就没有机会得到执行。为了解决这个缺陷,wind内核还采用轮转调度来配合基于抢占式的优先级调度。就是为了让优先级相同的(优先级高的仍然可以抢占轮转中的优先级低的任务)、处于就绪态的任务公平地共享CPU,VxWorks主要通过调用kernelTimeSlice()来实现轮转调度,这里不再赘述。
然后我们可以编写代码:
#include "vxWorks.h" #include "taskLib.h" #include "msgQLib.h" #include "sysLib.h" #include "stdio.h" #define CLIENT_TASK_PRI 100 #define SERVER_TASK_PRI 101 #define TASK_STACK_SIZE 5000 #define MSG_NUM 3 LOCAL MSG_Q_ID requestQId; LOCAL MSG_Q_ID response1QId; LOCAL MSG_Q_ID response2QId; LOCAL int notDone; typedef struct msg { int tid; int what; }MSG; LOCAL STATUS serverTask(); LOCAL STATUS clientTask(int cid,int value); STATUS mMain() { notDone = 1; /* create request Queue */ if((requestQId = msgQCreate(MSG_NUM * 2 + 1, sizeof(MSG), MSG_Q_FIFO)) == NULL) { perror("Error on creating requestQ"); } /* create response Queue */ if((response1QId = msgQCreate(MSG_NUM + 1, sizeof(MSG), MSG_Q_FIFO)) == NULL) { perror("Error on creating responseQ1"); } if ((response2QId = msgQCreate(MSG_NUM + 1, sizeof(MSG), MSG_Q_FIFO)) == NULL) { perror("Error on creating responseQ2"); } /* spawn server task */ if(taskSpawn("tServerTask", SERVER_TASK_PRI, 0, TASK_STACK_SIZE, (FUNCPTR)serverTask,0,0,0,0,0,0,0,0,0,0) == ERROR) { perror("Error on spawning tServerTask"); return (ERROR); } /* spawn client task */ if(taskSpawn("tClientTask_1", CLIENT_TASK_PRI, 0, TASK_STACK_SIZE, (FUNCPTR)clientTask,1,100,0,0,0,0,0,0,0,0) == ERROR) { perror("Error on spawning tClientTask_1"); return (ERROR); } if(taskSpawn("tClientTask_2", CLIENT_TASK_PRI, 0, TASK_STACK_SIZE, (FUNCPTR)clientTask,2,100,0,0,0,0,0,0,0,0) == ERROR) { perror("Error on spawning tClientTask_2"); return (ERROR); } /* wait tasks*/ while(notDone) { taskDelay(sysClkRateGet()); } if(msgQDelete(requestQId) == ERROR || msgQDelete(response1QId) == ERROR || msgQDelete(response2QId) == ERROR) { perror("Error in deleting msgQ"); return (ERROR); } return (OK); } /* server Task */ STATUS serverTask() { int num = 0; MSG msg; printf("\n\nI'm server task, my task id = %d.\n",taskIdSelf()); /* read message from request Queue */ while(num++ < MSG_NUM * 2) { printf("-->Num of request Queue:%d\n", msgQNumMsgs(requestQId)); if((msgQReceive(requestQId, (char *)&msg, sizeof(MSG), WAIT_FOREVER)) == ERROR){ perror("serverTask Error on receiving the msg"); return (ERROR); } /* print and add 1 to msg.what */ else { printf("serverTask receive msg from %d, msg.what = %d\n", msg.tid, msg.what++); } /* response to the client */ switch(msg.tid) { case 1: if((msgQSend(response1QId, (char *)&msg, sizeof(MSG), WAIT_FOREVER, MSG_PRI_NORMAL)) == ERROR) { perror("Error in sending the message to responsQ1."); return(ERROR); } else { printf("serverTask sending to responsQ1 with value %d.\n", msg.what); } break; case 2: if((msgQSend(response2QId, (char *)&msg, sizeof(MSG), WAIT_FOREVER, MSG_PRI_NORMAL)) == ERROR) { perror("Error in sending the message to responsQ2."); return(ERROR); } else { printf("serverTask sending to responsQ2 with value %d.\n", msg.what); } break; default: perror("Error when response to the msg."); } } return (OK); } /* client Task */ STATUS clientTask(int cid,int value) { int num = 0; MSG msg; printf("I am client task%d, my task id = %d.\n", cid, taskIdSelf()); /* sending message to request Queue. */ while (num++ < MSG_NUM) { msg.tid = cid; msg.what = value; if((msgQSend(requestQId, (char *)&msg, sizeof(MSG), WAIT_FOREVER, MSG_PRI_NORMAL)) == ERROR) { perror("Error on sending the message."); return(ERROR); } else { printf("clientTask%d sending to requestQ with value%d.\n", msg.tid, msg.what); } } num = 0; /* receiving message from its response Queue own. */ switch(cid) { case 1: while(num++ < MSG_NUM) { if((msgQReceive(response1QId,(char *)&msg, sizeof(MSG), WAIT_FOREVER)) == ERROR) { perror("Error on receiving the message from respons1Q."); return (ERROR); } else { printf("clientTask%d receiving msg from respons1Q with value%d.\n", msg.tid, msg.what); } } break; case 2: while(num++ < MSG_NUM) { if((msgQReceive(response2QId,(char *)&msg, sizeof(MSG), WAIT_FOREVER)) == ERROR) { perror("Error on receiving the message from respons2Q."); return (ERROR); } else { printf("clientTask%d receiving msg from respons2Q with value%d.\n", msg.tid, msg.what); } } break; default: perror("Error when receive message from response Queue."); } notDone = 0; return(OK); }
在windview的上下文切换视图中查看客户端和服务端任务的运行情况:
明白了VxWorks的任务调度机制和消息队列API的执行过程,理解这两个结果也就不难了。