使用消息队列进行 IPC

2025年4月30日 | 阅读 6 分钟

消息队列是一种进程间通信 (IPC) 机制,它允许进程以消息的形式在两个进程之间交换数据。它允许进程通过互相发送消息来进行异步通信,消息会存储在队列中,等待处理,并在处理后删除。

IPC using Message Queues

消息队列是一种在非共享内存环境中使用的缓冲区,在这种环境中,任务通过传递消息而不是访问共享变量来通信。任务共享一个公共缓冲区池。消息队列是一个无界 FIFO 队列,可以防止不同线程的并发访问。

事件是异步的。当一个类向另一个类发送事件时,它不会直接发送给目标响应类,而是将事件传递给操作系统消息队列。目标类在准备好处理事件时,会从消息队列的头部检索事件。同步事件可以使用触发的操作来传递。

多个任务可以写入消息到队列,但一次只有一个任务可以从队列读取消息。读取者会在消息队列上等待,直到有消息可供处理。消息的大小可以是任意的。

消息队列的功能

为了实现使用消息队列的 IPC,我们将使用四个重要的函数。

1. int msgget (key_t key, int msgflg);

我们使用 msgget 函数来创建和访问消息队列。它接受两个参数。

  • 第一个参数是标识系统中消息队列的键。
  • 第二个参数用于为消息队列分配权限,并与 IPC_CREAT 进行 OR 操作,以便在队列不存在时创建它。如果队列已存在,则 IPC_CREAT 被忽略。成功时,msgget 函数返回一个正数,即队列标识符;失败时,返回 -1。

2. int msgsnd (int msqid, const void *msg_ptr, size_t msg_sz, int msgflg);

此函数允许我们将消息添加到消息队列。

  • 第一个参数 (msgid) 是 msgget 函数返回的消息队列标识符。
  • 第二个参数是指向要发送的消息的指针,它必须以 long int 类型开头。
  • 第三个参数是消息的大小。它不应包含 long int 消息类型。
  • 第四个也是最后一个参数控制消息队列已满或系统队列消息数量达到限制时会发生什么。成功时,该函数返回 0,并将消息数据的副本放置在消息队列中。失败时,返回 -1。

消息结构有两个约束。首先,它必须小于系统限制,其次,它必须以 long int 开头。这个 long int 在接收函数中用作消息类型。最佳的消息结构如下所示。

由于 message_type 在消息接收中使用,您不能简单地忽略它。您必须声明您的数据结构以包含它,并且最好将其初始化为一个已知值。

3. int msgrcv (int msqid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg);

此函数从消息队列检索消息。

  • 第一个参数 (msgid) 是 msgget 函数返回的消息队列标识符。
  • 如上所述,第二个参数是指向要接收的消息的指针,它必须以 long int 类型开头。
  • 第三个参数是消息的大小。
  • 第四个参数允许实现优先级。如果值为 0,则检索队列中的第一个可用消息。但如果值大于 0,则检索具有相同消息类型的第一条消息。如果值为负数,则检索类型值与 msgtype 的绝对值相同的消息。简单来说,0 值表示按发送顺序接收消息,非零值表示接收具有特定消息类型的消息。
  • 最后一个参数控制消息队列已满或系统队列消息数量达到限制时会发生什么。成功时,该函数返回 0,并将消息数据的副本放置在消息队列中。失败时,返回 -1。

4. int msgctl (int msqid, int command, struct msqid_ds *buf);

最后一个函数是 msgctl,它是控制函数。

  • 第一个参数是 msgget 函数返回的标识符。
  • 第二个参数可以具有以下三个值之一。
命令描述
IPC_STAT它将 msqid_ds 结构中的数据设置为反映与消息队列相关联的值。
IPC_SET如果进程有权限这样做,它会将与消息队列相关联的值设置为 msqid_ds 数据结构中提供的值。
IPC_RMID它删除消息队列。

msgctl 函数成功时返回 0,错误时返回 -1。如果在进程等待 msgsnd 或 msgrcv 函数时消息队列被删除,发送或接收函数将失败。

使用消息队列执行 IPC 的步骤

消息队列是存储在内核中的消息的链表,由消息队列标识符标识。以下是使用消息队列执行通信的步骤。

IPC using Message Queues
  1. 使用 msgget() 创建新队列或打开现有队列。
  2. 使用 msgsnd() 将新消息添加到队列末尾。每条消息都有一个正的长整数类型字段、一个非负长度以及实际数据字节(对应于长度),所有这些在消息添加到队列时都指定给 msgsnd()。
  3. 使用 msgrcv() 从队列获取消息。我们不必按先进先出的顺序获取消息。相反,我们可以根据消息的类型字段来获取消息。所有进程都可以通过访问公共系统消息队列来交换信息。发送进程将消息放入另一个进程可以读取的队列中。每条消息都给有一个标识符或类型,以便进程可以选择合适的消息。进程必须共享一个通用键才能首先访问队列。
  4. 使用 msgctl() 对消息队列执行控制操作。

示例

程序 1:让我们编写一个使用消息队列进行 IPC 的程序来向消息队列发送数据。

输出

上面的程序输出如下。

IPC using Message Queues

它是如何工作的?

结构 my_msg 声明了 long int 变量和 char 数组来存储发送到消息队列的数据。然后使用 msgget() 函数创建消息队列。接下来,使用 fgets() 从用户那里读取数据到缓冲区,然后将其复制到结构 some_data 的变量 some_text 中。最后,使用 msgsnd() 函数将数据发送到队列。strcmp 函数用于通过比较数据的前三个字符来停止发送数据。如果数据以“end”开头,则表示不再发送数据。

程序 2:现在,让我们编写一个使用消息队列进行 IPC 的程序来从上面创建的消息队列接收或读取消息。

输出

上述代码给出以下输出。

IPC using Message Queues

它是如何工作的?

将 msg_to_rec 变量设置为 0,以便按发送的顺序接收数据。while 循环用于使用 mgrcv() 函数连续接收数据,直到接收到的文本为“end”,我们使用 strcmp 函数进行检查。使用 my_msg 结构读取数据。


下一个主题Guest-operating-system