本文讨论的是system v版本的消息队列。
共享内存不涉及同步、互斥以及异步。System V是一种经典的UNIX进程间通信(IPC)机制,提供了一套API来支持进程之间的高效数据交换和同步。消息队列和信号量是其中的两个关键部分,它们各自解决了不同的通信和同步问题,但都基于System V的IPC框架。虽然System V IPC功能强大,但其接口较为复杂,现代系统中逐渐被POSIX IPC替代。
消息队列(Message Queue)是进程间通信(IPC)的一种方式,通过将消息存入内核维护的队列中,实现异步的进程数据传递。与管道不同,消息队列不仅允许不同大小的数据块传递,还支持消息的优先级排序,从而提供了更灵活的通信机制。
1.1 消息队列的特点
1.2 消息队列的核心概念
消息队列遍历消息时,存数据块还是取数据块,取决于数据块中的类型type。
1.3 消息队列的数据结构
我们使用man手册来查看,输入man msgctl。
消息队列的数据结构:
struct msqid_ds { struct ipc_perm msg_perm; /* Ownership and permissions */ time_t msg_stime; /* Time of last msgsnd(2) */ time_t msg_rtime; /* Time of last msgrcv(2) */ time_t msg_ctime; /* Time of creation or last modification by msgctl() */ unsigned long msg_cbytes; /* # of bytes in queue */ msgqnum_t msg_qnum; /* # number of messages in queue */ msglen_t msg_qbytes; /* Maximum # of bytes in queue */ pid_t msg_lspid; /* PID of last msgsnd(2) */ pid_t msg_lrpid; /* PID of last msgrcv(2) */ };
我们要注意类型为struct ipc_perm这个类型,该类型在共享内存的数据结构中也出现过。下面是它的具体内容:
struct ipc_perm { key_t __key; /* Key supplied to msgget(2) */ uid_t uid; /* Effective UID of owner */ gid_t gid; /* Effective GID of owner */ uid_t cuid; /* Effective UID of creator */ gid_t cgid; /* Effective GID of creator */ unsigned short mode; /* Permissions */ unsigned short __seq; /* Sequence number */ };
1.4 消息队列的使用
如果你已经学习过共享内存,那么消息队列的使用也一定会得心应手的。
1.4.1 创建消息队列
使用msgget函数来创建消息队列。msgget是用于创建或访问System V消息队列的系统调用。它根据指定的键值创建或获取一个消息队列标识符,用于后续的消息操作。
#include <sys> #include <sys> #include <sys> int msgget(key_t key, int msgflg);
使用方法和shmget高度相似。
参数说明:
返回值:
是不是和shmget相似,简直一模一样啊!如果你没有看过我的共享内存文章,推荐一看,会对你理解消息队列也是有帮助的哦~ 【Linux】「共享内存揭秘」:高效进程通信的终极指南-CSDN博客。
还是老样子,我们先创建一个共享区域:common.hpp。
#include <iostream> #include <sys> #include <sys> #include <sys> #include <assert.h> #define PATHNAME "./" #define PROJ_ID 0x3f const mode_t mode = 0666; key_t getKey(){ /** * :return 返回key值 * @return */ key_t ret = ftok(PATHNAME,PROJ_ID); assert(ret!=-1); return ret; } int creatMsg(){ /**:function 创建消息队列 * :return 返回msgid * @return */ int msgid = msgget(getKey(),IPC_CREAT|IPC_EXCL|mode); assert(msgid!=-1); return msgid; }
然后创建一个消息队列。
#include "common.hpp" int main(){ //创建消息队列 int msgid = creatMsg(); return 0; }
运行后,我们在命令行输入指令ipcs -q。
由于现在我们并没有使用消息队列,其used-bytes和消息数messages都是0。消息队列的生命周期也是跟随操作系统的,并不会因为进程的结束而结束。
1.4.2 释放消息队列
同共享内存一样,你可以通过指令或者程序中的函数来将消息队列释放。
释放指令:ipcrm -q msqid。
函数释放:msgctl。例行惯例,我们先在介绍msgctl。
#include <sys> #include <sys> #include <sys> int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数说明:
返回值:
只是释放消息队列,函数的使用就是套路了。
msgctl(msqid,IPC_RMID,nullptr);
1.4.3 使用消息队列发送消息
为了将数据发送到消息队列,现在需要的函数为msgsnd。msgsnd是用于向System V消息队列发送消息的系统调用。它将一条消息放入消息队列中,以实现进程间通信。
#include <sys> #include <sys> #include <sys> int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数说明:
返回值:
关于参数2,表示的是待发送的数据块,是一个结构体,我们需要自己定义。
struct msgbuf { long mtype; /* message type, must be > 0 */ char mtext[1]; /* message data */ };
1.4.4 使用消息队列接收消息
为了接收消息,同样我们还需要一个函数msgrcv。msgrcv是一个用于从System V消息队列中接收消息的系统调用。通过它,进程可以从指定的消息队列中读取一条符合条件的消息。
#include <sys> #include <sys> #include <sys> ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
参数说明:
struct msgbuf { long mtype; // 消息类型 char mtext[MSGSZ]; // 消息正文 };
common.hpp:
#include <iostream> #include <sys> #include <sys> #include <sys> #include <cstring> #include <unistd.h> #include <assert.h> #define PATHNAME "./" #define PROJ_ID 0x3f #define MSGSZ 128 const mode_t mode = 0666; key_t getKey(){ /** * :return 返回key值 * @return */ key_t ret = ftok(PATHNAME,PROJ_ID); assert(ret!=-1); return ret; } int creatMsg(){ /**:function 创建消息队列 * :return 返回msgid * @return */ int msgid = msgget(getKey(),IPC_CREAT|IPC_EXCL|mode); assert(msgid!=-1); return msgid; } int getMsg(){ /**:function 获取消息队列 * :return 返回msgid * @return */ int msgid = msgget(getKey(),IPC_CREAT); assert(msgid!=-1); return msgid; }
client.cc用于向server.cc发送消息。
/** * 用户端发送消息 */ #include "common.hpp" struct my_msgbuf{ long mtype; char mtext[MSGSZ];//MSGSZ为新定义的标识常量:128 }; int main(){ int msgid = creatMsg(); struct my_msgbuf msg; msg.mtype = 1;//设置消息类型 strcpy(msg.mtext,"hello,Message Queue!"); //开始发送消息 int n = msgsnd(msgid,&msg,strlen(msg.mtext)+1,0); assert(n!=-1); std::cout << "消息已发送:" << msg.mtext << std::endl; return 0; }
server.cc用于接收client.cc消息。
/** * 服务端接收消息 */ #include "common.hpp" struct my_msgbuf{ long mtype; char mtext[MSGSZ];//MSGSZ为新定义的标识常量:128 }; int main(){ int msgid = getMsg(); struct my_msgbuf msg; //开始接受信息 ssize_t n = msgrcv(msgid,&msg,MSGSZ,1,0); assert(n!=-1); std::cout << "接收到消息:" << msg.mtext << std::endl; return 0; }
执行效果:
消息队列的大部分接口都与共享内存类似,如果你使用过更现代的POSIX IPC,可能会觉得System V已经落后,事实上也确实是如此,System V已经过于老旧,现在用的很少,对于此版本,不需要太过深入了解,在实际情况中遇到,查查文档就是了。
以上就是【Linux】消息传递的艺术:探索Linux消息队列机制的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号