进程间的通讯:消息队列


前言

消息队列:
1、消息队列是进程或线程间通讯的其中一种方式。遵循先进先出的原则,保证了时间的顺序性。拥有该消息队列读权限的进程可以从消息队列读出数据,拥有该消息队列写权限的进程可以向消息队列发送数据。
2、消息作为节点一个一个地存放在消息队列里,可把消息队列比作信箱,消息比作依次顺序存放的信件。地址比作消息类型,内容为消息
支持双向传输,可以使用消息类型区分不同的消息。
3、任何不同的进程都可以进行通讯,不局限于父子进程的通讯
4、不足之处是消息的读写涉及数据拷贝,比较花费时间。

消息队列有两套机制:system v 和posix 前者移植性比较好,后者使用比较简单还可配合select机制使用、信号和线程通知。


一、System V 消息队列

1、创建或打开msgget()

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflg);
success:返回标志符          error:-1

key:对象的关键字 或者 IPC_PRIVATE私有队列
msgflg:
IPC_CREAT:如果消息队列对象不存在,则创建之,否则则进行打开操作
IPC_EXCL: 和IPC_CREAT 一起使用(用”|”连接),如果消息对象不存在则创建之,
			 否则产生一个错误并返回。

2、控制(删除)msgctl()

 int msgctl(int msqid, int cmd, struct msqid_ds *buf);
 
 msqid:消息队列的标志符
 cmd:
    IPC_STAT:把msgid_ds结构中的数据设置为消息队列的当前关联值,即用消息队列的当前
    		  关联值覆盖msgid_ds的值。
    IPC_SET:如果进程有足够的权限,就把消息列队的当前关联值设置为msgid_ds结构中给
    		出的值
    IPC_RMID:删除消息队列
buf: 指向msgid_ds结构的指针,删除时设为NULL

3、发送/接收消息 msgsnd()/msgrcv()

消息结构体:
发送和接受的消息要定义一个消息结构体,且结构体必须至少含有消息类型(long的整型):

struct msg 
{
	long mtype;
	char msg[100];
	...
};

发送:

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
success: 0   error: -1

msqid:消息队列返回的标志符
msgp:待发送消息的buf
msgsz:除去消息类型(mtype)的最大消息大小
msgflg:
	0:阻塞
	IPC_NOWAIT:不阻塞。当消息队列满时,会阻塞知道队列中有足够的空间来存放这条消息。
				但如果指定这个标识,那么会立即返回EAGAIN

接收:

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
                      int msgflg);
success:接收到的size(<= msgsz)
msgp:存放消息的buf
msgsz:除去消息类型(mtype)的最大消息大小
msgtyp:
	msgtyp = 0:获取消息队列中的第一条消息
	msgtyp > 0:获取类型为 msgtyp 的第一条消息,除非指定了 msgflg 为MSG_EXCEPT,
				这表示获取除了 msgtyp 类型以外的第一条消息。
	msgtyp < 0:按优先级队列最小获取类型 ≤|msgtyp|的第一条消息。
msgflg:
	如果为 0 表示没有消息就阻塞。
	IPC_NOWAIT:如果指定类型的消息不存在就立即返回,同时设置 errno 为 ENOMSG
	MSG_EXCEPT:仅用于 msgtyp > 0 的情况。表示获取类型不为 msgtyp 的消息
	MSG_NOERROR:如果消息数据正文内容大于 msgsz,就将消息数据截断为 msgsz

参考:https://www.jianshu.com/p/7e3045cf1ab8

4、demo.c

用消息队列write写消息 read读消息 read如果没消息队列会一直报错
read.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>   
#include <sys/msg.h>
#include <errno.h>
#include <unistd.h>

/*
消息队列比作信箱
消息比作信
消息类型比作信的目标地址
消息内容比作信的真实内容
ipcs -q
ipcrm -q msgid
*/
#define MSGKEY 1024   

struct msgstru  
{  
    long msgtype;  
    char msgtext[2048];   
};  

int main(int argc, char *argv[])
{
    struct msgstru msgs;  
    int msgid,ret_value;  
    char str[512];  

    printf( "This is a read_process\r\n");

    while(1){  
        msgid = msgget(MSGKEY,IPC_EXCL );/*检查消息队列是否存在 */  
        if(msgid < 0){  
            printf("msq not existed! errno=%d [%s]\n",errno,strerror(errno));  
            sleep(2);  
            continue;  
        } 
        
		/*接收消息队列 msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
		* msgtyp:从消息队列内读取的消息形态。如果值为零,
		* 则表示消息队列中的所有消息都会被读取
		* msgflg : 0 表示阻塞等待,IPC_NOWAIT不等待
		*/  
        ret_value = msgrcv(msgid,&msgs,sizeof(struct msgstru)-sizeof(long), 0, 0);  
        printf("read_process read_msg:ret_value=[%d],type=[%ld],text=[%s]\n",ret_value,msgs.msgtype, 
        msgs.msgtext);  
   }     
   return 0;  
}

write.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>   
#include <sys/msg.h>
#include <errno.h>

#define MSGKEY 1024   


