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

进程通信(二)消息队列(System V 消息队列)

作者头像
lexingsen
发布于 2022-02-24 07:26:31
发布于 2022-02-24 07:26:31
2.8K00
代码可运行
举报
文章被收录于专栏:乐行僧的博客乐行僧的博客
运行总次数:0
代码可运行

一、System V 消息队列简介

消息队列:消息队列的本质是由Linux内核创建用于存放消息的链表,并且其功能是用来存放消息的,所以又称之为消息队列。 在Linux的不同进程中,包括有血缘的进程和无血缘的进程,都可以通过Linux消息队列API所得到的消息队列唯一标识符对消息队列进行操作。

二、分析消息队列的数据结构

Linux为了维护消息队列,为消息队列创建了数据结构,接下来我们来分析一下消息队列的结构以及消息队列节点的结构。

消息队列的结构:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct msqid_ds {
	struct ipc_perm msg_perm;   //消息队列的读写权限和所有者
	struct msg *msg_first;		//指向消息队列链表中的第一个节点
	struct msg *msg_last;		//指向消息队列链表中的最后一个节点
	long msg_stime;	            //最近一次从消息队列中取出消息的时间
	long msg_rtime;	            //最近一次从消息队列中放入消息的时间
	long msg_ctime;	            //最近一次修改消息队列的时间
	unsigned long  msg_lcbytes;	/* Reuse junk fields for 32 bit */
	unsigned long  msg_lqbytes;	/* ditto */
	unsigned short msg_cbytes;      //保存着队列总共占用内存的字节数
	unsigned short msg_qnum; 		//当前消息队列中消息的个数
	unsigned short msg_qbytes;	    //队列所占用内存的最大字节数。
	int msg_lspid;  //最近一次向消息队列发送消息进程的pid
	int msg_lrpid;	//最近一次从消息队列接受消息进程的pid
};

消息队列节点的结构:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct msg {
	struct msg *msg_next; //指向消息队列中下一个消息的指针
	long msg_type;//消息的类型
	char *msg_spot; //消息内容在内存中的地址
	time_t msg_stime; 
	short msg_ts;  //消息内容的长度
};

struct ipc_perm msg_perm的结构:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct ipc_perm
{
	__kernel_key_t	key;			//消息队列的key值
	__kernel_uid_t	uid;			//当前这一刻正在使用消息队列的用户
	__kernel_gid_t	gid;			//正在使用的用户所在的用户组
	__kernel_uid_t	cuid;		    //创建消息队列的用户
	__kernel_gid_t	cgid;		    //创建消息队列用户所在的用户组
	__kernel_mode_t	mode;           //读写权限
	unsigned short	seq;			//序列号,保证消息队列ID不会被
};

由上边的几个关于消息队列的结构体,我们可以大致画出消息队列的样子:

由上图可以直观的感受到Linux内核所设计消息队列的大致模样,这对于我们理解消息队列Linux API有着至关重要的作用。

三、消息队列API分析

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
msgget函数
1.int msgget(key_t key, int oflag);
返回值:唯一的标识符
关于参数key和参数oflag,请看下图,参考与《Unix网络编程2.进程通信》
key值的指定:
(1)指定为IPC_PRIVATE,每次调用msgget都会创建一个新的消息队列,这样的开销比较大,一般情况下,
只需要一个消息队列即可,因此此方法不建议使用。
(2)指定一个(key_t)value,这个value只要未使用即可,可以通过ipcs -q命令查看已有消息队列的key值,
以避免重复使用。
(3)使用ftok生成key。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
frok函数简介
#include <sys/types.h>
#include <sys/ipc.h>
key_t frok(const char  *filename, int id);
函数功能:ftok通过一个路径名和一个整型值,就可以返回一个唯一对应的key值。
只要路径名和整形数不变,key就不变。
filename+value(int) -> key

