【问题标题】:Is there a way to increase the maximum amount of messages that can be contained in a SysV message queue?有没有办法增加 SysV 消息队列中可以包含的最大消息量?
【发布时间】:2021-04-03 11:18:38
【问题描述】:

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

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

有没有办法提高 SysV 的限制?

【问题讨论】:

  • 为什么需要这么长的消息队列?我可以看到 10 有点短,但如果 1024 还不够,那么是否有任何实际适合的限制?如果您一次累积那么多消息,那么听起来消息消费者跟不上消息生产者的速度,更长的队列也无法解决这个问题。
  • 哪个 System V 实现?例如,在 Solaris 上,/etc 中有一个文本文件,可以在引导时配置(覆盖)系统 V IPC 限制的值。但细节取决于确切的 I/s。限制和改变它们的机制没有标准化。
  • the limit is much higher 这是排队消息的限制吗?因此,请改为在用户空间中缓冲它们。 1024 vs 10这些数字从何而来?测量?源代码?文档?
  • 使用队列进行长期存储似乎“不太理想”。另一种解决方案(例如数据库)可能更适合。
  • @EnricoBersanoBegey,如果程序的设计依赖于能够缓冲队列中任意数量的消息,那么是的,它使用队列作为存储机制。如果用户选择触发此行为的参数是有效的,则构成设计缺陷,调整队列限制不是解决方案——它只会改变程序失败的可能性。如果此类参数无效,则程序在无法将消息排入队列时应该正常失败。

标签: c ipc message-queue sysv sysv-ipc


【解决方案1】:

由于 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;
}

我们可以构建它,将其所有者/组更改为 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

【讨论】:

  • 非常感谢,帮了大忙。不幸的是,我不能使用“setuid helper”......它一直说open(): Permission denied,即使我已经设置了所有权限
  • 您是否设置了 setuid 位 (4555) 或者您可能在一个容器中,而没有对 /proc/sys 文件系统的写入权限?
  • 我确实设置了 setuid 位并且没有出现错误。 ls -l mq_helper 输出:-r-xr-xr-x 1 root root 16976 Dec 25 15:26 mq_helper
  • 但是您没有 setuid 位:“s”没有出现在您的显示器中
  • 对。出于某种原因,我无法设置该位,我还尝试运行sudo chmod u+s mq_helper。我会想办法解决这个问题。非常感谢
猜你喜欢
  • 2013-09-09
  • 2017-12-02
  • 1970-01-01
  • 2013-09-25
  • 2014-03-07
  • 2015-08-29
  • 2011-04-21
  • 2022-01-18
相关资源
最近更新 更多