struct msgstru  
{  
    long msgtype;  //消息类型type 的长度改不了
    char msgtext[2048];//消息内容可修改大小
};  

int main(int argc, char *argv[])
{ 
    struct msgstru msgs;  
    int msg_type;  
    char str[256];  
    int ret_value;  
    int msqid;  
    
    printf( "This is a write_process\n");  

    msqid=msgget(MSGKEY,IPC_EXCL);  /*检查消息队列是否存在*/  
    if(msqid < 0){  
         printf("msg not exist, so try to create one msg\r\n");
         msqid = msgget(MSGKEY,IPC_CREAT|0666);/*创建消息队列ipcs -q 显示消息队列的使用情况*/  
         if(msqid <0){  
         	printf("failed to create msq | errno=%d [%s]\n",errno,strerror(errno));  
         	return -1;
         }  
    }   
    
    while (1){  
        printf("input message type(end:0):");  
        scanf("%d",&msg_type);  
        if (msg_type == 0)  
            break;  
        printf("input message to be sent:");  
        scanf ("%s",str);  
        msgs.msgtype = msg_type;  
        strcpy(msgs.msgtext, str);  
		/* 发送消息队列 
		*  当msgflg为0时,msgsnd()及msgrcv()
		*  在队列呈满或呈空的情形时,采取阻塞等待的处理模式。IPC_NOWAIT
		*/  
        ret_value = msgsnd(msqid,&msgs,sizeof(struct msgstru)-sizeof(long),0); 
		printf("write_process=%d\r\n", ret_value);
        if ( ret_value < 0 ) {  
            printf("write_process write msg failed,errno=%d[%s]\n",errno,strerror(errno));  
            //return -1;
        }  
    }  
    printf("del the msg\r\n");
    msgctl(msqid,IPC_RMID,0); //删除消息队列   
    
   return 0;  
}

在这里插入图片描述

5、修改消息队列参数

1.永久修改
root用户下修改/etc/sysctl.conf 文件。
参数msgmax,msgmni,msgmnb都已经成功修改 更改的方法:
在配置文件/etc/sysctl.conf中加上 kernel.msgmax=value kernel.msgmni=value kernel.msgmnb=value 然后运行sysctl -p 即可进行修改

max queues system wide = // msgmni
max size of message (bytes) = //msgmax
default max size of queue (bytes) = //msgmnb

2.临时修改
root用户下sysctl -w kernel.msgmnb= 1048576

二、POSIX 消息队列

1.创建,关闭和删除

#include <mqueue.h>

mqd_t mq_open(const char *name, int oflag, mode_t mode,
					 struct mq_attr *attr );
成功返回消息队列描述符,失败返回-1					 
name:表示消息队列的名字,它符合POSIX IPC的名字规则。
oflag:表示打开的方式,和open函数的类似。有必须的选项:
					O_RDONLY,O_WRONLY,O_RDWR,
		还有可选的选项:O_NONBLOCK,O_CREAT,O_EXCL。
mode:是一个可选参数,在oflag中含有O_CREAT标志且消息队列不存在时,
	 才需要提供该参数。表示默认访问权限。可以参考open。
attr:也是一个可选参数,在oflag中含有O_CREAT标志且消息队列不存在时才需要。
	该参数用于给新队列设定某些属性,如果是空指针,那么就采用默认属性。


mqd_t mq_close(mqd_t mqdes);
用于关闭一个消息队列,和文件的close类型,关闭后,消息队列并不从系统中删除

mqd_t mq_unlink(const char *name);
用于删除一个消息队列。消息队列创建后只有通过调用该函数或者是内核自举才能进行删除。
成功返回0,失败返回-1



2.发送与接收


mqd_t mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, 
				unsigned msg_prio);
成功返回0,出错返回-1
 
mqd_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, 
					unsigned *msg_prio);
成功返回接收到消息的字节数,出错返回-1

#ifdef __USE_XOPEN2K
mqd_t mq_timedsend(mqd_t mqdes, const char *msg_ptr, size_t msg_len, 
			unsigned msg_prio, const struct timespec *abs_timeout);

mqd_t mq_timedreceive(mqd_t mqdes, char *msg_ptr, size_t msg_len, 
				unsigned *msg_prio, const struct timespec *abs_timeout);
#endif


mqdes:消息队列描述符;
msg_ptr:指向消息体缓冲区的指针;
msg_len:消息体的长度,其中mq_receive的该参数不能小于能写入队列中消息的最大大小,
		即一定要大于等于该队列的mq_attr结构中mq_msgsize的大小。如果mq_receive
		中的msg_len小于该值,就会返回EMSGSIZE错误。POXIS消息队列发送的消息长度
		可以为0。
msg_prio:消息的优先级;它是一个小于MQ_PRIO_MAX的数,数值越大,优先级越高。
		POSIX消息队列在调用mq_receive时总是返回队列中最高优先级的最早消息。
		如果消息不需要设定优先级,那么可以在mq_send是置msg_prio为0,mq_receive
		的msg_prio置为NULL。
abs_timeout:可超时的时间

转至https://www.cnblogs.com/WindSun/p/11438501.html

3.消息通知mq_notify()

https://blog.csdn.net/qq_35976351/article/details/87024570


  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值