前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【Linux系统】进程间通信-System V消息队列

【Linux系统】进程间通信-System V消息队列

作者头像
用户11396661
发布于 2025-04-03 00:50:44
发布于 2025-04-03 00:50:44
11800
代码可运行
举报
文章被收录于专栏:C++开发C++开发
运行总次数:0
代码可运行

1.进程间通信(IPC:Inter-Process Communication

常见的IPC方式:

匿名管道,命名管道,共享内存,消息队列,信号量。

System -V 消息队列

生命周期随内核,不随进程。

也就是说,当前面一个创建了一个消息队列,但是没有进行销毁,最后进程也结束了。当下次新的进程再次创建的时候,就会出错。

消息队列的销毁:1.命名销毁。 2.msgctl。 3.系统重启。

需要注意的是,如果在编程过程中,虽然写了msgctl进行销毁,如果提前终止了进程,消息队列也是没有进行销毁的。

下面会对这些函数进行封装。包括msgqueue类,客户端,服务端。


2.MSGGET函数

函数原型:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflg);

成功返回消息队列唯一标识符。

错误返回-1。

功能:

官方说明: 获取一个System V 消息队列标识符(get a System V message queue identifier)。类型为int

也就是说,通过这个函数,可以创建一个消息队列,然后返回一个消息队列标识符(这个是给用户态唯一确定的标识符,在内核态是通过key来唯一定位一个标识符)。

参数key

The msgget() system call returns the System V message queue identifier associated with the value of the key argument.

msgget通过key去创建消息队列,key值我们可以通过ftok函数去创建。(下面会讲解)。

参数msgflag

IPC_CREATE:如果key对应的消息队列不存在则创建,如果存在就获取它的ID。

IPC_CREATE|IPC_EXCL_:两个一起使用,唯一的不同就是如果存在则出错。

另外可以加上权限:

如果权限设置为0666,系统的umask为022,最终的权限就是0666&(~022)=0644.

权限是八进制的,打印的时候,要记得按八进制打印更方便观察。第1位是特殊标记位,未启用。

3.ftok函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <sys/types.h>
#include <sys/ipc.h>

key_t ftok(const char *pathname, int proj_id);

pathname是文件路径,proj_id的值是0-255的值(也就是取int的低8位)。

然后回通过pathname和proj_id唯一创建一个key_t类型的键值。

如果创建失败,可以更换文件路径或者proj_id进行重新尝试。


3.msgctl函数

msgctl函数用于对System V消息队列进行控制操作,如获取消息队列的状态、设置消息队列的属性或删除消息队列。

函数原型

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

参数msqid

这个就是上面msgget函数的返回值,消息队列的标识符。

参数cmd

这个就是要进行哪种操作。

IPC_STAT

获取消息队列的状态信息

IPC_SET

设置属性

IPC_RMID

删除消息队列

buf:指向msqid_ds结构体的指针,用于获取或设置消息队列的信息。对于IPC_STATIPC_SETbuf必须是一个有效的指针;对于IPC_RMIDbuf可以是NULL。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct msqid_ds {
    struct ipc_perm msg_perm;    // 权限信息
    struct msg *msg_first;       // 消息队列中的第一个消息
    struct msg *msg_last;        // 消息队列中的最后一个消息
    ulong msg_cbytes;            // 消息队列中当前的总字节数
    ulong msg_qnum;              // 消息队列中当前的总消息数
    ulong msg_qbytes;            // 消息队列中最大可容纳的字节数
    pid_t msg_lspid;             // 最近一个执行`msgsnd`的进程PID
    pid_t msg_lrpid;             // 最近一个执行`msgrcv`的进程PID
    time_t msg_stime;            // 最近一次执行`msgsnd`的时间
    time_t msg_rtime;            // 最近一次执行`msgrcv`的时间
    time_t msg_ctime;            // 最近一次改变消息队列的时间
};
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <cstring>
#include <string>
#include <iomanip>


int main()
{
    key_t key=ftok("./",123);
    int msgid=msgget(key,IPC_CREAT|0666);
    if(-1==msgid){
        std::cout<<"创建消息队列失败!"<<std::endl;
    }
    struct msqid_ds ds;
    msgctl(msgid,IPC_STAT,&ds);

    printf("Mode: %04o\n", ds.msg_perm.mode);
    std::cout<<std::setw(4)<<std::setfill('0')<<std::oct<<ds.msg_perm.mode<<std::endl;
    
    getchar();
    msgctl(msgid,IPC_RMID,0);
    return 0;
}

