Linux内核中消息队列是如何实现的?

Sen*_*Sen 31 linux kernel

我想知道消息队列是如何在 Linux 内核中实现的。

wag*_*wag 41

Linux 内核 (2.6) 实现了两个消息队列:(
而不是“消息列表”,因为实现是通过使用不严格遵循 FIFO 原则的链表完成的)

System V IPC 消息

来自 System V 的消息队列。

进程可以调用msgsnd()以发送消息。他需要传递接收消息队列的IPC标识、消息的大小和消息结构,包括消息类型和文本。

另一方面,进程调用msgrcv()以接收消息,传递消息队列的 IPC 标识符(消息应存储在何处)、大小和值t

t指定从队列返回的消息,正值表示返回其类型等于t的第一条消息,负值返回等于类型t的最后一条消息,零返回队列的第一条消息。

这些函数在include/linux/msg.h中定义并在ipc/msg.c 中实现

消息大小(max)、消息总数(mni)和队列中所有消息的总大小(mnb)有限制:

$ sysctl kernel.msg{max,mni,mnb}
kernel.msgmax = 8192
kernel.msgmni = 1655
kernel.msgmnb = 16384
Run Code Online (Sandbox Code Playgroud)

上面的输出来自 Ubuntu 10.10 系统,默认值在msg.h中定义。

更多令人难以置信的旧 System V 消息队列内容在这里解释。

POSIX 消息队列

POSIX 标准定义了一种基于 System V IPC 的消息队列的消息队列机制,并通过一些功能对其进行了扩展:

  • 简单的基于文件的应用程序界面
  • 支持消息优先级
  • 支持异步通知
  • 阻塞操作超时

ipc/mqueue.c

例子

util-linux 提供了一些用于分析和修改消息队列的程序,POSIX 规范给出了一些 C 示例:

创建一个消息队列ipcmk;通常你会通过调用像ftok()and这样的 C 函数来做到这一点msgget()

$ ipcmk -Q
Run Code Online (Sandbox Code Playgroud)

让我们看看使用ipcs或使用 a发生了什么cat /proc/sysvipc/msg

$ ipcs -q

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x33ec1686 65536      user       644        0            0           
Run Code Online (Sandbox Code Playgroud)

现在用一些消息填充队列:

$ cat <<EOF >msg_send.c
#include <string.h>
#include <sys/msg.h> 

int main() {
  int msqid = 65536;
  struct message {
    long type;
    char text[20];
  } msg;

  msg.type = 1;
  strcpy(msg.text, "This is message 1");
  msgsnd(msqid, (void *) &msg, sizeof(msg.text), IPC_NOWAIT);
  strcpy(msg.text, "This is message 2");
  msgsnd(msqid, (void *) &msg, sizeof(msg.text), IPC_NOWAIT);

  return 0;
}
EOF
Run Code Online (Sandbox Code Playgroud)

同样,您通常不会在代码中对 msqid 进行硬编码。

$ gcc -o msg_send msg_send.c
$ ./msg_send
$ ipcs -q

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x33ec1686 65536      user       644        40           2        
Run Code Online (Sandbox Code Playgroud)

而另一端,将接收消息:

$ cat <<EOF >msg_recv.c
#include <stdio.h>
#include <sys/msg.h>

int main() {
  int msqid = 65536;
  struct message {
    long type;
    char text[20];
  } msg;
  long msgtyp = 0;

  msgrcv(msqid, (void *) &msg, sizeof(msg.text), msgtyp, MSG_NOERROR | IPC_NOWAIT);
  printf("%s \n", msg.text);

  return 0;
}
EOF
Run Code Online (Sandbox Code Playgroud)

走着瞧吧:

$ gcc -o msg_recv msg_recv.c
$ ./msg_recv
This is message 1
$ ./msg_recv
This is message 2
$ ipcs -q

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x33ec1686 65536      user       644        0            0           
Run Code Online (Sandbox Code Playgroud)

两次接收后,队列再次为空。

之后通过指定键 ( -Q) 或 msqid ( -q)将其删除:

$ ipcrm -q 65536
Run Code Online (Sandbox Code Playgroud)