linux进程间通信之msgq

这次项目中关于进程间通信的部分我一般都采用了msgq,一般信息数据量都不是很大。

一、简述一下项目的需求部分:

1. api与server端进行相互通信。

2. server对不同的api接口做出应答,并且api能够识别出是应答哪个接口。

3.api等待超时,这里考虑到server端故障或者已经退出等等异常情况,api应该采取一些措施,避免死等而造成假死现象。

4.代码原文,请翻到文末。

5.本代码在api端和server端都可以使用

项目完整代码,请参考github

二、代码分步讲解

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

大智兄

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

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

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

打赏作者

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

抵扣说明:

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

余额充值