What Is mq_attr?
Every POSIX message queue has a set of attributes that control its behavior: how many messages it can hold, how large each message can be, and whether operations on it should block or return immediately. These attributes are communicated to and from the kernel using the struct mq_attr structure, defined in <mqueue.h>.
Three functions use this structure: mq_open() (at creation), mq_getattr() (to read attributes), and mq_setattr() (to modify the O_NONBLOCK flag).
The mq_attr Structure
#include <mqueue.h>
struct mq_attr {
long mq_flags; /* Message queue description flags:
0 or O_NONBLOCK.
Used by: mq_getattr(), mq_setattr() */
long mq_maxmsg; /* Maximum number of messages on queue.
Used by: mq_open() (set), mq_getattr() (read) */
long mq_msgsize; /* Maximum message size in bytes.
Used by: mq_open() (set), mq_getattr() (read) */
long mq_curmsgs; /* Number of messages currently in the queue.
Used by: mq_getattr() only (read-only) */
};
Field-by-Field Breakdown
| Field | Type | Meaning | Who Uses It | Fixed After Create? |
|---|---|---|---|---|
mq_flags |
long |
0 or O_NONBLOCK. Controls blocking behavior |
getattr, setattr | No – can be changed with mq_setattr() |
mq_maxmsg |
long |
Max number of messages the queue can hold. Must be > 0 | mq_open (set), getattr | Yes – fixed at creation |
mq_msgsize |
long |
Max size in bytes of each individual message. Must be > 0 | mq_open (set), getattr | Yes – fixed at creation |
mq_curmsgs |
long |
Current number of messages in the queue at this moment | getattr only | N/A – read-only, changes dynamically |
Critical Rule: mq_maxmsg and mq_msgsize Are Immutable
Once a queue is created with mq_open(), the values of mq_maxmsg and mq_msgsize are permanently fixed. They cannot be changed later. This is because the kernel uses these two values to pre-calculate the maximum memory the queue may ever need. Only the mq_flags field (specifically O_NONBLOCK) can be modified after creation, using mq_setattr().
Which Field Belongs Where?
The mq_attr structure carries information at two different levels. Understanding this distinction is key:
|
Open MQ Description Level
mq_flags
Associated with the open description, not the queue itself. Each descriptor has its own open description with its own flags. Changed with |
| |
Queue Object Level
mq_maxmsg mq_msgsize mq_curmsgs
|
Setting Attributes at Queue Creation with mq_open()
When calling mq_open() with O_CREAT, you can pass a pointer to an mq_attr structure to set mq_maxmsg and mq_msgsize. If you pass NULL for the attribute argument, the system uses implementation-defined defaults.
On Linux, the defaults are governed by two /proc files:
/proc/sys/fs/mqueue/msg_default— default value formq_maxmsg/proc/sys/fs/mqueue/msgsize_default— default value formq_msgsize/proc/sys/fs/mqueue/msg_max— upper limit formq_maxmsg/proc/sys/fs/mqueue/msgsize_max— upper limit formq_msgsize
mq_getattr() – Reading Queue Attributes
#include <mqueue.h>
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
/* Returns 0 on success, -1 on error (sets errno) */
Fills in the mq_attr structure pointed to by attr with current information about the open message queue description and the queue associated with descriptor mqdes. This is the function you call to find out the queue’s capacity, current message count, and the current O_NONBLOCK flag state.
mq_setattr() – Modifying Queue Flags
#include <mqueue.h>
int mq_setattr(mqd_t mqdes,
const struct mq_attr *newattr,
struct mq_attr *oldattr);
/* Returns 0 on success, -1 on error (sets errno) */
Sets the flags of the open message queue description for mqdes to the value specified in newattr->mq_flags. Only the mq_flags field is used from newattr — the other fields are ignored. If oldattr is not NULL, the previous attribute values are stored there before the change.
mq_setattr() can only change mq_flags (i.e., toggle O_NONBLOCK). You cannot use it to change mq_maxmsg or mq_msgsize — those are fixed permanently at queue creation.Code Examples
Pass a filled mq_attr structure to mq_open() to control the maximum message count and maximum message size.
#include <mqueue.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
int main(void)
{
struct mq_attr attr;
mqd_t mqd;
/* Set custom attributes */
attr.mq_maxmsg = 10; /* queue can hold at most 10 messages */
attr.mq_msgsize = 128; /* each message is at most 128 bytes */
/* mq_flags and mq_curmsgs are ignored by mq_open() */
mqd = mq_open("/custom_q",
O_CREAT | O_RDWR,
S_IRUSR | S_IWUSR,
&attr); /* <-- pass our custom attrs */
if (mqd == (mqd_t)-1) {
perror("mq_open");
exit(EXIT_FAILURE);
}
printf("Queue created with mq_maxmsg=10, mq_msgsize=128\n");
mq_close(mqd);
mq_unlink("/custom_q");
return 0;
}
Use mq_getattr() to inspect the queue’s capacity, current occupancy, and blocking mode at runtime.
#include <mqueue.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
int main(void)
{
mqd_t mqd;
struct mq_attr attr;
/* Open existing queue (or create) */
mqd = mq_open("/info_q", O_CREAT | O_RDWR,
S_IRUSR | S_IWUSR, NULL);
if (mqd == (mqd_t)-1) { perror("mq_open"); exit(1); }
/* Send a test message so mq_curmsgs > 0 */
const char *msg = "test";
mq_send(mqd, msg, 4, 0);
/* Read attributes */
if (mq_getattr(mqd, &attr) == -1) {
perror("mq_getattr");
mq_close(mqd);
mq_unlink("/info_q");
exit(1);
}
printf("=== Queue Attributes ===\n");
printf(" mq_flags : %ld (%s)\n",
attr.mq_flags,
(attr.mq_flags & O_NONBLOCK) ? "O_NONBLOCK" : "blocking");
printf(" mq_maxmsg : %ld messages max\n", attr.mq_maxmsg);
printf(" mq_msgsize: %ld bytes max per msg\n", attr.mq_msgsize);
printf(" mq_curmsgs: %ld messages currently in queue\n",
attr.mq_curmsgs);
mq_close(mqd);
mq_unlink("/info_q");
return 0;
}
/* Sample output:
=== Queue Attributes ===
mq_flags : 0 (blocking)
mq_maxmsg : 10
mq_msgsize: 8192
mq_curmsgs: 1 messages currently in queue
*/
Switch a queue from blocking mode to non-blocking mode at runtime. This only affects the current open description (your descriptor), not other descriptors.
#include <mqueue.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
int main(void)
{
mqd_t mqd;
struct mq_attr newattr, oldattr, current;
char buf[256];
unsigned int prio;
mqd = mq_open("/nb_q", O_CREAT | O_RDWR,
S_IRUSR | S_IWUSR, NULL);
if (mqd == (mqd_t)-1) { perror("mq_open"); exit(1); }
/* Check current flags */
mq_getattr(mqd, ¤t);
printf("Before setattr: flags = %ld\n", current.mq_flags);
/* Enable O_NONBLOCK on this descriptor's description */
newattr.mq_flags = O_NONBLOCK;
if (mq_setattr(mqd, &newattr, &oldattr) == -1) {
perror("mq_setattr");
exit(1);
}
printf("Old flags were: %ld\n", oldattr.mq_flags);
/* Now verify */
mq_getattr(mqd, ¤t);
printf("After setattr: flags = %ld (O_NONBLOCK set)\n",
current.mq_flags);
/* Try to receive from an EMPTY queue in non-blocking mode */
ssize_t n = mq_receive(mqd, buf, 256, &prio);
if (n == -1) {
if (errno == EAGAIN) {
/* Expected: queue is empty, returns immediately */
printf("mq_receive returned EAGAIN (queue empty, non-blocking)\n");
} else {
perror("mq_receive");
}
}
/* Restore blocking mode */
newattr.mq_flags = 0;
mq_setattr(mqd, &newattr, NULL);
printf("Restored to blocking mode.\n");
mq_close(mqd);
mq_unlink("/nb_q");
return 0;
}
This is the TLPI pmsg_create.c example — a command-line tool that creates a POSIX message queue with optional custom attributes. It demonstrates how to handle optional attrp (NULL vs pointer to struct).
#include <mqueue.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
/* Usage: pmsg_create [-cx] [-m maxmsg] [-s msgsize] mq-name [octal-perms]
-c Create queue (O_CREAT)
-m Set mq_maxmsg
-s Set mq_msgsize
-x Create exclusively (O_EXCL) */
static void usageError(const char *prog)
{
fprintf(stderr,
"Usage: %s [-cx] [-m maxmsg] [-s msgsize] name [perms]\n",
prog);
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
int flags = O_RDWR;
int opt;
mode_t perms;
mqd_t mqd;
struct mq_attr attr, *attrp;
/* Defaults in case only one of -m / -s is given */
attrp = NULL;
attr.mq_maxmsg = 50; /* default max messages */
attr.mq_msgsize = 2048; /* default max message size */
while ((opt = getopt(argc, argv, "cm:s:x")) != -1) {
switch (opt) {
case 'c':
flags |= O_CREAT;
break;
case 'm':
attr.mq_maxmsg = atoi(optarg);
attrp = &attr; /* caller wants custom attrs */
break;
case 's':
attr.mq_msgsize = atoi(optarg);
attrp = &attr;
break;
case 'x':
flags |= O_EXCL;
break;
default:
usageError(argv[0]);
}
}
if (optind >= argc)
usageError(argv[0]);
/* Default permissions: owner read/write */
perms = (argc <= optind + 1)
? (S_IRUSR | S_IWUSR)
: (mode_t)strtol(argv[optind + 1], NULL, 8);
/* If attrp is NULL here: kernel uses its own defaults */
mqd = mq_open(argv[optind], flags, perms, attrp);
if (mqd == (mqd_t)-1) {
perror("mq_open");
exit(EXIT_FAILURE);
}
printf("Queue '%s' created/opened (mqd=%d)\n",
argv[optind], (int)mqd);
/* In a real tool you'd keep it open. Here we just close it. */
mq_close(mqd);
exit(EXIT_SUCCESS);
}
/* Build: gcc -o pmsg_create pmsg_create.c -lrt
Usage examples:
./pmsg_create -c /myqueue # create, default attrs, NULL attrp
./pmsg_create -c -m 5 -s 64 /myq # create, 5 msgs max, 64 bytes each
./pmsg_create -cx /myq # create exclusively, fail if exists */
Linux imposes system-wide limits on mq_maxmsg and mq_msgsize. If your requested values exceed these limits, mq_open() fails with EINVAL. You can read and change these via /proc.
/* Read current limits from the shell: */
$ cat /proc/sys/fs/mqueue/msg_max /* max allowed mq_maxmsg */
10
$ cat /proc/sys/fs/mqueue/msgsize_max /* max allowed mq_msgsize */
8192
$ cat /proc/sys/fs/mqueue/msg_default /* default mq_maxmsg */
10
$ cat /proc/sys/fs/mqueue/msgsize_default /* default mq_msgsize */
8192
/* Temporarily raise msg_max (requires root): */
$ echo 100 > /proc/sys/fs/mqueue/msg_max
/* From C code: */
#include <stdio.h>
#include <stdlib.h>
void print_mq_limits(void)
{
FILE *f;
long val;
f = fopen("/proc/sys/fs/mqueue/msg_max", "r");
if (f) {
fscanf(f, "%ld", &val);
fclose(f);
printf("msg_max (max mq_maxmsg) = %ld\n", val);
}
f = fopen("/proc/sys/fs/mqueue/msgsize_max", "r");
if (f) {
fscanf(f, "%ld", &val);
fclose(f);
printf("msgsize_max (max mq_msgsize) = %ld\n", val);
}
f = fopen("/proc/sys/fs/mqueue/msg_default", "r");
if (f) {
fscanf(f, "%ld", &val);
fclose(f);
printf("msg_default = %ld\n", val);
}
}
A real-world pattern: call mq_getattr() before sending to confirm the queue is not full and your message fits within mq_msgsize.
#include <mqueue.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
int safe_send(mqd_t mqd, const char *message, unsigned int priority)
{
struct mq_attr attr;
size_t msglen = strlen(message);
/* Step 1: read current attributes */
if (mq_getattr(mqd, &attr) == -1) {
perror("mq_getattr");
return -1;
}
/* Step 2: check message fits within size limit */
if ((long)msglen > attr.mq_msgsize) {
fprintf(stderr,
"Error: message length %zu exceeds mq_msgsize %ld\n",
msglen, attr.mq_msgsize);
return -1;
}
/* Step 3: check queue is not already full */
if (attr.mq_curmsgs >= attr.mq_maxmsg) {
fprintf(stderr,
"Warning: queue is full (%ld/%ld messages)\n",
attr.mq_curmsgs, attr.mq_maxmsg);
/* Could wait, or return error depending on design */
return -1;
}
/* Step 4: send */
if (mq_send(mqd, message, msglen, priority) == -1) {
perror("mq_send");
return -1;
}
printf("Sent: '%s' (prio=%u, now %ld/%ld msgs in queue)\n",
message, priority,
attr.mq_curmsgs + 1, /* approximate */
attr.mq_maxmsg);
return 0;
}
int main(void)
{
struct mq_attr attr;
mqd_t mqd;
attr.mq_maxmsg = 5;
attr.mq_msgsize = 64;
mqd = mq_open("/safe_q", O_CREAT | O_RDWR,
S_IRUSR | S_IWUSR, &attr);
if (mqd == (mqd_t)-1) { perror("mq_open"); exit(1); }
safe_send(mqd, "hello", 1);
safe_send(mqd, "world", 2);
safe_send(mqd, "linux", 1);
/* Try to send a message that is too large */
char toobig[128];
memset(toobig, 'X', 127);
toobig[127] = '\0';
safe_send(mqd, toobig, 1); /* will fail: exceeds mq_msgsize=64 */
mq_close(mqd);
mq_unlink("/safe_q");
return 0;
}
Function Quick Reference
| Function | Purpose | Fields Used from mq_attr | Link flag |
|---|---|---|---|
mq_open() |
Open / create queue | mq_maxmsg, mq_msgsize |
-lrt |
mq_getattr() |
Read all attributes | All four fields (read) | -lrt |
mq_setattr() |
Change flags only | mq_flags only (write) |
-lrt |
mq_close() |
Close descriptor | N/A | -lrt |
mq_unlink() |
Remove queue name | N/A | -lrt |
Interview Questions & Answers
A: mq_flags — holds 0 or O_NONBLOCK; controls blocking behavior of the open description.
mq_maxmsg — maximum number of messages the queue can hold at any time.
mq_msgsize — maximum size in bytes of any single message.
mq_curmsgs — the current number of messages in the queue (dynamic, read-only).
A: No. mq_maxmsg and mq_msgsize are fixed at the time the queue is created with mq_open(O_CREAT). They cannot be changed later. The kernel uses these values to pre-calculate the maximum memory the queue could need. mq_setattr() only allows changing mq_flags.
A: The queue is created with implementation-defined default values for mq_maxmsg and mq_msgsize. On Linux, these defaults are set by /proc/sys/fs/mqueue/msg_default and /proc/sys/fs/mqueue/msgsize_default. The typical defaults are mq_maxmsg=10 and mq_msgsize=8192 bytes, but they may vary by system configuration.
A: mq_setattr() modifies the attributes of the open message queue description associated with a descriptor. In practice, it can only usefully change the mq_flags field — specifically, to set or clear O_NONBLOCK. The other fields in the newattr structure passed to mq_setattr() are silently ignored.
A: In the message queue object (the per-queue data structure), not in the open description. These are properties of the queue itself — they are shared by all processes that have the queue open. The open description only stores mq_flags.
A: mq_curmsgs is a snapshot of the number of messages currently in the queue at the moment mq_getattr() was called. It is useful for monitoring queue occupancy, checking whether the queue is empty before a non-blocking receive, or checking if the queue is full before a send. However, because other processes may be sending/receiving concurrently, the value may be stale by the time you act on it.
A: Read the following /proc files:
/proc/sys/fs/mqueue/msg_max # max value for mq_maxmsg
/proc/sys/fs/mqueue/msgsize_max # max value for mq_msgsize
/proc/sys/fs/mqueue/msg_default # default mq_maxmsg when NULL attr passed
/proc/sys/fs/mqueue/msgsize_default # default mq_msgsize when NULL attr passed
If you try to create a queue with mq_maxmsg or mq_msgsize exceeding the corresponding _max value, mq_open() returns -1 with errno == EINVAL. Root can raise these limits by writing to the files.
A: If oldattr is not NULL, mq_setattr() stores the previous attribute values (before the change) into the structure pointed to by oldattr. This is useful for saving and later restoring the original state (e.g., temporarily enabling O_NONBLOCK and then restoring the original flag). If you don’t need the old values, pass NULL.
A: The kernel uses these two values together to calculate the maximum amount of memory the queue might need (mq_maxmsg × mq_msgsize bytes for message data, plus overhead). A value of 0 for either would be meaningless (a queue with zero capacity or zero-byte messages cannot function), so mq_open() rejects such values with EINVAL.
A: mq_send() fails and returns -1 with errno set to EMSGSIZE. Similarly, if the buffer passed to mq_receive() is smaller than mq_msgsize of the queue, mq_receive() fails with EMSGSIZE. This is why you should always call mq_getattr() first and allocate your receive buffer to be at least attr.mq_msgsize bytes.
