VxWorks5.5中,为了保证各个独立的任务可以协同工作,提供了一整套任务间的通信机制,主要包括信号量,共享内存,消息队列,管道,信号,事件。
当然除了上面这些机制,VxWorks5.5还提供了一些网络通信(socket),以及多CPU任务间的通信。
信号量
信号量主要用来实现任务互斥和同步。
#include "vxWorks.h"
#include "stdio.h"
#include "stdlib.h"
#include "semLib.h"
#include "taskLib.h"
#define NUM_SAMPLE (10)
#define DELAY_TICKS (8)
#define STACK_SIZE (20000)
typedef struct _LIST_NODE {
int data;
struct _LIST_NODE *pNextNode;
}LIST_NODE;
int tidCosmos;
int tidSchlep;
int tidCrunch;
int tidMonitor;
int cosmicData = 0;
int result = 0;
LIST_NODE *pCurrNode = NULL;
SEM_ID dataSemId;
SEM_ID syncSemId;
SEM_ID nodeListGuardSemId;
void cosmos(vodi);
void nodeAdd(int data);
void schlep(void);
void nodeScrap(void);
void crunch(void);
void monitor(void);
void progStop(void);
/***************/
STATUS progStart(void) {
syncSemId = semBCreate(SEM_Q_FIFO, SEM_EMPTY);
dataSemId = semBCreate(SEM_Q_FIFO, SEM_EMPTY);
nodeListGuardSemId = semMCreate(SEM_Q_FIFO | SEM_INVERSION_SAFE | SEM_DELETE_SAFE);
pCurrNode = NULL;
tidCosmos = taskSpawn("tCosmos", 200, 0, STACK_SIZE, (FUNCPTR)cosmos,0,0,0,0,0,0,0,0,0,0);
tidSchlep = taskSpawn("tSchlep", 220, 0, STACK_SIZE, (FUNCPTR)schlep,0,0,0,0,0,0,0,0,0,0);
tidCrunch = taskSpawn("tCrunch", 200, 0, STACK_SIZE, (FUNCPTR)crunch,0,0,0,0,0,0,0,0,0,0);
tidMonitor = taskSpawn("tMonitor", 200, 0, STACK_SIZE, (FUNCPTR)monitor,0,0,0,0,0,0,0,0,0,0);
return (OK);
}
/**************/
void cosmos(void) {
while(1) {
cosmicData = rand();
semGive(dataSemId);
taskDelay(DELAY_TICKS);
}
return;
}
/***************/
void nodeAdd(int data) {
LIST_NODE *node;
if((node = (LIST_NODE *)malloc(sizeof(LIST_NODE))) != NULL) {
node ->data = data;
semTake(nodeListGuardSemId, WAIT_FOREVER);
node->pNextNode = pCurrNode;
pCurrNode = node;
semGive(nodeListGuardSemId);
}
else {
printf("nodeAdd: out of Memory.\n");
taskSuspend(0);
}
return;
}
/**************/
void schlep(void) {
int i;
while(1) {
for(i = 0; i < NUM_SAMPLE; i++) {
semTake(dataSemId,WAIT_FOREVER);
nodeAdd(cosmicData);
}
semGive(syncSemId);
}
return;
}
/*************/
void nodeScrap(void) {
LIST_NODE *pTmpNode;
semTake(nodeListGuardSemId, WAIT_FOREVER);
if(pCurrNode != NULL) {
pTmpNode = pCurrNode;
pCurrNode = pCurrNode->pNextNode;
free(pTmpNode);
}
semGive(nodeListGuardSemId);
return;
}
/*****************/
void crunch(void) {
int sampleSum;
while(1) {
sampleSum = 0;
semTake(syncSemId, WAIT_FOREVER);
semTake(nodeListGuardSemId, WAIT_FOREVER);
while(pCurrNode != NULL) {
sampleSum += pCurrNode->data;
nodeScrap();
}
semGive(nodeListGuardSemId);
result = sampleSum;
}
return;
}
/****************/
void monitor(void) {
BOOL isHot = FALSE;
BOOL averge = 0;
averge = RAND_MAX*NUM_SAMPLE/2;
while(1) {
if ((!isHot) && (result >= averge)) {
isHot = TRUE;
printf("HOT\n");
}
else if(isHot && (result < averge))
{
isHot = FALSE;
printf("COOL\n");
}
}
return;
}
/*****************/
void progStop(void) {
result = semTake(nodeListGuardSemId, WAIT_FOREVER);
if(result == OK) {
taskDelete(tidCosmos);
taskDelete(tidSchlep);
taskDelete(tidCrunch);
taskDelete(tidMonitor);
while(pCurrNode != NULL) {
nodeScrap();
}
}
semDelete(dataSemId);
semDelete(syncSemId);
semDelete(nodeListGuardSemId);
printf("BYE!\n");
return;
}
共享内存
VxWorks5.5中是没有进程线程一说的,都是以任务的形式存在,如果非要做个比较,那任务应该是和操作系统中线程的概念是一致的,可以大致认为VxWorks操作系统启动了一个主线程,然后在这个主线程中创建其他线程(任务)。
因此,VxWorks5.5中的任务运行于同一个线性地址空间,各个任务之间可以直接通过共享内存实现信息共享和信息传递。共享内存的存在形式可以是全局变量的简单变量,数组,链表,环形缓冲以及指针,他们可以在不同的任务上下文中访问,如下图所示
需要注意的是,任务之间如果通过共享内存的方式共享和传递信息,常常需要额外的保护措施,如信号量互斥,中断上锁,任务抢占上锁等,否则会产生访问共享内存这段代码的可重入性问题。
消息队列
前面介绍的信号量可以实现同步和互斥机制,共享内存可以达到任务间的信息共享,但是都不能提供完善的响应式交互信息方式,VxWorks5.5提供了一种更为高级的机制,也是单CPU中VxWorks的任务间最主要的通信机制。
代码实例:
给出一个两个任务间通过消息队列进行单向通信的简单例子,然后给出一个基于客服端/服务器架构的多个任务间通信的例子。
#include "vxWorks.h"
#include "msgQlib.h"
#define MAX_MSGS (10)
#define MAX_MSG_LEN (100)
#define STACK_SIZE (20000)
#define DELAY_TICKS (50)
int tidTask1;
int tidTask2;
MSG_Q_ID myMsgQId;
STATUS porgStart(void);
STATUS task1(void);
STATUS task2(void);
void progStop(void);
/**************/
STATUS progStart(void) {
myMsgQId = msgQCreate(MAX_MSGS, MAX_MSG_LEN, MSG_Q_PRIORITY);
if(myMsgQId == NULL) {
return (ERROR);
}
tidTask1 = taskSpawn("tTask1", 200, 0, STACK_SIZE, (FUNCPTR)task1,0,0,0,0,0,0,0,0,0,0);
tidTask2 = taskSpawn("tTask2", 200, 0, STACK_SIZE, (FUNCPTR)task2,0,0,0,0,0,0,0,0,0,0);
return (OK);
}
/*************/
#define MESSAGE "Greetings from Task1"
STATUS task1(void) {
while(1) {
if(msgQSend(myMsgQId, MESSAGE, sizeof(MESSAGE), WAIT_FOREVER, MSG_PRI_NORMAL) == ERROR) {
return (ERROR);
}
taskDelay(DELAY_TICKS);
}
return (OK);
}
/***************/
STATUS task2(void) {
char msgBuf[MAX_MSG_LEN];
while(1) {
memset(msgBuf, 0, MAX_MSG_LEN);
if(msgQReceive(myMsgQId, msgBuf, MAX_MSG_LEN, WAIT_FOREVER) == ERROR) {
return (ERROR);
}
printf("Message from task1: %s\n",msgBuf);
}
return (OK);
}
/*****************/
void progStop(void) {
taskDelete(tidTask1);
taskDelete(tidTask2);
msgQDelete(myMsgQId);
printf("BYE!\n");
return;
}
基于客服端/服务器架构的多任务间通信
#include "vxWorks.h"
#include "msgQLib.h"
#define MAX_MSGS (10)
#define MAX_MSG_LEN sizeof(MESSAGE)
#define STACK_SIZE 20000
#define DELAY_TICKS 50
#define NUM_CLIENT 2
#define MID_CLIENT(id) (id)
#define MID_SERVER NUM_CLIENT
typedef struct _MESSAGE {
int mSendId;
int mRecvId;
int mData;
}MESSAGE;
int tidServer;
int tidClient[NUM_CLIENT];
MSG_Q_ID msgQIdServer;
MSG_Q_ID msgQIdClient[NUM_CLIENT];
STATUS progStart(void);
STATUS server(void);
STATUS client(int id);
void progStop(void);
STATUS progStart(void) {
int id;
msgQIdServer = msgQCreate(MAX_MSGS, MAX_MSG_LEN, MSG_Q_PRIORITY);
if(msgQIdServer == NULL) {
return (ERROR);
}
for (id = 0; id < NUM_CLIENT; id++) {
msgQIdClient[id] = magQCreate(MAX_MSGS, MAX_MSG_LEN, MSG_Q_PRIORITY);
if(msgQIdClient[id] == NULL) {
return (ERROR);
}
}
tidServer = taskSpawn("tServer", 220, 0, STACK_SIZE, (FUNCPTR)server, 0,0,0,0,0,0,0,0,0,0);
for(id = 0; id < NUM_CLIENT; id++) {
char tempName[20];
sprintf(tempName, "tClient%d", id);
tidClient[id] = taskSpawn(tempName, 200, 0, STACK_SIZE, (FUNCPTR)client, id,0,0,0,0,0,0,0,0,0,0);
}
return (OK);
}
STATUS server(void) {
MESSAGE rxMsg;
MESSAGE txMsg;
txMsg.mSendId = MID_SERVER;
while(1) {
if(msgQReceive(msgQIdServer, (char*)&rxMsg, MAX_MSG_LEN, WAIT_FOREVER) == ERROR) {
return (ERROR);
}
if (rxMsg.mRecvId != MID_SERVER) {
return (ERROR);
}
if((rxMsg.mSendId < MID_CLIENT(0)) || (rxMsg.mSendId > MID_CLIENT(NUM_CLIENT - 1))) {
return (ERROR);
}
printf("\nServer: receive a data %d from tClient %d\n", rxMsg.mData, rxMsg.mSendId);
txMsg.mRecvId = rxMsg.mSendId;
txMsg.mData = rxMsg.mData;
if(msgQSend(msgQIdClient[rxMsg.mSendId], (char*)&txMsg, sizeof(MESSAGE), WAIT_FOREVER, MSG_PRI_NORMAL) == ERROR) {
return (ERROR);
}
}
return (OK);
}
STATUS client(int id) {
MESSAGE rxMsg;
MESSAGE txMsg;
txMsg.mSendId = MID_CLIENT(id);
txMsg.mRecvId = MID_SERVER;
while(1) {
txMsg.mData = rand();
if(msgQSend(msgQIdServer, (char*)&txMsg, sizeof(MESSAGE), WAIT_FOREVER, MSG_PRI_NORMAL) == ERROR) {
return (ERROR);
}
printf("\nClient%d: transmit a data %d to tServer\n", id, txMsg.mData);
if(msgQReceive(msgQIdClient[id], (char*)&rxMsg, MAX_MSG_LEN, 20) == ERROR) {
printf("\ntClient %d: wait ACK timeout!!!\n");
continue;
}
printf("\ntClient %d: receive the ACK from tServer", id);
if(txMsg.mData == rxMsg.mData) {
printf(", and data check OK!\n");
}
else {
printf("\n, but data check ERROR!!!\n");
printf("tx data(%d) != ACK data(%d)\n", txMsg.mData, rxMsg.mData);
}
taskDelay(DELAY_TICKS + (txMsg .mData % DELAY_TICKS));
}
return (OK);
}
/********************/
void progStop(void) {
int id;
for(id = 0; id < NUM_CLIENT; id++) {
taskDelete(tidClient[id]);
}
taskDelete(tidServer);
msgQDelete(msgQIdServer);
for(id = 0; id < NUM_CLIENT; id++) {
msgQDelete(msgQIdClient[id]);
}
printf("BYE!\n");
return;
}
管道
VxWorks5.5提供的管道机制与消息队列比较类似,创建管道时也会分配一个消息队列,不同的是,管道作为一种虚拟I/O设备,它可由VxWorks5.5提供的一套标准I/O接口来驱动、管理。
管道代码实践
#include "vxWorks.h"
#include "ioLib.h"
#include "pipeDrv.h"
#define MESSAGE "Greetings from Task 1"
#define MAX_MSGS (10)
#define MAX_MSG_LEN sizeof(MESSAGE)
#define STACK_SIZE 20000
#define DELAY_TICKS 50
int tidTask1;
int tidTask2;
int demoPipeFd;
STATUS porgStart(void);
STATUS task1(void);
STATUS task2(void);
void progStop(void);
STATUS progStart(void) {
int result;
result = pipeDevCreate("/pipe/demo", MAX_MSGS, MAX_MSG_LEN);
if(result == ERROR) {
return (ERROR);
}
demoPipeFd = open("/pipe/demo", O_RDWR, 0);
if(demoPipeFd == ERROR) {
return (ERROR);
}
tidTask1 = taskSpawn("tTask1", 200, 0, STACK_SIZE, (FUNCPTR)task1, 0,0,0,0,0,0,0,0,0,0);
tidTask2 = taskSpawn("tTask2", 200, 0, STACK_SIZE, (FUNCPTR)task2, 0,0,0,0,0,0,0,0,0,0);
return (OK);
}
STATUS task1(void) {
while(1) {
write(demoPipeFd, MESSAGE, sizeof(MESSAGE));
taskDelay(DELAY_TICKS);
}
return (OK);
}
STATUS task2(void) {
char msgBuf[MAX_MSG_LEN];
int len = 0;
int result;
fd_set readFd;
while(1) {
memset(msgBuf, 0, MAX_MSG_LEN);
len = 0;
while(len < sizeof(MESSAGE)) {
FD_ZERO(&readFd);
FD_SET(demoPipeFd, &readFd);
result = select(sizeof(fd_set), &readFd, NULL, NULL,NULL);
if(result == 0) {
return (ERROR);
}
len += read(demoPipeFd, msgBuf, sizeof(MESSAGE) - len);
}
printf("Message from task1: %s\n", msgBuf);
}
return (OK);
}
void progStop(void) {
taskDelete(tidTask1);
taskDelete(tidTask2);
close(demoPipeFd);
pipeDevDelete("/pipe/demo", TRUE);
printf("BYE!\n");
return;
}
信号
信号是一种由事件触发的软件中断,它可以异步地改变信号接收任务的执行流程,使其转向执行对应的信号处理程序,所谓软件中断是指,其触发事件来自系统内部执行的软件,而不像通常的中断那样,由CPU的外部管脚接收到的中断信号触发。
#include <stdio.h>
#include "vxWorks.h"
#include "semLib.h"
#include "errno.h"
#include "sigLib.h"
#define STACK_SIZE 20000
SEM_ID semId;
int tidSigReceiver;
STATUS sigReceiver(void);
STATUS txSignal(void);
STATUS sigHandler(int sig);
STATUS progStart(void) {
semId = semBCreate(SEM_Q_FIFO, SEM_EMPTY);
tidSigReceiver = taskSpawn("tSigReceiver", 200, 0, STACK_SIZE, (FUNCPTR)sigReceiver,0,0,0,0,0,0,0,0,0,0);
printf("\nRun cmd \"txSignal()\" in shell to send a signal.\n");
return (OK);
}
STATUS sigReceiver(void) {
signal(SIGUSR1, sigHandler);
semTake(semId, WAIT_FOREVER);
return (OK);
}
STATUS sigHandler(int sig) {
if(sig != SIGUSR1) {
logMsg("\nsigHandler: Invalid sig %d\n", sig, 0, 0, 0, 0, 0);
return;
}
logMsg("\nsighandler is running in %s 's context!\n", taskName(taskIdSelf()), 0,0,0,0,0);
logMsg("\nsighandler is handling sig : %d!\n", sig, 0,0,0,0,0);
return;
}
STATUS txSignal(void) {
kill(tidSigReceiver, SIGUSR1);
return (OK);
}
事件
#include "vxWorks.h"
#include "semLib.h"
#include "eventLib.h"
#define STACK_SIZE 30000
#define DELAY_TICKS 50
int tidEventReceiver;
int tidSemHandler;
SEM_ID semIdEventSender;
STATUS eventReceiver(void);
void semHandler(void);
void progStop(void);
/******************/
STATUS progStart(void) {
semIdEventSender = semBCreate(SEM_Q_FIFO, SEM_EMPTY);
if(semIdEventSender == NULL) {
return (ERROR);
}
tidEventReceiver = taskSpawn("tEventReceiver", 200, 0, STACK_SIZE, (FUNCPTR)eventReceiver, 0,0,0,0,0,0,0,0,0,0);
tidSemHandler = taskSpawn("tSemHandler", 220, 0, STACK_SIZE, (FUNCPTR)semHandler, 0,0,0,0,0,0,0,0,0,0);
return (OK);
}
/*****************/
STATUS eventReceiver(void) {
int result;
UINT32 eventsReceived;
result = sdmEvStart(semIdEventSender, VXEV01, EVENTS_OPTIONS_NONE);
if(result == OK) {
printf("\nEventReceiver: semEvSstart() succ!\n");
}
else{
printf("\nEventReceiver: semEvStart() failed!\n");
return (ERROR);
}
while(1) {
result = eventReceive(VXEV01 | VXEV02, EVENTS_WAIT_ANY, WAIT_FOREVER, &eventsReceived);
if(result == OK) {
printf("\nEventReceiver: received event(VXEV%02d)!\n",eventsReceived);
if((eventsReceived & VXEV01) != 0) {
printf("\nEventReceiver: semphone free!\n");
}
if((eventsReceived & VXEV02) != 0) {
result = semEvStop(semIdEventSender);
if(result == OK) {
printf("\nEventReceiver: semEvStop succ!\n");
}
else {
printf("\ntEventReceiver: semEvStop fail!\n");
printf("\nterrno = %d\n", errnoGet());
}
taskDelete(tidSemHandler);
semDeleete(semIdEventSender);
taskDelete(tidEventReceiver);
}
}
}
return (OK);
}
/*******************/
void semHandler(void) {
while(1) {
semGive(semIdEventSender);
semTake(semIdEventSender, NO_WAIT);
taskDelay(DELAY_TICKS);
}
return;
}
/*********************/
void progStop(void) {
eventSend(tidEventReceiver, VXEV02);
printf("BYE!\n");
return;
}
套接字(socket)
可以参考我的两篇博客: