【原理剖析】4-进程间通信方式之消息队列

概述:知识点

  • Linux 消息队列方式原理介绍
  • Linux 消息队列相关 API 介绍

0x01 Linux 消息队列

消息队列 (也叫做报文队列)是 Unix 系统 V 版本中 3 种进程间通信机制之一。另外两种是信号量和共享内存。这些 IPC 机制使用共同的授权方法。只有通过系统调用将标志符传递给核心之后,进程才能存取这些资源。这种系统 IPC 对象使用的控制方法和文件系统非常类似。使用对象的引用标志符作为资源表中的索引。

消息队列就是一个消息的链表。就是把消息看作一个记录,并且这个记录具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读出消息。

Linux 采用消息队列的方式来实现消息传递。这种消息的发送方式是:发送方不必等待接收方检查它所收到的消息就可以继续工作下去,而接收方如果没有收到消息也不需等待。这种通信机制相对简单,但是应用程序使用起来就需要使用相对复杂的方式来应付了。新的消息总是放在队列的末尾,接收的时候并不总是从头来接收,可以从中间来接收。

消息队列是随内核持续的并和进程相关,只有在内核重起或者显示删除一个消息队列时,该消息队列才会真正被删除。因此系统中记录消息队列的数据结构 (struct ipc_ids msg_ids)位于内核中,系统中的所有消息队列都可以在结构 msg_ids 中中找到访问入口。

IPC 标识符:每一个 IPC 目标都有一个唯一的 IPC标识符 。这里所指的 IPC目标 是指一个单独的消息队列、一个信号量集或者一个共享的内存段。系统内核使用此标识符在系统内核中指明 IPC目标

IPC关键字 :想要获得唯一的标识符,则必须使用一个 IPC关键字 。客户端进程和服务器端进程必须双方都同意此关键字。这是建立一个客户机/服务器框架的第一步。在 System V IPC 机制中,建立两端联系的路由方法是和 IPC关键字 直接相关的。通过在应用程序中设置关键字值,每一次使用的关键字都可以是相同的。一般情况下,可以使用 ftok() 函数为客户端和服务器端产生关键字值。

0x02 Linux消息队列使用步骤

  • 创建或打开消息队列。使用的函数是 msgget(),这里创建的消息队列的数量会受到系统消息队列数量的限制。
  • 添加消息。使用的函数是 msgsnd(),它把消息添加到已打开的消息队列末尾。
  • 读取消息。使用的函数是 msgrcv(),它把消息从消息队列中取走,与 FIFO 不同的是,这里可以取走指定的某一条消息。
  • 控制消息队列。使用的函数是 msgctl(),它可以完成多项功能。

0x03 Linux消息队列相关 API 介绍

在 Linux 操作系统中,我们可以通过使用 msgget() 函数来创建和打开消息队列,具体描述如下:

1
2
3
4
5
6
7
8
9
10
11
12
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflg)
/*
参数:
- key 消息队列键值,多个进程可以通过这个 ID 值来访问同一个消息队列,其中有个特殊值 `IPC_PRIVATE` 用于创建当前进程的私有消息队列
- msgflg 权限标志位
返回值:
成功返回消息队列 ID, 失败返回 -1
*/

向已经被创建和打开的消息队列中发送数据,可以使用 msgsnd() 函数,具体描述如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#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 消息队列的队列 ID
- msgp 执行消息结构的指针,该消息结构 `msgbuf` 通常如下:
struct msgbuf{
long mtype; //消息类型
char mtext[1];//消息正文
}
- msgsz 消息正文的字节数
- msgflg 如下:
`IPC_NOWAIT`:若消息无法立即发生(当前消息队列已满,函数立即返回
0:msgsnd() 调用阻塞知道发送成功为止
函数返回值:
成功返回 0, 失败返回 -1
*/

对于消息队列的接收,在程序中可以通过调用 msgrcv() 来进行读取数据,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgrcv(int msqid, void *msgp, size_t msgsz, long int msgtyp, int msgflg)
/*
参数:
- msqid 消息队列的队列 ID
- msgp 消息缓冲区
- msgsz 正文字节数
- msgtyp 描述如下:
0:接收消息队列中第一个消息
大于0:接收消息队列中第一个类型为 msgtyp 的消息
小于0:接收消息队列中第一个类型值不小于 msgtyp 绝对值且值最小的消息
- msgflg 描述如下:
- MSG_NOERROR:若返回的消息比 msgsz 字节多,则消息就会截断到 msgsz 字节,且不会通知消息到进程
- IPC_NOWAIT:若消息队列中没有相应类型的消息可以接收,则函数会立即返回
- 0:函数调用阻塞等待接收到一条消息为止
返回值:
成功返回 0, 失败返回 -1
*/

除了上述 API 之外,还可以用 msgctl() 函数来对消息队列进行设置,具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgctl(int msgqid, int cmd, struct msgid_ds *buf)
/*
参数:
- msqid 消息队列的队列 ID
- cmd 执行操作,如下:
- IPC_STAT:读取消息队列的数据结构 msgid_ds ,并将其存储到 buf 位置
- IPD_SET:设置消息队列的数据结构 msgid_ds 中的 ipc_perm 值,这个值取自 buf 参数
- IPC_RMID:从系统内核中删除消息队列
- buf:描述消息队列的 msqid_ds 结构类型的变量
返回值
成功返回 0, 失败返回 -1
*/

【原理剖析】4-进程间通信方式之消息队列
https://hodlyounger.github.io/A_OS/Linux/Linux操作系统原理剖析/【原理剖析】4-进程间通信方式之消息队列/
作者
mingming
发布于
2023年12月26日
许可协议