Linux - 进程间通信(IPC)消息队列
1. 什么是消息队列?
消息队列(Message Queue,简称MQ)是由内核管理的消息链接表,由消息队列标识符标识,标识符简称队列ID。消息队列提供了进程之间单向传送数据的方法,每个消息包含有一个正的长整型类型的数据段、一个非负的长度以及实际数据字节数(对应于长度),消息队列总字节数是有上限的,系统上消息队列总数也有上限。
MQ传递的是消息,也就是进程间需要传递的数据,系统内核中有很多MQ,这些MQ采用链表实现并由系统内核维护,每个MQ用消息队列描述符(qid)来区分,每个MQ 的pid具有唯一性。如下图:在进程间通信时,一个进程A将消息加到由内核管理的MQ 末端,另一个进程B在MQ中获取消息(获取信息时不遵循先进先出的规则,也可以按照消息类型字段获取消息):
2. 消息队列使用的函数
消息队列(MQ)在进程间通信的操作分为以下四个步骤:
1、创建MQ;
2、发送message;
3、接收message;
4、删除MQ.
所需函数如下:
2.1 ftok () 函数
key_t ftok(const char **pathname,int proj_id);
该函数是根据pathname指定的文件或目录的索引节点号和proj_id计算并返回一个key_t类型的ID值,失败则返回-1;
第一个参数pathname是一个系统中必须存在的文件或文件夹的路径,会使用该文件的索引节点;
第二个参数prij_id是用户指定的一个子序号,这个数字有的称之为project ID,它是一个8bit的整数,取值范围为1~255.
注意:如果要确保key值不变,要么确保fork()的文件不会被删除,要么不用fork()指定一个固定的key值。
2.2 msgget () 函数
功能:创建一个新队列或者打开一个现有队列,成功返回一个非负整数,即该共享内存段的标识码,失败返回-1;
函数原型:
#include<sys/msg.h>
int msgget(key_t key,int msgflg);
说明:
key是fork()返回的key_t类型键值;
msgflg是创建标志:IPC_CREAT,不存在则创建,存在则返回已有的mqid,IPC_CREAT|IPC_EXCL,不存在则创建,存在则返回出错。
2.3 msgsnd () 函数
功能:将新消息添加到队列尾端,也就是发送一条消息,必须要有写消息队列的权限。成功返回0;失败返回-1;并设置errno.
函数原型:
#include<sys/msg.h>
int msgsnd(int msqid,const void *ptr,size_t msgsz,int magflg);
说明
msgid 是由msgget函数返回的消息队列 ID;
ptr是一个指针,它指向要发送的消息结构体类型的变量,消息结构在两方面受到制约。首先,它必须小于系统规定的上限值;其次,它必须以一个long int 长整数开始,接收者函数将利用这个长整数确定消息的类型,其参考类型定义如下:1
typedef struct s_msgbuf
{
long mtype; /*positive message type */
char mtext[512]; /*message data,of length nbytes */
}t_msgbuf;
msgsz是要发送消息的长度;
msgflg控制着当前消息队列已满或系统上限时将要发生的事情,设置为IPC_NOWAIT表示队列已满不等待,返回EAGAIN错误。
2.4 msgrcv () 函数
功能:用于从队列中取消息,成功返回实际放到接收缓冲区里去的个数,失败返回-1;并设置errno.
函数原型:
#include<sys/msg.h>
ssize_t msgrcv(int msqid,void *ptr,size_t msgsz,long msgtyp,int msgflg);
参数说明:
msgid是由msgget 函数返回的消息队列ID;
ptr是一个指针,它指向准备接收的消息;
msgsz是msgp指向的消息长度,这个长度不含保存消息类型的long int 长整型;
msgtype是消息的类型,它可以指定想要哪一种消息,可以实现接收优先级的简单形式;;
type=0 返回队列中的第一个值;
type>0 返回队列中消息类型为type的第一个消息;
type<0 返回队列中消息类型值≤type绝对值的消息,如果这种消息有若干个,则取类型值最小的消息。
type 值非0 用于以非先进先出次序读取消息,程序若给消息赋予优先权,则type就可以是优先权值,如果一个消息队列由多个客户进程和一个服务器进程使用,则type字段可以用来包含客户进程的进程ID(前提是ID可以存放在长整型中)。
2.5 msgctl () 函数
功能:msgctl函数对队列执行多种操作,用于控制消息队列。它和进程间通信的另外两种方法(信号量和共享存储)中的函数(semctl和shmctl)都是类似于ioctl函数(也叫垃圾桶函数)。
函数原型:
#include<sys/msg.h>
int msgctl(int msqid,int cmd,struct msqid_ds *buf);
msgid:msgget 函数返回的消息队列ID;
cmd:该参数指定对msqid指定的队列要执行的命令,可取一下三个值:
IPC_STAT:取msqid_ds结构,并将它存放在buf 指向的结构中;
IPC_SET:将字段msg_perm.uid、msg_perm.gid、msg_perm.mode和msg_qbytes从buf指向的结构复制到与 这个队列相关的msqid_ds结构中;
IPC_RMID:从系统中删除该消息队列以及仍在该队列中的所有数据。
3. 代码演示
消息队列函数的原型:
// 创建或打开消息队列:成功返回队列ID,失败返回-1
int msgget(key_t key, int flag);
// 添加消息:成功返回0,失败返回-1
int msgsnd(int msqid, const void *ptr, size_t size, int flag);
// 读取消息:成功返回消息数据的长度,失败返回-1
int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
// 控制消息队列:成功返回0,失败返回-1
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msgSend.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
// int msgget(key_t key, int msgflg);
// int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
// ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
struct msgbuf{
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main()
{
struct msgbuf sendbuf={888,"message from send"};
struct msgbuf readbuf;
key_t key;
if((key = ftok(".",'z')) < 0){
printf("ftok error\n");
}
int msgId = msgget(key,IPC_CREAT|0777);
if(msgId == -1){
printf("get quen failed\n");
}
msgsnd(msgId,&sendbuf,strlen(sendbuf.mtext),0);
printf("send over\n");
msgrcv(msgId,&readbuf,sizeof(readbuf.mtext),999,0);
printf("read from get is:%s\n",readbuf.mtext);
msgctl(msgId,IPC_RMID,NULL);
return 0;
}
msgGet.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
// int msgget(key_t key, int msgflg);
// int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
// ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
struct msgbuf{
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main()
{
struct msgbuf readbuf;
memset(readbuf.mtext, '\0', sizeof(readbuf.mtext));
struct msgbuf sendbuf={999,"thank for your reach"};
key_t key;
//获取key值
if((key = ftok(".",'z')) < 0){
printf("ftok error\n");
}
int msgId = msgget(key,IPC_CREAT|0777);
if(msgId == -1){
printf("get quen failed\n");
perror("why");
}
msgrcv(msgId,&readbuf,sizeof(readbuf.mtext),888,0);
printf("read from send is:%s\n",readbuf.mtext);
msgsnd(msgId,&sendbuf,strlen(sendbuf.mtext),0);
msgctl(msgId,IPC_RMID,NULL);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)