删除消息队列

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bool destroy(){
    int n=msgctl(_msgfd,IPC_RMID,0);
    if(-1==n){
        std::cerr<<"msgctl error"<<std::endl;
        return false;
    }
    std::cout<<"destroy success"<<std::endl;
    return true;
}

4.msgsnd函数

函数原型

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

msqid参数

消息队列唯一标识符

msgp参数

必须满足第一个为long类型,其他的是数据部分。可以设置为如下类型:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct mymsg {
    long mtype;  // 消息类型
    char mtext[1024];  // 消息数据
};

msgsz参数

这是消息数据的大小,不包括long类型的mtype大小。

msgflag参数

IPC_NOWAIT:如果消息队列已满,则立即返回,而不是阻塞等待

0:默认行为,如果消息队列已满,则阻塞等待,直到消息被发送或超时


5.msgrcv函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);

msgid,msgp,msgsz和上面的一样。

msgtyp参数

消息的类型,用于选择性接收消息:

0

收队列中第一个消息。

正整数

接收类型等于 msgtyp 的消息

负整数

接收类型小于或等于 abs(msgtyp) 的消息

通过这个参数,可以设置优先级,看先读取哪个,如果失败,再读取哪些。

msgflag参数

IPC_NOWAIT:如果队列中没有符合条件的消息,则立即返回,而不是阻塞等待。

MSG_NOERROR:如果消息的大小超过 msgsz,则截断消息(只接收前 msgsz 字节)。

返回值

成功:返回接收到的消息数据部分的大小(以字节为单位)。

失败:返回 -1,并设置 errno 以指示错误。


MsgQueue类封装

对于客户端,只进行消息队列的获取,不进行创建,而服务端确确实实要进行创建一个新的消息队列。服务端msgget就设置参数为IPC_CREATE|IPC_EXCL|0666。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#pragma once

#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <cstring>
#include <string>

#define PATH_NAME "/tmp"
#define PROJ_ID 0x12
#define GLOBAL_MSG_FD -1
#define MTEXT_SIZE 128
#define CLIEN_TYPE 1
#define SERVER_TYPE 2

namespace msgqueue{
    class MsgQueue{
        public:
            //定义类型结构体
            struct MsgBuff{
                long mtype;
                char mtext[MTEXT_SIZE];
            };
    
            MsgQueue():_msgfd(GLOBAL_MSG_FD){}
            //选项由外部决定
            bool create(int flag){
                key_t key=ftok(PATH_NAME,PROJ_ID);
                if(-1==key){
                    std::cerr<<"key error"<<std::endl;
                    return false;
                }
                _msgfd=msgget(key,flag);
                if(-1==_msgfd){
                    std::cerr<<"msgget error"<<std::endl;
                    return false;
                }
                std::cout<<"create success!msgid:"<<_msgfd<<" key:"<<key<<std::endl;
                return true;
            }
            bool destroy(){
                int n=msgctl(_msgfd,IPC_RMID,0);
                if(-1==n){
                    std::cerr<<"msgctl error"<<std::endl;
                    return false;
                }
                std::cout<<"destroy success"<<std::endl;
                return true;
            }
            bool send(int mtype,std::string& mtext){
                MsgBuff msgbuff;
                memset(&msgbuff,0,sizeof(msgbuff));
                msgbuff.mtype=mtype; 
                memcpy(&msgbuff.mtext,mtext.c_str(),mtext.size());    
                //发送数据
                int n=msgsnd(_msgfd,&msgbuff,mtext.size(),0);
                if(-1==n){
                    std::cerr<<"msgsnd error"<<std::endl;
                    return false;
                }
                return true;
            }
    
            bool recv(int mtype,std::string& mtext){
                MsgBuff msgbuff;
                memset(msgbuff.mtext,0,sizeof(msgbuff.mtext));
                size_t end=msgrcv(_msgfd,&msgbuff,MTEXT_SIZE-1,mtype,0);
                if(-1==end){
                    std::cerr<<"msgrcv error"<<std::endl;
                    return false;
                }
                mtext=msgbuff.mtext;
                return true;
            }
        private:
            int _msgfd;
    };

