VxWorks中的任务间通信(信号量、共享内存、消息队列、管道、信号、事件、套接字socket)


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中的任务运行于同一个线性地址空间,各个任务之间可以直接通过共享内存实现信息共享和信息传递。共享内存的存在形式可以是全局变量的简单变量,数组,链表,环形缓冲以及指针,他们可以在不同的任务上下文中访问,如下图所示

在这里插入图片描述

需要注意的是,任务之间如果通过共享内存的方式共享和传递信息,常常需要额外的保护措施,如信号量互斥,中断上锁,任务抢占上锁等,否则会产生访问共享内存这段代码的可重入性问题。

https://raw.githubusercontent.com/xkyvvv/blogpic/main/pic1/image-20210723214909177.png
https://raw.githubusercontent.com/xkyvvv/blogpic/main/pic1/image-20210723214927817.png

消息队列

前面介绍的信号量可以实现同步和互斥机制,共享内存可以达到任务间的信息共享,但是都不能提供完善的响应式交互信息方式,VxWorks5.5提供了一种更为高级的机制,也是单CPU中VxWorks的任务间最主要的通信机制。

image-20210723215257430

https://raw.githubusercontent.com/xkyvvv/blogpic/main/pic1/image-20210723215334895.png

在这里插入图片描述

代码实例:

给出一个两个任务间通过消息队列进行单向通信的简单例子,然后给出一个基于客服端/服务器架构的多个任务间通信的例子。

https://raw.githubusercontent.com/xkyvvv/blogpic/main/pic1/image-20210723215747066.png

#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;
}

在这里插入图片描述

基于客服端/服务器架构的多任务间通信

https://raw.githubusercontent.com/xkyvvv/blogpic/main/pic1/image-20210723222424134.png
https://raw.githubusercontent.com/xkyvvv/blogpic/main/pic1/image-20210723222435981.png

#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接口来驱动、管理。

https://raw.githubusercontent.com/xkyvvv/blogpic/main/pic1/image-20210724001523968.png
https://raw.githubusercontent.com/xkyvvv/blogpic/main/pic1/image-20210724001547935.png

管道代码实践

https://raw.githubusercontent.com/xkyvvv/blogpic/main/pic1/image-20210724001718277.png

#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;
}

image-20210724003923016

信号

信号是一种由事件触发的软件中断,它可以异步地改变信号接收任务的执行流程,使其转向执行对应的信号处理程序,所谓软件中断是指,其触发事件来自系统内部执行的软件,而不像通常的中断那样,由CPU的外部管脚接收到的中断信号触发。

https://raw.githubusercontent.com/xkyvvv/blogpic/main/pic1/image-20210724004515183.png

https://raw.githubusercontent.com/xkyvvv/blogpic/main/pic1/image-20210724005840224.png

在这里插入图片描述

#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);
}

在这里插入图片描述

事件

https://raw.githubusercontent.com/xkyvvv/blogpic/main/pic1/image-20210724015600030.png

#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)

可以参考我的两篇博客:

总结

在这里插入图片描述

  • 10
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小熊coder

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值