The Linux kernel is shared by all running processes. If a single process were allowed to create thousands of message queues and fill each with gigabytes of data, it would starve other processes of resources. To prevent this, the kernel enforces system-wide limits on message queues.
These limits control three things: how many queues can exist simultaneously, how large a single message can be, and how much data a single queue can hold at one time. Understanding these limits is essential for writing robust IPC code — especially in embedded Linux systems or server applications where resource budgets are tight.
msg_qbytes tuned lower than MSGMNB, but never higher (unless privileged).These three limits are the primary constraints. When any of these is hit, a specific system call fails with a specific error code.
What it controls: The maximum number of message queue identifiers (i.e., message queues) that can exist simultaneously on the entire system.
What happens when reached: Any attempt to create a new message queue with msgget(IPC_CREAT, ...) fails with errno = ENOSPC.
Default value (Linux 2.6.31, x86-32): 748 queues
Maximum ceiling: 32768 (defined as IPCMNI in the kernel)
/proc file: /proc/sys/kernel/msgmni
What it controls: The maximum number of bytes in the mtext field of a single message. This is a hard ceiling on individual message size.
What happens when reached: msgsnd() fails with errno = EINVAL if you try to send a message whose mtext length exceeds MSGMAX.
Default value: 8192 bytes (8 KB)
Maximum ceiling: Depends on available system memory
/proc file: /proc/sys/kernel/msgmax
What it controls: The maximum total number of bytes (of mtext data) that a single message queue can hold at one time. This is the initial value assigned to msg_qbytes in msqid_ds when a new queue is created.
What happens when reached: msgsnd() blocks until a receiver drains the queue. If IPC_NOWAIT is specified, it fails with errno = EAGAIN.
Default value: 16384 bytes (16 KB)
Maximum ceiling: 2147483647 (INT_MAX)
/proc file: /proc/sys/kernel/msgmnb
After queue creation, each queue’s msg_qbytes can be individually tuned via msgctl(IPC_SET) — but an unprivileged process cannot set it above MSGMNB. MSGMNB also controls the per-queue upper bound for unprivileged processes.
| Limit Name | What It Limits | Error When Reached | Default (x86-32, Linux 2.6.31) | Maximum Ceiling | /proc File |
|---|---|---|---|---|---|
| MSGMNI | Number of message queues on system | ENOSPC (msgget) | 748 | 32768 (IPCMNI) | /proc/sys/kernel/msgmni |
| MSGMAX | Max bytes per single message | EINVAL (msgsnd) | 8192 bytes | Depends on memory | /proc/sys/kernel/msgmax |
| MSGMNB | Max bytes per queue (init value for msg_qbytes) | Blocks or EAGAIN (msgsnd) | 16384 bytes | 2147483647 (INT_MAX) | /proc/sys/kernel/msgmnb |
Some UNIX implementations define two additional limits. Linux does not enforce these directly, but they appear in older documentation and other OS man pages.
The system-wide maximum number of messages (across all queues combined). Linux does not impose this limit globally but instead limits the number of messages per queue indirectly through msg_qbytes.
The total size of the kernel buffer pool used to hold message data across all queues. Again, Linux does not impose a MSGPOOL global; it relies on the general VM subsystem to limit memory usage.
msg_qbytes: even zero-length messages count as 1-byte equivalent to prevent infinite queuing of zero-byte messages (each still consumes kernel bookkeeping memory).It is legal to send a message with an mtext size of 0 bytes. However, even though the message carries no data, it still consumes kernel overhead (for bookkeeping, the message header, etc.). Without a limit on these, a process could flood the queue with infinite zero-length messages.
Linux handles this by treating zero-length messages as equivalent to 1-byte messages when counting against the msg_qbytes limit. So the maximum number of zero-length messages you can put in a queue equals the maximum number of 1-byte messages — which is bounded by msg_qbytes.
Linux exposes all three enforced limits as readable/writable files under /proc/sys/kernel/. This means you can:
- Read current limit:
cat /proc/sys/kernel/msgmni - Change limit (as root):
echo 2048 > /proc/sys/kernel/msgmni - Make permanent across reboots: add to
/etc/sysctl.conf
Changes to MSGMNB via /proc affect all subsequently created message queues (the initial msg_qbytes for new queues), as well as raising the ceiling for unprivileged IPC_SET calls on existing queues.
$ cat /proc/sys/kernel/msgmni → 748$ cat /proc/sys/kernel/msgmax → 8192$ cat /proc/sys/kernel/msgmnb → 16384Instead of reading /proc files, your program can query the kernel for all message queue limits at once using the Linux-specific msgctl(IPC_INFO) operation. This fills a struct msginfo with the current values of all limits.
The struct msginfo includes fields such as:
| Field | Corresponds To |
|---|---|
| msgmni | MSGMNI — max queues on system |
| msgmax | MSGMAX — max bytes per message |
| msgmnb | MSGMNB — max bytes per queue |
| msgtql | MSGTQL — (implementation-defined) |
| msgpool | MSGPOOL — (implementation-defined) |
msgctl() in this case is ignored (pass 0 by convention). The third argument must be cast to (struct msqid_ds *) even though you are passing a struct msginfo *.This program reads all three system-wide message queue limits from the /proc filesystem and prints them in a human-readable form.
#include <stdio.h>
#include <stdlib.h>
/* Helper: read a single integer from a /proc file */
static long read_proc_int(const char *path)
{
FILE *fp;
long val;
fp = fopen(path, "r");
if (fp == NULL) {
perror(path);
return -1;
}
if (fscanf(fp, "%ld", &val) != 1)
val = -1;
fclose(fp);
return val;
}
int main(void)
{
long msgmni, msgmax, msgmnb;
msgmni = read_proc_int("/proc/sys/kernel/msgmni");
msgmax = read_proc_int("/proc/sys/kernel/msgmax");
msgmnb = read_proc_int("/proc/sys/kernel/msgmnb");
printf("===== System V Message Queue Limits =====\n\n");
printf("MSGMNI (max queues on system) : %ld\n", msgmni);
printf("MSGMAX (max bytes per message) : %ld bytes\n", msgmax);
printf("MSGMNB (max bytes per queue) : %ld bytes\n", msgmnb);
printf("\n");
printf("Notes:\n");
printf(" - Unprivileged IPC_SET: msg_qbytes max = MSGMNB (%ld)\n", msgmnb);
printf(" - Privileged IPC_SET : msg_qbytes max = INT_MAX (2147483647)\n");
printf(" - To change: echo VALUE > /proc/sys/kernel/msgmni (as root)\n");
return 0;
}
This program uses the Linux-specific IPC_INFO operation of msgctl() to read the msginfo structure and display all message queue limits programmatically (no file I/O needed).
#define _GNU_SOURCE /* Required to get IPC_INFO and struct msginfo */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/msg.h>
int main(void)
{
struct msginfo info;
/*
* IPC_INFO: Linux-specific operation.
* First arg (msqid) is ignored — pass 0.
* Cast the msginfo pointer to (struct msqid_ds *) as required by API.
* Returns the highest occupied slot in the message queue table.
*/
if (msgctl(0, IPC_INFO, (struct msqid_ds *) &info) == -1) {
perror("msgctl IPC_INFO");
exit(EXIT_FAILURE);
}
printf("===== msginfo (from IPC_INFO) =====\n\n");
printf("msgmni (max queues) : %d\n", info.msgmni);
printf("msgmax (max bytes/message) : %d bytes\n", info.msgmax);
printf("msgmnb (max bytes/queue) : %d bytes\n", info.msgmnb);
printf("msgtql (max total messages) : %d\n", info.msgtql);
printf("msgpool (kernel buffer pool KB) : %d\n", info.msgpool);
return 0;
}
_GNU_SOURCE before including the headers to get the definitions of IPC_INFO and struct msginfo. Without it, the compiler will report that IPC_INFO is undeclared.msg_qbytes).msgsnd() fails with errno = EINVAL. This is a hard error — the message is simply too large to ever fit in a single send. The solution is either to reduce the message size or increase MSGMAX via /proc/sys/kernel/msgmax (as root).msgget() fails with errno = ENOSPC — meaning there is no space (no free message queue slot) available in the kernel’s IPC table. To resolve this, either remove unused queues or increase MSGMNI via /proc/sys/kernel/msgmni.msg_qbytes in the associated msqid_ds is initialized to MSGMNB. After creation, msg_qbytes can be changed per-queue via msgctl(IPC_SET) — but an unprivileged process cannot raise it above MSGMNB. MSGMNB also serves as the ceiling for IPC_SET by unprivileged processes.msg_qbytes. Zero-length messages are treated as equivalent to 1-byte messages, preventing unbounded queuing of zero-byte signals.cat /proc/sys/kernel/msgmni — number of queuescat /proc/sys/kernel/msgmax — max bytes per messagecat /proc/sys/kernel/msgmnb — max bytes per queueProgrammatically, use
msgctl(0, IPC_INFO, (struct msqid_ds*)&buf) with struct msginfo./proc/sys/kernel/msgmni (etc.) changes the value only until the next reboot. To make it permanent, add the corresponding line to /etc/sysctl.conf:kernel.msgmni = 2048kernel.msgmax = 65536kernel.msgmnb = 65536Then run
sysctl -p to apply without rebooting.msg_qbytes limit._GNU_SOURCE before including the IPC header files. This macro unlocks GNU/Linux extensions that are not part of the POSIX standard. Without it, the compiler will not see the IPC_INFO constant or the struct msginfo definition.msgrcv() on the same queue, removing one or more messages and freeing space. The blocked msgsnd() is then unblocked and proceeds. (2) The message queue is removed with msgctl(IPC_RMID) — in this case the blocked msgsnd() returns with errno = EIDRM. If instead IPC_NOWAIT was used, it would never have blocked in the first place — it would have returned EAGAIN immediately.Next: Displaying All Queues (46.6) → ← Back: msqid_ds Fields (46.4)
