这次项目中关于进程间通信的部分我一般都采用了msgq,一般信息数据量都不是很大。
一、简述一下项目的需求部分:
1. api与server端进行相互通信。
2. server对不同的api接口做出应答,并且api能够识别出是应答哪个接口。
3.api等待超时,这里考虑到server端故障或者已经退出等等异常情况,api应该采取一些措施,避免死等而造成假死现象。
4.代码原文,请翻到文末。
5.本代码在api端和server端都可以使用
二、代码分步讲解
1. magq的数据组成,h文件中声明
typedef struct //消息结构体
{
long types; //指定消息的类型
int cmd; //消息的命令
int param1; //参数1
int param2; //参数2
int ret; //参数3,可用于返回值
}msgq_t;
由于我本身需要发送的信息并不是很多,就定义了一个这样的结构体,主要还是cmd其主要作用,我以此区分是什么api的请求(或者是什么样的功能)。
注意:types是本身msgq的要求。必须为long类型。
2. 初始化
参数,无
返回值,0表示成功,其他表示失败
//返回值0表示成功,其他表示失败
int msgq_init(void)
{
key_t key;
key = ftok(KEY_PATH,KEY_ID); //获取key值
if(key < 0)
{
printf("ftok fail \n");
fflush(stdout);
return (-EAGAIN);
}
// 创建消息队列,如果消息队列存在,errno 会提示 eexist
// 错误,此时只需要直接打开消息队列即可
msgid = msgget(key,IPC_CREAT|IPC_EXCL|0666);
if(msgid < 0)
{
if(errno == EEXIST) //文件存在错误提示
{
msgid = msgget(key,0666);//打开消息队列
MsgClear();
}
else //其他错误退出
{
printf("msgget fail \n");
return(-EAGAIN);
}
}
//printf("msgget success!! msgid = %d\n",msgid);
return 0;
}
进程间通信,系统中两个进程是互不可见的,那如何找到对方呢?那就是要使用唯一的口令key,来保障通信双方。
ftok 本身没有在内核(系统)中产生什么东西,只是根据传入的参数,计算了一个数据(使它尽量产生的数据是唯一的)。
msgget 将是使用ftok的数据去打开或者创建一个msgq的消息队列。IPC_EXCL存在时报错,但是我对错误进行了处理,EEXIST表示文件存在错误,这时再次尝试去打开就好。
打开成功,就返回0.
程序运行后,可以使用ipcs -q 命令查看系统中的msgq的信息。除非手动(命令或者调用函数)删除,直到系统关闭比之前,是会一直存在的。
3.发送
参数1:ack_types 表示接收(等待应答用)的消息类型
参数2:需要发送的消息
参数3:用于设置超时,0表示阻塞模式,非零则有超时时间
系统调用msgsnd 的第三个参数,消息的字节数应该要减去type的字节数。
//发送消息,并且要等待应答。
参数 timeout 大于0时,设置接收应答超时时间(50ms的整数倍),等于0表示阻塞模式
// ack_types 指定应答的类型,之前是TYPE_RECV+cmd
// msgbuf 发送消息的缓存,1次发送一条消息
//返回0表示成功,其他表示失败
//注意,函数使用时,需要指定msgbuf->types !!!!
int msgq_send(long ack_types,msgq_t *msgbuf,int timeout)
{
int ret = -EBUSY; //初始值
// int cmd = msgbuf->cmd;if(msgid == -1) //未初始化
{
printf("send error,msgq_init first!!!\n");
fflush(stdout);
return -EPERM;
}//msgbuf->types = TYPE_SEND; //给消息结构赋值,发送的类型是固定的TYPE_SEND
//printf("debug: send type = %ld cmd = %d b = %d c = %d rt = %d\n",msgbuf->types,msgbuf->cmd,msgbuf->param1,msgbuf->param2,msgbuf->ret);
//发送消息
if(msgsnd(msgid, msgbuf, sizeof(msgq_t)-sizeof(long),0) == 0)
{
// printf("debug:msgsnd ok \n");
//等待应答,等待的类型跟命令有关!!!!!
ret = msgq_recv(ack_types,msgbuf,timeout);
if(ret == 0) //正常在这返回0,仍然可能不是0.
{
// printf("msgq_send and recv ok!!msgbuf.ret = %d\n",msgbuf->ret);
//用param1返回数据
return 0;
}
}
printf("msgsnd error!\n"); //这条路应该还是有问题的。打印一下错误提示
fflush(stdout);
return ret; // 返回错误值
}
4.接收
参数1:ack_types 表示接收的消息类型
参数2:接收消息的缓存
参数3:用于设置超时,0表示阻塞模式,非零则有超时时间
//接收消息
//参数 timeout_50ms 大于0时,表示非阻塞模式,等待的时间为timeout_50ms*50ms的值,等于0表示阻塞模式
// types 接收消息的类型
// msgbuf 接收消息的缓存,1次接收一条消息
//返回0表示成功,其他表示失败
int msgq_recv(long types,msgq_t *msgbuf,unsigned int timeout_50ms)
{
int recv_flg = 0;
int ret;
// int retry = 0;if(msgid == -1) //未初始化
{
printf("recv error,msgq_init first!!!\n");
fflush(stdout);
return -EPERM;
}if(timeout_50ms > 0)
recv_flg = IPC_NOWAIT;//接收消息
do{
ret = msgrcv(msgid, msgbuf, sizeof(msgq_t)-sizeof(long),types,recv_flg); //防止死循环
if(ret >= 0)
break;
else if(0 == recv_flg) //阻塞堵塞模式出问题
{
if(errno == EINTR)
continue;
return -EINTR;
}
else if(errno != ENOMSG)
{
printf("msgrcv\n");
fflush(stdout);
return -ENOMSG;
}usleep(50000); //休眠50ms
}while(--timeout_50ms);
if(recv_flg && (0 == timeout_50ms)) //超时,可能设置为等待模式
{
printf("ERROR:msg recv wait timeout,recv type = %ld!!!\n",types);
fflush(stdout);
return -EAGAIN;
}
// printf("type = %ld cmd = %d b = %d c = %d rt = %d\n",msgbuf->types,msgbuf->cmd,msgbuf->b,msgbuf->c,msgbuf->ret);
// fflush(stdout);
return 0;
}
5.应答,一般用于服务端对api的应答
参数:消息对应的缓存
//发送应答消息消息,不等待应答。
int msgq_send_ack(msgq_t *msgbuf)
{
if(msgid == -1) //未初始化
{
printf("send error,msgq_init first!!!\n");
fflush(stdout);
return -1;
}//msgbuf->types = TYPE_SEND; //给消息结构赋值,发送的类型是固定的TYPE_SEND
// printf("send type = %ld cmd = %d b = %d c = %d rt = %d\n",msgbuf->types,msgbuf->cmd,msgbuf->b,msgbuf->c,msgbuf->ret);
//发送消息
if(msgsnd(msgid, msgbuf, sizeof(msgq_t)-sizeof(long),0) == 0)
{
return 0;
}printf("msgsnd error!\n"); //这条路应该还是有问题的。打印一下错误提示
fflush(stdout);
return -1;
}
三、代码全文
c.的全文
/*
* @Author: dazhi
* @Date: 2022-07-27 09:57:14
* @Last Modified by: dazhi
* @Last Modified time: 2022-08-16 15:02:16
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
//#include "drv_22134.h"
#include <utmp.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#include "my_ipc_msgq.h" //用于与串口程序通信!!
//用户创建消息队列的关键字
#define KEY_PATH "/proc/cpuinfo"
#define KEY_ID -123321
static int msgid = -1; //消息队列的描述符
static int MsgClear(void)
{
msgq_t msgbuf;
if(msgid == -1)
return -EPERM;
while( msgrcv(msgid, &msgbuf, sizeof(msgbuf)-sizeof(long),0,IPC_NOWAIT) >0) ; //关闭的时候把所有的消息都清除掉
return 0;
}
//返回值0表示成功,其他表示失败
int msgq_init(void)
{
key_t key;
key = ftok(KEY_PATH,KEY_ID); //获取key值
if(key < 0)
{
printf("ftok fail \n");
fflush(stdout);
return (-EAGAIN);
}
// 创建消息队列,如果消息队列存在,errno 会提示 eexist
// 错误,此时只需要直接打开消息队列即可
msgid = msgget(key,IPC_CREAT|IPC_EXCL|0666);
if(msgid < 0)
{
if(errno == EEXIST) //文件存在错误提示
{
msgid = msgget(key,0666);//打开消息队列
MsgClear();
}
else //其他错误退出
{
printf("msgget fail \n");
return(-EAGAIN);
}
}
//printf("msgget success!! msgid = %d\n",msgid);
return 0;
}
//返回值0表示成功,其他表示失败
int msgq_exit(void)
{
MsgClear(); //清楚消息队列中的消息
return 0;
}
//接收消息
//参数 timeout_50ms 大于0时,表示非阻塞模式,等待的时间为timeout_50ms*50ms的值,等于0表示阻塞模式
// types 接收消息的类型
// msgbuf 接收消息的缓存,1次接收一条消息
//返回0表示成功,其他表示失败
int msgq_recv(long types,msgq_t *msgbuf,unsigned int timeout_50ms)
{
int recv_flg = 0;
int ret;
// int retry = 0;
if(msgid == -1) //未初始化
{
printf("recv error,msgq_init first!!!\n");
fflush(stdout);
return -EPERM;
}
if(timeout_50ms > 0)
recv_flg = IPC_NOWAIT;
//接收消息
do{
ret = msgrcv(msgid, msgbuf, sizeof(msgq_t)-sizeof(long),types,recv_flg); //防止死循环
if(ret >= 0)
break;
else if(0 == recv_flg) //阻塞堵塞模式出问题
{
if(errno == EINTR)
continue;
return -EINTR;
}
else if(errno != ENOMSG)
{
printf("msgrcv\n");
fflush(stdout);
return -ENOMSG;
}
usleep(50000); //休眠50ms
}while(--timeout_50ms);
if(recv_flg && (0 == timeout_50ms)) //超时,可能设置为等待模式
{
printf("ERROR:msg recv wait timeout,recv type = %ld!!!\n",types);
fflush(stdout);
return -EAGAIN;
}
// printf("type = %ld cmd = %d b = %d c = %d rt = %d\n",msgbuf->types,msgbuf->cmd,msgbuf->b,msgbuf->c,msgbuf->ret);
// fflush(stdout);
return 0;
}
//发送消息,并且要等待应答。
参数 timeout 大于0时,设置接收应答超时时间(50ms的整数倍),等于0表示阻塞模式
// ack_types 指定应答的类型,之前是TYPE_RECV+cmd
// msgbuf 发送消息的缓存,1次发送一条消息
//返回0表示成功,其他表示失败
//注意,函数使用时,需要指定msgbuf->types !!!!
int msgq_send(long ack_types,msgq_t *msgbuf,int timeout)
{
int ret = -EBUSY; //初始值
// int cmd = msgbuf->cmd;
if(msgid == -1) //未初始化
{
printf("send error,msgq_init first!!!\n");
fflush(stdout);
return -EPERM;
}
//msgbuf->types = TYPE_SEND; //给消息结构赋值,发送的类型是固定的TYPE_SEND
//printf("debug: send type = %ld cmd = %d b = %d c = %d rt = %d\n",msgbuf->types,msgbuf->cmd,msgbuf->param1,msgbuf->param2,msgbuf->ret);
//发送消息
if(msgsnd(msgid, msgbuf, sizeof(msgq_t)-sizeof(long),0) == 0)
{
// printf("debug:msgsnd ok \n");
//等待应答,等待的类型跟命令有关!!!!!
ret = msgq_recv(ack_types,msgbuf,timeout);
if(ret == 0) //正常在这返回0,仍然可能不是0.
{
// printf("msgq_send and recv ok!!msgbuf.ret = %d\n",msgbuf->ret);
//用param1返回数据
return 0;
}
}
printf("msgsnd error!\n"); //这条路应该还是有问题的。打印一下错误提示
fflush(stdout);
return ret; // 返回错误值
}
//发送应答消息消息,不等待应答。
int msgq_send_ack(msgq_t *msgbuf)
{
if(msgid == -1) //未初始化
{
printf("send error,msgq_init first!!!\n");
fflush(stdout);
return -1;
}
//msgbuf->types = TYPE_SEND; //给消息结构赋值,发送的类型是固定的TYPE_SEND
// printf("send type = %ld cmd = %d b = %d c = %d rt = %d\n",msgbuf->types,msgbuf->cmd,msgbuf->b,msgbuf->c,msgbuf->ret);
//发送消息
if(msgsnd(msgid, msgbuf, sizeof(msgq_t)-sizeof(long),0) == 0)
{
return 0;
}
printf("msgsnd error!\n"); //这条路应该还是有问题的。打印一下错误提示
fflush(stdout);
return -1;
}
h文件全文
/*
* @Author: dazhi
* @Date: 2022-07-27 09:57:14
* @Last Modified by: dazhi
* @Last Modified time: 2022-07-27 10:19:48
*/
#ifndef __MY_IPC_SMGQ_H__
#define __MY_IPC_SMGQ_H__
//用户创建消息队列的关键字
#define KEY_PATH "/proc/cpuinfo"
#define KEY_ID -123321
typedef struct //消息结构体
{
long types; //指定消息的类型
int cmd; //消息的命令
int param1; //参数1
int param2; //参数2
int ret; //参数3,可用于返回值
}msgq_t;
enum API_CMD_types{
eAPI_LEDSET_CMD = 0, //设置led
eAPI_LEDGET_CMD, //获取led状态
eAPI_LEDSETALL_CMD, //设置所有的led
eAPI_LEDGETALL_CMD, //获取所有的led
eAPI_BTNGET_CMD, //获取按键(没有使用)
eAPI_BTNEVENT_CMD, //等待按键事件(没有使用)
eAPI_LCDONOFF_CMD, //lcd开启关闭事件
eAPI_LEDSETPWM_CMD, //led设置亮度
eAPI_BOART_TEMP_GET_CMD, //获得单片机的温度
eAPI_CHECK_APIRUN_CMD, //检测api是否已经运行
eAPI_HWTD_SETONOFF_CMD, //开门狗设置开关
eAPI_HWTD_FEED_CMD , //看门狗喂狗
eAPI_HWTD_SETTIMEOUT_CMD, //设置看门狗喂狗时间
eAPI_HWTD_GETTIMEOUT_CMD, //获取看门狗喂狗时间
eAPI_RESET_COREBOARD_CMD, //复位核心板
eAPI_RESET_LCD_CMD, //复位lcd 9211(复位引脚没有连通)
eAPI_RESET_LFBOARD_CMD, //复位底板,好像没有这个功能!!!
eAPI_MICCTRL_SETONOFF_CMD//, //控制底板mic_ctrl引脚的电平
};
//返回值0表示成功,其他表示失败
int msgq_init(void);
//返回值0表示成功,其他表示失败
int msgq_exit(void);
//接收消息
//参数 timeout_50ms 大于0时,表示非阻塞模式,等待的时间为timeout_50ms*50ms的值,等于0表示阻塞模式
// types 接收消息的类型
// msgbuf 接收消息的缓存,1次接收一条消息
//返回0表示成功,其他表示失败
int msgq_recv(long types,msgq_t *msgbuf,unsigned int timeout_50ms);
//发送消息,并且要等待应答。
参数 timeout 大于0时,设置接收应答超时时间,等于0表示阻塞模式
// ack_types 指定应答的类型,之前是TYPE_RECV+cmd
// msgbuf 发送消息的缓存,1次发送一条消息
//返回0表示成功,其他表示失败
//注意,函数使用时,需要指定msgbuf->types !!!!
int msgq_send(long ack_types,msgq_t *msgbuf,int timeout);
//发送应答消息消息,不等待应答。
int msgq_send_ack(msgq_t *msgbuf);
#endif