由于ftok只会使用整型数id的低八位,所以在使用时指定一个ascii码即可。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
msgsnd函数
2.int msgsnd(int msqid, const void *ptr, size_t length, int flag);
参数msqid是由msgget返回的消息队列的唯一标识符
const void* ptr是一个消息包的指针,消息包可以:
struct msgbuf{
	long mtype;//消息类型必须大于0
	char mtext[1];//1个字节肯定是不够用的,因此可以自定义消息包
};
length:消息内容的大小
flag:10:阻塞发送消息,如果没有发送成功时,该函数会一直阻塞,直到发送成功。
(2IPC_NOWAIT:非阻塞的方式发送消息,无论是否发送成功,函数都会返回,即使
发送不成功,函数也会返回。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
msgrcv函数
3.ssize_t msgrcv(int msqid, void *ptr, size_t length, long type, int flag);
参数ptr: 指定接受消息存放的位置。
参数length:指定了由ptr指向的缓冲区中数据部分的大小,这是该函数能返回的最大数据量,该长度不包含整型类型字段。
参数type:指定希望从所给定的队列中读出什么样的消息。
参数flag:指定所请求类型的消息不在指定的队列中应该作何处理。
(10:阻塞接受消息
(2IPC_NOWAIT:非阻塞接受消息,没有消息时,不阻塞。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
msgctl
4.int msgctl(int msqid, int cmd, struct msqid_ds* buff);
msgctl接口可以使得进程在msqid所标识的消息队列上进行各种控制操作。
msgctl的三个命令cmd参数:
(1IPC_RMID:从系统中删除由msqid指定的消息队列。此时第三个参数缺省省略,这个命令可以理解为删除消息队列。
(2IPC_SET,给所指定的消息队列设置其msqid_ds结构体的以下4个成员:msg_perm.uid, msg_perm.gid,msg_perm.mode和msg_gbytes。此时需要自己创建msqid_ds结构体并初始化然后传入。
(3IPC_STAT,此时buff作为传出参数可以获取消息队列消息头中msg_perm结构体的在内核中的内容。

四、代码实战

(1)有血缘关系的进程间通信

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


typedef struct msgbuf {
  long mtype;
  char mtext[128];
} MSG;


int main() {
  int msgid = msgget((key_t)1235, 0664|IPC_CREAT);
  assert(msgid != -1);
  pid_t pid = fork();
  assert(pid != -1);
  if (pid > 0) {
    MSG msg;
    memset(&msg, 0, sizeof(msg));
    msg.mtype = 100;
    strcpy(msg.mtext, "hello");
    msgsnd(msgid, &msg, strlen(msg.mtext), 0);
  } else if (pid == 0) {
    MSG msg;
    memset(&msg, 0, sizeof(msg));
    msgrcv(msgid, &msg, 127, 100, 0);
    printf("msg.mtype: %ld\n", msg.mtype);
    printf("msg.mtext: %s\n", msg.mtext);
  }
  return 0;
}

(2)无血缘关系的进程间通信 msg_send.c

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/msg.h>

typedef struct msgdata{
  long mtype; //消息类型
  char mtext[128];
} MSG;


int main() {
  int id = msgget((key_t)1234, IPC_CREAT | 0664);
  assert(id != -1);

  MSG msg;
  memset(&msg, 0, sizeof(msg));
  msg.mtype = 100;
  strcpy(msg.mtext, "hello world");
  msgsnd(id, &msg, strlen(msg.mtext), 0);
 
  memset(&msg, 0, sizeof(msg));
  msg.mtype = 200;
  strcpy(msg.mtext, "ni hao, shi jian");
  msgsnd(id, &msg, strlen(msg.mtext), 0);
  return 0;
}

msg_send.c

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



typedef struct msgdata{
  long mtype;
  char mtext[128];
}MSG;

int main() {
  int id = msgget((key_t)1234, 0664|IPC_CREAT);
  assert(id != -1);

  MSG msg;
  memset(&msg, 0, sizeof(msg));

  msgrcv(id, &msg, 127, 200, 0);
  printf("msg.mtype: %ld\n", msg.mtype);
  printf("msg.mtext: %s\n", msg.mtext);
  return 0;
}

(3)删除消息队列内核对象

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
使用msgctl来删除消息队列内核对象

void msgRemove(int msgid) {
	int res = msgctl(msgid, IPC_RMID, NULL);
	if (res == -1) return -1;
	else return 0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
system V消息队列
1.消息队列 1)消息队列提供了一个从进程向另外一个进程发送一块是数据的方法 2)每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型 不足之处: 每个消息的最大长度是有限制的。MSGMAX 每个消息队列的总的字节数也是有上限。MSGMNB 系统上消息队列的总数也有一个上限。MSGMNI 可以这样查看这三个限制:
xcywt
2022/05/09
5270
system V消息队列
UNIX(进程间通信):10 消息队列
1.消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识. 2.消息队列允许一个或多个进程向它写入与读取消息. 3.管道和命名管道都是通信数据都是先进先出的原则。 4.消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比FIFO更有优势。
用户3479834
2021/03/04
1.1K0
Linux消息队列及函数
消息队列就是一个消息的链表,每个消息队列都有一个队列头,用结构struct msg_queue来描述。队列头中包含了该队列的大量信息,包括消息队列的键值、用户ID、组ID、消息数目、读写进程ID等。其定义如下:
xxpcb
2020/08/04
5K0
【Linux系统】进程间通信-System V消息队列
也就是说,当前面一个创建了一个消息队列,但是没有进行销毁,最后进程也结束了。当下次新的进程再次创建的时候,就会出错。
用户11396661
2025/04/03
1400
【Linux系统】进程间通信-System V消息队列
【Linux】消息传递的艺术:探索Linux消息队列机制
共享内存没有进行同步于互斥以及异步 System V 是一种经典的 UNIX 进程间通信(IPC)机制,提供了一套 API 来支持进程之间的高效数据交换和同步。消息队列 和 信号量 是其中的两个关键部分,它们各自解决了不同的通信和同步问题,但都基于 System V 的 IPC 框架。 虽然 System V IPC 功能强大,但其接口较为复杂,现代系统中逐渐被 POSIX IPC 替代。
Yui_
2024/12/20
4980
【Linux】消息传递的艺术:探索Linux消息队列机制
UNPv2第六章:System V 消息队列
返回值是一个整数标识符,其他三个msg函数就用它来指代该队列。它是基于指定的key产生的,而key既可以是ftok返回值,也可以是IPC_PRIVATE。 参数oflag可以为以下:IPC_CREAT、IPC_EXCL、IPC_NOWAIT或三者的或结果。
提莫队长
2019/02/21
4670
Linux进程间通信 消息队列
简单理解,消息队列就是一堆消息的有序集合,并缓存于内核中。如此一来,多个进程就可通过访问内核来实现多个进程之间的通信。目前存在的消息队列有POSIX与System V标准的接口,本篇主要介绍System V接口的使用。
开源519
2021/07/30
4.8K0
Linux进程间通信 消息队列
Linux系统编程-进程间通信(消息队列)
前面文章介绍了Linux下进程的创建,管理,陆续介绍了进程间通信的方式:管道、内存映射、共享内存等。这篇文章继续介绍Linux的进程间通信方式消息队列。
DS小龙哥
2022/04/08
2K0
Linux系统编程-进程间通信(消息队列)
Linux内核编程--消息队列
IPC的意思是“ 进程间通信机制”,Linux内核有三种常用IPC对象可以拿来做进程间通信--消息队列,共享内存,信号量。这三种IPC对象在Linux内核中都以链表的形式存储,它们都有特定的ID来标识(消息队列标识符msqid、共享内存标识符shmid,信号量标识符semid)。
Coder-ZZ
2022/05/09
4.8K0
Linux内核编程--消息队列
【Linux】责任链模式和消息队列
其实之前在 【Linux】 IPC 进程间通信(三)(消息队列 & 信号量) 也了解过相关知识,这里的话只是做个补充
IsLand1314
2025/02/23
2580
【Linux】责任链模式和消息队列
【Linux】 IPC 进程间通信(三)(消息队列 & 信号量)
🔥 消息队列(Message Queue) 是一种进程间通信(IPC)机制,它允许不同进程或线程之间通过发送和接收消息来交换数据。
IsLand1314
2024/11/19
8620
【Linux】 IPC 进程间通信(三)(消息队列 & 信号量)
【Linux】system V消息队列,信号量
消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法 每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值
用户11029103
2025/03/19
1660
【Linux】system V消息队列,信号量
Linux 的进程间通信:消息队列
本文主要探讨了Linux消息队列的发送、接收以及异步通知机制。首先介绍了消息队列的发送和接收过程,然后详细描述了异步通知的方式,最后通过一个示例展示了如何使用epoll机制实现异步通知。
邹立巍
2017/07/24
13.9K0
linux网络编程之System V 消息队列(一):消息队列内核结构和msgget、msgctl 函数
s1mba
2017/12/28
2K0
linux网络编程之System V 消息队列(一):消息队列内核结构和msgget、msgctl 函数
【Linux】systemV消息队列和信号量
操作系统在内核建立一个队列,通信的两个进程AB以数据块的形式将需要发送的数据pushback到队列中,数据块是一个结构体,其中有字段标识该数据块是谁发送的,所以我们只要让不同的进程看到同一个队列就可以了
s-little-monster
2025/03/21
1410
Linux进程间通信(二) - 消息队列
消息队列 消息队列是Linux IPC中很常用的一种通信方式,它通常用来在不同进程间发送特定格式的消息数据。 消息队列和之前讨论过的管道和FIFO有很大的区别,主要有以下两点(管道请查阅我的另一篇文章:https://cloud.tencent.com/developer/article/1021159): Ø 一个进程向消息队列写入消息之前,并不需要某个进程在该队列上等待该消息的到达,而管道和FIFO是相反的,进程向其中写消息时,管道和FIFO必须已经打开来读,否则写进程就会阻塞(默认情况下)。 Ø IP
三丰SanFeng
2018/01/16
6.4K1
进程间通讯(五).message queue(2)
key_t <= __KEY_T_TYPE <= __S32_TYPE <= int
franket
2021/09/15
8700
Linux进程间通信【消息队列、信号量】
在 System V 通信标准中,还有一种通信方式:消息队列,以及一种实现互斥的工具:信号量;随着时代的发展,这些陈旧的标准都已经较少使用了,但作为 IPC 中的经典知识,我们可以对其做一个简单了解,扩展 IPC 的知识栈,尤其是 信号量,可以通过它,为以后多线程学习中 POSIX 信号量的学习做铺垫
北 海
2023/07/01
8260
Linux进程间通信【消息队列、信号量】
Linux系统编程——进程间通信:消息队列
对于消息队列的操作,我们可以类比为这么一个过程:假如 A 有个东西要给 B,因为某些原因 A 不能当面直接给 B,这时候他们需要借助第三方托管(如银行),A 找到某个具体地址的建设银行,然后把东西放到某个保险柜里(如 1 号保险柜),对于 B 而言,要想成功取出 A 的东西,必须保证去同一地址的同一间银行取东西,而且只有 1 号保险柜的东西才是 A 给自己的。
马修
2021/01/21
1.5K0
Linux系统编程——进程间通信:消息队列
【Linux】system V进程间通信——共享内存、消息队列、信号量
进程具有独立性:内核数据结构包括对应的代码、数据与页表都是独立的。OS系统为了让进程间进行通信:1.申请一块空间 2.将创建好的内存映射进进程的地址空间。共享内存让不同的进程看到同一份的资源就是在物理内存上申请一块内存空间,如何将创建好的内存分别与各个进程的页表之间建立映射,然后在虚拟地址空间中将虚拟地址填充到各自页表的对应位置,建立起物理地址与虚拟地址的联系。
平凡的人1
2023/10/15
4250
【Linux】system V进程间通信——共享内存、消息队列、信号量
相关推荐
system V消息队列
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验