有没有办法增加 SysV 消息队列中可以包含的最大消息量?

Is there a way to increase the maximum amount of messages that can be contained in a SysV message queue?

我刚从 POSIX 切换到 SysV,因为 SysV 的限制要高得多(1024 对 10)。但我仍然需要一个更高的限制。该限制应在 运行 时更改,因为它取决于用户选择的数据。

使用 POSIX,可以增加限制,但每次都必须 运行 以 root 身份执行代码,我不能那样做。

有没有办法增加 SysV 的限制?

由于 SYSV IPC 被认为已弃用,使用那些老式服务设计全新的应用程序是一种遗憾。

POSIX 消息队列基于文件系统。通常挂载在/dev/mqueue:

$ ls -la /dev/mqueue
total 0
drwxrwxrwt  2 root root   40 dec.  25 21:02 .
drwxr-xr-x 20 root root 4600 dec.  25 20:51 ..

已使用 mq_open() 创建消息队列。 attr参数提供了设置一些属性的能力:

struct mq_attr {
  long mq_flags;       /* Flags (ignored for mq_open()) */
  long mq_maxmsg;      /* Max. # of messages on queue */
  long mq_msgsize;     /* Max. message size (bytes) */
  long mq_curmsgs;     /* # of messages currently in queue (ignored for mq_open()) */
};

根据 documentation/proc/sys/fs/mqueue/msg_max 文件定义了队列中最大消息数的上限值。在Linux 5.4中,其默认值为DFLT_MSGMAX(10),上限为HARD_MSGMAX (65536).

默认值在Linux源代码中定义(参见/include/linux/ipc_namespace.h):

#define DFLT_QUEUESMAX      256
#define MIN_MSGMAX          1
#define DFLT_MSG            10U
#define DFLT_MSGMAX         10
#define HARD_MSGMAX         65536
#define MIN_MSGSIZEMAX      128
#define DFLT_MSGSIZE        8192U
#define DFLT_MSGSIZEMAX     8192
#define HARD_MSGSIZEMAX     (16*1024*1024)

这是一个创建消息队列的示例程序。它接收消息队列名称和队列中的最大消息数作为参数:

#include <errno.h>
#include <pthread.h>
#include <mqueue.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
  mqd_t mqdes;
  struct mq_attr attr;

  if (argc != 3) {
    fprintf(stderr, "Usage: %s <mq-name> <msg_max>\n", argv[0]);
    exit(EXIT_FAILURE);
  }

  attr.mq_flags = 0;
  attr.mq_maxmsg = atoi(argv[2]);
  attr.mq_msgsize = 2048;
  attr.mq_curmsgs = 0;

  mqdes = mq_open(argv[1], O_CREAT | O_RDWR, 0777, &attr);
  if (mqdes == (mqd_t) -1) {
    perror("mq_open");
    return 1;
  }

  return 0;
}

在执行时,我们可以验证,默认情况下,队列的消息数不能超过 10 条:

$ gcc mq.c -o mq -lrt
$ ./mq 
Usage: ./mq <mq-name> <msg_max>
$ ./mq /q0 5
$ ls -la /dev/mqueue/
total 0
drwxrwxrwt  2 root   root     60 dec.  25 21:09 .
drwxr-xr-x 20 root   root   4600 dec.  25 20:51 ..
-rwxrwxr-x  1 xxxx   xxxx     80 dec.  25 21:09 q0
$ ./mq /q1 9
$ ./mq /q2 10
$ ./mq /q3 11
mq_open: Invalid argument
$ ls -la /dev/mqueue/
total 0
drwxrwxrwt  2 root   root    100 dec.  25 21:10 .
drwxr-xr-x 20 root   root   4600 dec.  25 20:51 ..
-rwxrwxr-x  1 xxxx   xxxx     80 dec.  25 21:09 q0
-rwxrwxr-x  1 xxxx   xxxx     80 dec.  25 21:10 q1
-rwxrwxr-x  1 xxxx   xxxx     80 dec.  25 21:10 q2

让我们将 msg_max 的上限值从 10 更改为 256:

$ cat /proc/sys/fs/mqueue/msg_max
10
$ sudo sh -c "echo 256 > /proc/sys/fs/mqueue/msg_max"
$ cat /proc/sys/fs/mqueue/msg_max
256

现在可以创建最多包含 256 条消息的消息队列:

$ ./mq /q3 11
$ ./mq /q4 256
$ ./mq /q5 257
mq_open: Invalid argument
$ ls -la /dev/mqueue/
total 0
drwxrwxrwt  2 root   root    140 dec.  25 21:16 .
drwxr-xr-x 20 root   root   4600 dec.  25 20:51 ..
-rwxrwxr-x  1 xxxx   xxxx     80 dec.  25 21:09 q0
-rwxrwxr-x  1 xxxx   xxxx     80 dec.  25 21:10 q1
-rwxrwxr-x  1 xxxx   xxxx     80 dec.  25 21:10 q2
-rwxrwxr-x  1 xxxx   xxxx     80 dec.  25 21:15 q3
-rwxrwxr-x  1 xxxx   xxxx     80 dec.  25 21:16 q4

但是正如你所说,提高上限值需要超级用户权限。可以创建一个增加上限值的“setuid helper”。例如,以下程序设置作为参数传递的上限值:

#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char *argv[])
{
int fd;
int rc;
int msg_max;

  if (argc != 2) {
    fprintf(stderr, "Usage: %s <msg_max>\n", argv[0]);
    return 1;
  }

  fd = open("/proc/sys/fs/mqueue/msg_max", O_RDWR);
  if (fd < 0) {
    perror("open()");
    return 1;
  }

  rc = write(fd, argv[1], strlen(argv[1]));
  if (rc != strlen(argv[1])) {
    if (rc >= 0) {
      errno = EIO;
    }
    perror("write()");
    return 1;
  }

  close(fd);

  return 0;
}

我们可以构建它,将其 owner/group 更改为 root 并添加 setuid 位:

$ gcc mq_helper.c -o mq_helper
$ sudo chown root mq_helper
$ sudo chgrp root mq_helper
$ sudo chmod 4555 mq_helper
$ ls -l mq_helper
-r-sr-xr-x 1 root root 17016 dec.  25 21:45 mq_helper

然后,可以从非超级用户帐户运行此程序更改上限值msg_max:

$ cat /proc/sys/fs/mqueue/msg_max
256
$ ./mq_helper 98
$ cat /proc/sys/fs/mqueue/msg_max
98