    class Server:public MsgQueue{
        public:
            Server(){
                if(false==create(IPC_CREAT|IPC_EXCL|0666)){
                    std::cerr<<"create error"<<std::endl;
                }
            }
            ~Server(){
                if(false==destroy()){
                    std::cerr<<"destroy error"<<std::endl;
                }
            }

    };

    class Client:public MsgQueue{
        public:
            Client(){
                //只进行获取,不进行创建,若干不存在,则出错
                if(false==create(IPC_CREAT)){
                    std::cerr<<"create error"<<std::endl;
                }
            }
            ~Client(){
            }
    };
}

客户端:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include "msgqueue.hpp"

int main()
{
    msgqueue::Client client;
    while(1){
        std::string s;
        std::cout<<"请输入发送的信息:";
        std::cin>>s;
        client.send(CLIEN_TYPE,s);
        if("quit"==s){
            break;
        }
    } 
}

服务端:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include "msgqueue.hpp"

int main()
{
    msgqueue::Server server;
    while(1){
        std::string s;
        server.recv(CLIEN_TYPE,s);
        if("quit"==s){
            break;
        }
        std::cout<<s<<std::endl;
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-04-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Linux系统编程——进程间通信:消息队列
对于消息队列的操作,我们可以类比为这么一个过程:假如 A 有个东西要给 B,因为某些原因 A 不能当面直接给 B,这时候他们需要借助第三方托管(如银行),A 找到某个具体地址的建设银行,然后把东西放到某个保险柜里(如 1 号保险柜),对于 B 而言,要想成功取出 A 的东西,必须保证去同一地址的同一间银行取东西,而且只有 1 号保险柜的东西才是 A 给自己的。
马修
2021/01/21
1.5K0
Linux系统编程——进程间通信:消息队列
【Linux】责任链模式和消息队列
其实之前在 【Linux】 IPC 进程间通信(三)(消息队列 & 信号量) 也了解过相关知识,这里的话只是做个补充
IsLand1314
2025/02/23
2180
【Linux】责任链模式和消息队列
【Linux】system V消息队列,信号量
消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法 每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值
用户11029103
2025/03/19
1520
【Linux】system V消息队列,信号量
System V通信
之前已经讲了通过管道来进行进程间通信,匿名管道是通过子进程继承父进程的文件描述符表来使两个进程看到同一份匿名管道文件实现的,有名管道是通过文件名作为唯一标识来使两个毫不相干的进程看到同一份资源。管道通信是基于文件系统的通信方式。而System V是操作系统提供的聚焦于本地通信的通信方式,本文介绍System V主要是介绍共享内存这种通信方式。
始终学不会
2023/10/17
1700
System V通信
system V消息队列
1.消息队列 1)消息队列提供了一个从进程向另外一个进程发送一块是数据的方法 2)每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型 不足之处: 每个消息的最大长度是有限制的。MSGMAX 每个消息队列的总的字节数也是有上限。MSGMNB 系统上消息队列的总数也有一个上限。MSGMNI 可以这样查看这三个限制:
xcywt
2022/05/09
5230
system V消息队列
Linux进程间通信(上)之管道、消息队列实践
进程间通信的几种方式:无名管道、有名管道、消息队列、共享内存、信号、信号量、套接字(socket)。
杨源鑫
2020/09/04
2.5K0
Linux进程间通信(上)之管道、消息队列实践
【操作系统】进程间的通信——消息队列
函数原型:int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
半生瓜的blog
2023/05/13
5610
【操作系统】进程间的通信——消息队列
Linux系统编程-进程间通信(消息队列)
前面文章介绍了Linux下进程的创建,管理,陆续介绍了进程间通信的方式:管道、内存映射、共享内存等。这篇文章继续介绍Linux的进程间通信方式消息队列。
DS小龙哥
2022/04/08
1.9K0
Linux系统编程-进程间通信(消息队列)
Linux之进程间通信——system V(共享内存、消息队列、信号量等)
本文介绍了另一种进程间通信——system V,主要介绍了共享内存,消息队列、信号量,当然消息队列了信号量并非重点,简单了解即可。
摘星
2023/10/15
6680
Linux之进程间通信——system V(共享内存、消息队列、信号量等)
进程间通信—管道,共享内存,消息队列,信号量
在操作系统中进程具有独立性,那么进程之间进行通信必然成本不低。那么进程间通信方式有哪些呢?
梨_萍
2023/06/01
2.1K0
进程间通信—管道,共享内存,消息队列,信号量
Linux消息队列及函数
消息队列就是一个消息的链表,每个消息队列都有一个队列头,用结构struct msg_queue来描述。队列头中包含了该队列的大量信息,包括消息队列的键值、用户ID、组ID、消息数目、读写进程ID等。其定义如下:
xxpcb
2020/08/04
4.9K0
进程通信(二)消息队列(System V 消息队列)
消息队列:消息队列的本质是由Linux内核创建用于存放消息的链表,并且其功能是用来存放消息的,所以又称之为消息队列。 在Linux的不同进程中,包括有血缘的进程和无血缘的进程,都可以通过Linux消息队列API所得到的消息队列唯一标识符对消息队列进行操作。
lexingsen
2022/02/24
2.8K0
进程通信(二)消息队列(System V 消息队列)
UNPv2第六章:System V 消息队列
返回值是一个整数标识符,其他三个msg函数就用它来指代该队列。它是基于指定的key产生的,而key既可以是ftok返回值,也可以是IPC_PRIVATE。 参数oflag可以为以下:IPC_CREAT、IPC_EXCL、IPC_NOWAIT或三者的或结果。
提莫队长
2019/02/21
4660
linux网络编程之System V 消息队列(二):消息队列实现回射客户/服务器和 msgsnd、msgrcv 函数
本文讲述通过消息队列实现回射客户/服务器模式和基于消息队列的RPC调用,以及如何使用libevent库进行高效的异步读写操作。同时,针对可能存在死锁的问题,提出了多开私有队列的解决方案。
s1mba
2017/12/28
1.7K0
linux网络编程之System V 消息队列(二):消息队列实现回射客户/服务器和 msgsnd、msgrcv 函数
Linux进程间通信【消息队列、信号量】
在 System V 通信标准中,还有一种通信方式:消息队列,以及一种实现互斥的工具:信号量;随着时代的发展,这些陈旧的标准都已经较少使用了,但作为 IPC 中的经典知识,我们可以对其做一个简单了解,扩展 IPC 的知识栈,尤其是 信号量,可以通过它,为以后多线程学习中 POSIX 信号量的学习做铺垫
北 海
2023/07/01
7620
Linux进程间通信【消息队列、信号量】
进程间通讯(五).message queue(2)
key_t <= __KEY_T_TYPE <= __S32_TYPE <= int
franket
2021/09/15
8640
Linux进程间通信 消息队列
简单理解,消息队列就是一堆消息的有序集合,并缓存于内核中。如此一来,多个进程就可通过访问内核来实现多个进程之间的通信。目前存在的消息队列有POSIX与System V标准的接口,本篇主要介绍System V接口的使用。
开源519
2021/07/30
4.8K0
Linux进程间通信 消息队列
【Linux】 IPC 进程间通信(三)(消息队列 & 信号量)
🔥 消息队列(Message Queue) 是一种进程间通信(IPC)机制,它允许不同进程或线程之间通过发送和接收消息来交换数据。
IsLand1314
2024/11/19
7420
【Linux】 IPC 进程间通信(三)(消息队列 & 信号量)
UNIX(进程间通信):10 消息队列
1.消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识. 2.消息队列允许一个或多个进程向它写入与读取消息. 3.管道和命名管道都是通信数据都是先进先出的原则。 4.消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比FIFO更有优势。
用户3479834
2021/03/04
1.1K0
Linux:进程间通信(二.共享内存详细讲解以及小项目使用和相关指令、消息队列、信号量)
Linux:进程间通信(二.共享内存详细讲解以及小项目使用和相关指令、消息队列、信号量)
是Nero哦
2024/07/13
3860
Linux:进程间通信(二.共享内存详细讲解以及小项目使用和相关指令、消息队列、信号量)
推荐阅读
相关推荐
Linux系统编程——进程间通信:消息队列
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验