我刚刚从 POSIX 切换到 SysV,因为 SysV 的限制要高得多(1024 对 10)。但我仍然需要更高的限制。该限制应在运行时更改,因为它取决于用户选择的数据。
使用 POSIX,可以增加限制,但每次都必须以 root 身份运行代码,而我不能这样做。
有没有办法增加 SysV 的限制?
我刚刚从 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()) */
};
根据文档,/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