信号量+共享内存通信
该文章主要用c语言实现共享内存方式的进程间通信,通信过程使用有名信号量控制对应进程的读写,进而判断该进程是否可读,可写。
该实现方式申请了两块共享内存,创建了两对信号量(一对信号量用来控制一个进程的读写,即创建了4个信号量,4个信号量控制分别控制读写,方便理解),使得两个进程可以同时读写。
代码不是全部代码,需自行理解并适配自己的代码逻辑
信号量初始化
#define Printf(format, ...) \
do\
{\
printf("%s(%d):", __func__, __LINE__); \
printf(format, ##__VA_ARGS__); \
printf("\n"); \
}\
while(0)
/**************************************************************************
函数名称: Set_SemTimeOut
功能描述: 设置信号量等待超时时间
输入参数: IN int Mode 判断模式;0-不分白天夜晚、1-白天、2-夜晚
IN double current_time 时间
IN JSON_DATA *pstJsonData config文件中的json数据
输出参数:
注 意 点: 该函数固定设置超时时间为120s
***************************************************************************/
int Set_SemTimeOut(OUT struct timespec *pstTime)
{
struct timespec stTime;
memset(&stTime, 0, sizeof(stTime));
if (NULL == pstTime)
{
log_e("[CURL]Invalid parameter");
return ERR_COMMON_INVALID_PARAM;
}
stTime.tv_sec = time(NULL) + 120;
stTime.tv_nsec = 0;
memcpy(pstTime, &stTime, sizeof(stTime));
return ERR_COMMON_SUCCEED;
}
/**************************************************************************
函数名称: POSIX_Sem_Init
功能描述: 初始化POSIX信号量
输入参数: INOUT SM_PosixSignal *LiShield_signal LiShield程序的信号量
INOUT SM_PosixSignal Algorithm_signal 算法程序的信号量
输出参数: INOUT SM_PosixSignal *LiShield_signal LiShield程序的信号量
INOUT SM_PosixSignal Algorithm_signal 算法程序的信号量
注 意 点: 目前信号量对应的文件名固定为LiShield_W、LiShield_R、Algorithm_W、Algorithm_R
***************************************************************************/
int POSIX_Sem_Init(INOUT SM_PosixSignal *pstLiShield_signal, INOUT SM_PosixSignal *pstAlgorithm_signal)
{
pstLiShield_signal->Write = NULL;
pstLiShield_signal->Read = NULL;
pstAlgorithm_signal->Write = NULL;
pstAlgorithm_signal->Read = NULL;
pstLiShield_signal->Write = sem_open(LiShield_W, O_CREAT, 0777, 1);
if (NULL == pstLiShield_signal->Write)
{
Printf("[MSG_CTRL]sem_open(%s) faild, error: %s\n", LiShield_W, strerror(errno));
return ERR_COMMON_FAIL;
}
pstLiShield_signal->Read = sem_open(LiShield_R, O_CREAT, 0777, 0);
if (NULL == pstLiShield_signal->Read)
{
Printf("[MSG_CTRL]sem_open(%s) faild, error: %s\n", LiShield_R, strerror(errno));
return ERR_COMMON_FAIL;
}
pstAlgorithm_signal->Write = sem_open(Algorithm_W, O_CREAT, 0777, 1);
if (NULL == pstAlgorithm_signal->Write)
{
Printf("[MSG_CTRL]sem_open(%s) faild, error: %s\n", Algorithm_W, strerror(errno));
return ERR_COMMON_FAIL;
}
pstAlgorithm_signal->Read= sem_open(Algorithm_R, O_CREAT, 0777, 0);
if (NULL == pstAlgorithm_signal->Read)
{
Printf("[MSG_CTRL]sem_open(%s) faild, error: %s\n", Algorithm_R, strerror(errno));
return ERR_COMMON_FAIL;
}
return ERR_COMMON_SUCCEED;
}
共享内存初始化
/**************************************************************************
函数名称: POSIX_ShareMem_Init
功能描述: 初始化共享内存
输入参数: INOUT Sharemem_Info *pstLiShield_share LiShield程序的共享内存
INOUT Sharemem_Info pstAlgorithm_share 算法程序的共享内存
输出参数: INOUT Sharemem_Info *pstLiShield_share LiShield程序的共享内存
INOUT Sharemem_Info pstAlgorithm_share 算法程序的共享内存
注 意 点: 目前共享内存文件名固定为LiShield_W、Algorithm_W;共享内存大小固定为40M
***************************************************************************/
int POSIX_ShareMem_Init(INOUT Sharemem_Info *pstLiShield_share, INOUT Sharemem_Info *pstAlgorithm_share)
{
int shmid = 0;
if ((NULL == pstLiShield_share) || (NULL == pstAlgorithm_share))
{
Printf("[MSG_CTRL]Invalid parameter\n");
return ERR_COMMON_INVALID_PARAM;
}
/* 初始化LiShield程序的共享内存 */
shmid = shm_open(LiShield_W, O_CREAT|O_RDWR, 0777);
if (shmid < 0)
{
Printf("[MSG_CTRL]shm_open() error: %s\n", strerror(errno));
return ERR_COMMON_FAIL;
}
ftruncate(shmid, SHAREMEM_SIZE_MAX);
pstLiShield_share->szie = SHAREMEM_SIZE_MAX;
pstLiShield_share->pShmsendbuf = mmap(NULL, SHAREMEM_SIZE_MAX, PROT_READ|PROT_WRITE, MAP_SHARED, shmid, 0);
if (NULL == pstLiShield_share->pShmsendbuf)
{
Printf("[MSG_CTRL]mmap failed\n");
//shm_unlink(pstLiShield_share->name);
return ERR_COMMON_FAIL;
}
pstLiShield_share->shmid = shmid;
/* 初始化算法程序的共享内存 */
shmid = shm_open(Algorithm_W, O_CREAT|O_RDWR, 0777);
if (shmid < 0)
{
Printf("[MSG_CTRL]shm_open() error: %s\n", strerror(errno));
return ERR_COMMON_FAIL;
}
ftruncate(shmid, SHAREMEM_SIZE_MAX);
pstAlgorithm_share->szie = SHAREMEM_SIZE_MAX;
pstAlgorithm_share->pShmsendbuf = mmap(NULL, SHAREMEM_SIZE_MAX, PROT_READ|PROT_WRITE, MAP_SHARED, shmid, 0);
if (NULL == pstAlgorithm_share->pShmsendbuf)
{
Printf("[MSG_CTRL]mmap failed\n");
//shm_unlink(pstAlgorithm_share->name);
return ERR_COMMON_FAIL;
}
pstAlgorithm_share->shmid = shmid;
return ERR_COMMON_SUCCEED;
}
发送接收信息
/**************************************************************************
函数名称: LiShieldSendMsg
功能描述: LiShield程序发送消息
输入参数: IN IPC_Info *pstIpcInfo 进程间通信相关结构
INOUT MSG_Info *pstMsgInfo 消息结构体
输出参数:
注 意 点:
***************************************************************************/
int LiShieldSendMsg(IN IPC_Info *pstIpcInfo, IN MSG_Info *pstMsgInfo)
{
int MsgStatusCode = ERR_COMMON_FAIL;
struct timespec stTime;
MSG_Info *pstMsgInfoTmp = NULL;
memset(&stTime, 0, sizeof(stTime));
if ((NULL == pstIpcInfo) ||
(NULL == pstIpcInfo->LiShield_signal.Write) ||
(NULL == pstIpcInfo->Algorithm_signal.Read) ||
(NULL == pstMsgInfo) ||
((sizeof(MSG_Info) + pstMsgInfo->DataLen) > pstIpcInfo->LiShield_share.szie))
{
Printf("[MSG_CTRL]Invalid parameter");
return ERR_COMMON_INVALID_PARAM;
}
Printf("[MSG_CTRL]LiShield Send Msg begin, MsgType[%d], MsgCmd[%d]", pstMsgInfo->MsgType, pstMsgInfo->MsgCmd);
/* 等待LiShield程序可以写共享内存的信号量, 使用sem_timedwait--固定超时时间为120s, 避免出现另一个进程状态异常导致本进程阻塞 */
//sem_wait(pstIpcInfo->LiShield_signal.Write);
Set_SemTimeOut(&stTime);
if (0 != sem_timedwait(pstIpcInfo->LiShield_signal.Write, &stTime))
{
Printf("[MSG_CTRL]sem_timedwait failed, error:%s", strerror(errno));
return ERR_COMMON_FAIL;
}
/* 先清空再拷贝,避免出现pShmsendbuf中残留数据 */
pstMsgInfo->MsgStatusCode = ERR_COMMON_FAIL;
memset(pstIpcInfo->LiShield_share.pShmsendbuf, 0, pstIpcInfo->LiShield_share.szie);
memcpy(pstIpcInfo->LiShield_share.pShmsendbuf, pstMsgInfo, sizeof(MSG_Info) + pstMsgInfo->DataLen);
/* LiShield程序写完后告诉算法可以读取共享内存的数据 */
sem_post(pstIpcInfo->Algorithm_signal.Read);
/* 发完消息后,等待算法程序处理完消息,嵌入式程序需要查看消息是否处理成功,以及拷贝GET类型消息返回的数据 */
//sem_wait(pstIpcInfo->LiShield_signal.Write);
Printf("[MSG_CTRL]LiShield wait get msg result");
if (0 != sem_timedwait(pstIpcInfo->LiShield_signal.Write, &stTime))
{
/* 120s超时, 则认为另外一个进程B不存在, 则需清空共享内存, 避免出现进程B启动时读到本次发送的消息, 其实本次消息对于进程B来说属于无用消息 */
memset(pstIpcInfo->LiShield_share.pShmsendbuf, 0, pstIpcInfo->LiShield_share.szie);
Printf("[MSG_CTRL]sem_timedwait failed, error:%s", strerror(errno));
return ERR_COMMON_FAIL;
}
usleep(1000 * 20);
if (MSG_TYPE_GET == pstMsgInfo->MsgType)
{
memcpy(pstMsgInfo, pstIpcInfo->LiShield_share.pShmsendbuf, sizeof(MSG_Info) + pstMsgInfo->DataLen);
MsgStatusCode = pstMsgInfo->MsgStatusCode;
}
else
{
pstMsgInfoTmp = (MSG_Info *)pstIpcInfo->LiShield_share.pShmsendbuf;
MsgStatusCode = pstMsgInfoTmp->MsgStatusCode;
}
sem_post(pstIpcInfo->LiShield_signal.Write);
if (ERR_COMMON_SUCCEED != MsgStatusCode)
{
Printf("[MSG_CTRL]send LiShield fail[%d], MsgType[%d], MsgCmd[%d]", MsgStatusCode, pstMsgInfo->MsgType, pstMsgInfo->MsgCmd);
return ERR_COMMON_FAIL;
}
Printf("[MSG_CTRL]LiShield Send Msg end success, MsgType[%d], MsgCmd[%d]", pstMsgInfo->MsgType, pstMsgInfo->MsgCmd);
return ERR_COMMON_SUCCEED;
}
/**************************************************************************
函数名称: LiShieldMsgProc
功能描述: LiShield程序消息处理
输入参数: IN MSG_Info *pstMsgInfo 消息结构体
输出参数:
注 意 点: 内部根据不同的消息类型进行处理
***************************************************************************/
int LiShieldMsgProc(IN MSG_Info *pstMsgInfo)
{
int ret = ERR_COMMON_FAIL;
if ((NULL == pstMsgInfo) || (NULL == pstMsgInfo->pData))
{
Printf("[MSG_CTRL]Invalid parameter");
return ERR_COMMON_INVALID_PARAM;
}
/* 目前只支持SET消息,后续支持其他消息则取消此判断 */
if (MSG_TYPE_SET != pstMsgInfo->MsgType)
{
Printf("[MSG_CTRL]Recv Msg-->not support MsgType[%d]. MsgCmd[%d]", pstMsgInfo->MsgType, pstMsgInfo->MsgCmd);
return ERR_COMMON_INVALID_PARAM;
}
else
{
Printf("[MSG_CTRL]Recv Msg-->MsgType[%d], MsgCmd[%d]", pstMsgInfo->MsgType, pstMsgInfo->MsgCmd);
}
/* 根据消息命令字处理不同的消息 */
switch (pstMsgInfo->MsgCmd)
{
case 1:
{
/* 自己写对应消息的处理函数 */
break;
}
case 2:
{
break;
}
case 3:
{
break;
}
default:
{
ret = ERR_COMMON_NOT_SUPPORT;
Printf("[MSG_CTRL]not support msgcmd[%d]", pstMsgInfo->MsgCmd);
break;
}
}
if (ERR_COMMON_SUCCEED != ret)
{
Printf("[MSG_CTRL]msg proc fail[%d], MsgType[%d] MsgCmd[%d]", ret, pstMsgInfo->MsgType, pstMsgInfo->MsgCmd);
}
return ret;
}
/**************************************************************************
函数名称: LiShieldRecvMsg
功能描述: LiShield接收消息
输入参数: IN IPC_Info *pstIpcInfo 进程间通信相关结构
INOUT MSG_Info *pstMsgInfo 消息结构体
输出参数: INOUT MSG_Info *pstMsgInfo 消息结构体
注 意 点:
***************************************************************************/
int LiShieldRecvMsg(IN IPC_Info *pstIpcInfo, INOUT MSG_Info *pstMsgInfo)
{
int ret = ERR_COMMON_FAIL;
if ((NULL == pstIpcInfo) ||
(NULL == pstIpcInfo->LiShield_signal.Read) ||
(NULL == pstIpcInfo->Algorithm_signal.Write) ||
(NULL == pstMsgInfo))
{
Printf("[MSG_CTRL]Invalid parameter");
return ERR_COMMON_INVALID_PARAM;
}
while (1)
{
sem_wait(pstIpcInfo->LiShield_signal.Read);
pstMsgInfo = (MSG_Info *)pstIpcInfo->Algorithm_share.pShmsendbuf;
ret = LiShieldMsgProc(pstMsgInfo);
if (ERR_COMMON_SUCCEED != ret)
{
Printf("[MSG_CTRL]msg proc failed[%d], MsgType[%d], MsgCmd[%d]", ret, pstMsgInfo->MsgType, pstMsgInfo->MsgCmd);
}
pstMsgInfo->MsgStatusCode = ret;
sem_post(pstIpcInfo->Algorithm_signal.Write);
usleep(1000 * 10);
}
return ERR_COMMON_SUCCEED;
}
/**************************************************************************
函数名称: AlgorithmSendMsg
功能描述: 算法程序发送消息
输入参数: IN IPC_Info *pstIpcInfo 进程间通信相关结构
INOUT MSG_Info *pstMsgInfo 消息结构体
输出参数:
注 意 点:
***************************************************************************/
int AlgorithmSendMsg(IN IPC_Info *pstIpcInfo, IN MSG_Info *pstMsgInfo)
{
int MsgStatusCode = ERR_COMMON_FAIL;
struct timespec stTime;
MSG_Info *pstMsgInfoTmp = NULL;
memset(&stTime, 0, sizeof(stTime));
if ((NULL == pstIpcInfo) ||
(NULL == pstIpcInfo->Algorithm_signal.Write) ||
(NULL == pstIpcInfo->LiShield_signal.Read) ||
(NULL == pstMsgInfo) ||
((sizeof(MSG_Info) + pstMsgInfo->DataLen) > pstIpcInfo->LiShield_share.szie))
{
Printf("[MSG_CTRL]Invalid parameter");
return ERR_COMMON_INVALID_PARAM;
}
/* 等待算法程序可以写共享内存的信号量, 使用sem_timedwait--固定超时时间为120s, 避免出现另一个进程状态异常导致本进程阻塞 */
//sem_wait(pstIpcInfo->Algorithm_signal.Write);
Set_SemTimeOut(&stTime);
if (0 != sem_timedwait(pstIpcInfo->Algorithm_signal.Write, &stTime))
{
Printf("[MSG_CTRL]sem_timedwait failed, error:%s", strerror(errno));
return ERR_COMMON_FAIL;
}
/* 先清空再拷贝,避免出现pShmsendbuf中残留数据 */
pstMsgInfo->MsgStatusCode = ERR_COMMON_FAIL;
memset(pstIpcInfo->Algorithm_share.pShmsendbuf, 0, pstIpcInfo->Algorithm_share.szie);
memcpy(pstIpcInfo->Algorithm_share.pShmsendbuf, pstMsgInfo, sizeof(MSG_Info) + pstMsgInfo->DataLen);
/*算法程序写完后告诉LiShield程序可以读取共享内存的数据 */
sem_post(pstIpcInfo->LiShield_signal.Read);
/* 发完消息后,等待嵌入式程序处理完消息,算法程序需要查看消息是否处理成功,以及拷贝GET类型消息返回的数据 */
//sem_wait(pstIpcInfo->Algorithm_signal.Write);
if (0 != sem_timedwait(pstIpcInfo->Algorithm_signal.Write, &stTime))
{
/* 120s超时, 则认为另外一个进程B不存在, 则需清空共享内存, 避免出现进程B启动时读到本次发送的消息, 其实本次消息对于进程B来说属于无用消息 */
memset(pstIpcInfo->Algorithm_share.pShmsendbuf, 0, pstIpcInfo->Algorithm_share.szie);
Printf("[MSG_CTRL]sem_timedwait failed, error:%s", strerror(errno));
return ERR_COMMON_FAIL;
}
usleep(1000 * 20);
if (MSG_TYPE_GET == pstMsgInfo->MsgType)
{
memcpy(pstMsgInfo, pstIpcInfo->Algorithm_share.pShmsendbuf, sizeof(MSG_Info) + pstMsgInfo->DataLen);
MsgStatusCode = pstMsgInfo->MsgStatusCode;
}
else
{
pstMsgInfoTmp = (MSG_Info *)pstIpcInfo->Algorithm_share.pShmsendbuf;
MsgStatusCode = pstMsgInfoTmp->MsgStatusCode;
}
sem_post(pstIpcInfo->Algorithm_signal.Write);
if (ERR_COMMON_SUCCEED != MsgStatusCode)
{
Printf("[MSG_CTRL]send Algorithm fail[%d], MsgType[%d], MsgCmd[%d]", MsgStatusCode, pstMsgInfo->MsgType, pstMsgInfo->MsgCmd);
return ERR_COMMON_FAIL;
}
return ERR_COMMON_SUCCEED;
}
/**************************************************************************
函数名称: AlgorithmMsgProc
功能描述: 算法程序消息处理
输入参数: IN MSG_Info *pstMsgInfo 消息结构体
输出参数:
注 意 点: 内部根据不同的消息类型进行处理
***************************************************************************/
int AlgorithmMsgProc(IN MSG_Info *pstMsgInfo)
{
int ret = ERR_COMMON_FAIL;
if ((NULL == pstMsgInfo) || (NULL == pstMsgInfo->pData))
{
Printf("[MSG_CTRL]Invalid parameter");
return ERR_COMMON_INVALID_PARAM;
}
/* 目前只支持SET消息,后续支持其他消息则取消此判断 */
if (MSG_TYPE_SET != pstMsgInfo->MsgType)
{
Printf("[MSG_CTRL]not support MsgType[%d]. MsgCmd[%d]", pstMsgInfo->MsgType, pstMsgInfo->MsgCmd);
return ERR_COMMON_INVALID_PARAM;
}
else
{
Printf("[MSG_CTRL]MsgType[%d], MsgCmd[%d]", pstMsgInfo->MsgType, pstMsgInfo->MsgCmd);
}
/* 根据消息类型处理不同的消息 */
switch (pstMsgInfo->MsgCmd)
{
case 1:
{
/* 自己写处理函数 */
break;
}
case 2:
{
break;
}
case 3:
{
break;
}
default:
{
ret = ERR_COMMON_NOT_SUPPORT;
Printf("[MSG_CTRL]not support msgcmd[%d]", pstMsgInfo->MsgCmd);
break;
}
}
return ret;
}
/**************************************************************************
函数名称: AlgorithmRecvMsg
功能描述: 算法程序接收消息
输入参数: IN IPC_Info *pstIpcInfo 进程间通信相关结构
INOUT MSG_Info *pstMsgInfo 消息结构体
输出参数: INOUT MSG_Info *pstMsgInfo 消息结构体
注 意 点:
***************************************************************************/
int AlgorithmRecvMsg(IN IPC_Info *pstIpcInfo, INOUT MSG_Info *pstMsgInfo)
{
int ret = ERR_COMMON_FAIL;
if ((NULL == pstIpcInfo) ||
(NULL == pstIpcInfo->Algorithm_signal.Read) ||
(NULL == pstIpcInfo->LiShield_signal.Write) ||
(NULL == pstMsgInfo))
{
Printf("[MSG_CTRL]Invalid parameter");
return ERR_COMMON_INVALID_PARAM;
}
while (1)
{
sem_wait(pstIpcInfo->Algorithm_signal.Read);
pstMsgInfo = (MSG_Info *)pstIpcInfo->LiShield_share.pShmsendbuf;
ret = AlgorithmMsgProc(pstMsgInfo);
if (ERR_COMMON_SUCCEED != ret)
{
Printf("[MSG_CTRL]msg proc failed[%d], MsgType[%d], MsgCmd[%d]\n", ret, pstMsgInfo->MsgType, pstMsgInfo->MsgCmd);
}
pstMsgInfo->MsgStatusCode = ret;
sem_post(pstIpcInfo->LiShield_signal.Write);
usleep(1000 * 10);
}
return ERR_COMMON_SUCCEED;
}
相关定义
#define LiShield_W "LiShield_W2_Algorithm" /* LiShield写数据有名信号量。LiShield写完需要告诉算法共享内存可读了,需要sem_post(Algorithm_R) */
#define LiShield_R "LiShield_R" /* LiShield读数据有名信号量。LiShield读完需要告诉算法共享内存可写了, 需要sem_post(Algorithm_W) */
#define Algorithm_W "Algorithm_W2_LiShield" /* Algorithm写数据信号量 */
#define Algorithm_R "Algorithm_R" /* Algorithm读数据信号量 */
/* 与共享内存相关的信号量 */
typedef struct sharemem_signal
{
sem_t *Write; /* 是否可写的信号量,为1表示可写 */
sem_t *Read; /* 是否可读的信号量,为1表示可读 */
}SM_PosixSignal;
/* 共享内存信息 */
typedef struct sharemem_info
{
char name[128]; /* POSIX 共享内存标识, 比如LiShield_W */
int szie; /* 共享内存大小 */
int shmid; /* 创建共享内存成功后系统返回的文件标识符 */
void *pShmsendbuf; /* 共享内存对应的内存空间 */
}Sharemem_Info;
/* 进程间通信交互信息 */
typedef struct POSIXSharemem_signal
{
SM_PosixSignal LiShield_signal; /* LiShield程序的信号量 */
Sharemem_Info LiShield_share; /* LiShield程序的共享内存信息 */
SM_PosixSignal Algorithm_signal; /* 算法程序的信号量 */
Sharemem_Info Algorithm_share; /* 算法程序的共享内存信息 */
}IPC_Info;
/* 消息结构体信息 */
typedef struct msginfo
{
int MsgType; /* 消息类型: MSG_Type */
int MsgCmd; /* 消息命令字 */
int FirstChannel; /* 第一通道 */
int SecondChannel; /* 第二通道 */
int ThirdChannel; /* 第三通道 */
int SubChannel; /* 子通道 */
int MsgStatusCode; /* 消息处理结果码 */
int DataLen; /* 消息大小 */
char pData[0]; /* 消息内容, 可变长数组; 此处使用指针的话, 内存空间不连续 消息不好拷贝, 因此不使用指针 */
}MSG_Info;
/* 消息类型 */
typedef enum MsgType {
MSG_TYPE_GET,
MSG_TYPE_SET,
MSG_TYPE_NOTIFY,
}MSG_Type;