POSIX MQ Attributes mq_getattr() & mq_setattr()

 

POSIX MQ Attributes
Chapter 52 · File 1 of 3 | mq_getattr() & mq_setattr()

← Index | File 1: MQ Attributes | File 2: mq_send →

What Are Message Queue Attributes?

Every POSIX message queue has a set of properties called attributes. These attributes control how many messages the queue can hold, how large each message can be, how many messages are currently in the queue, and whether the queue operates in blocking or non-blocking mode.

These attributes are stored in a structure called struct mq_attr defined in <mqueue.h>. You can read them at any time using mq_getattr() and change limited ones using mq_setattr().

The mq_attr Structure

The mq_attr structure holds four fields. You set two of them when creating the queue and read all four when querying an existing queue.

Field Type Set at Create? Description
mq_flags long From oflag Only O_NONBLOCK. Controls blocking behaviour on this open description.
mq_maxmsg long Yes (mq_open) Max number of messages the queue can hold. Linux default: 10.
mq_msgsize long Yes (mq_open) Max size in bytes of each message. Linux default: 8192.
mq_curmsgs long Read-only Number of messages currently in the queue at the time of the call.

⚠ Important: mq_maxmsg and mq_msgsize are fixed at queue creation time. You cannot change them later via mq_setattr(). Only mq_flags (specifically the O_NONBLOCK bit) can be changed after creation.

Reading Attributes with mq_getattr()

mq_getattr() reads the current attributes of a message queue and fills the mq_attr structure you provide. It is like calling fstat() but for a message queue.

#include <mqueue.h>

int mq_getattr(mqd_t mqdes, struct mq_attr *attr);

/* Returns 0 on success, -1 on error */

mqdes — the message queue descriptor returned by mq_open().
attr — pointer to an mq_attr structure that will be filled with the current attributes.

Example 1: Read and Display Queue Attributes

/* compile: gcc -o pmsg_getattr pmsg_getattr.c -lrt */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mqueue.h>

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

    if (argc != 2) {
        fprintf(stderr, "Usage: %s /queue-name\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    /* Open queue read-only — we just want to inspect it */
    mqd = mq_open(argv[1], O_RDONLY);
    if (mqd == (mqd_t) -1) {
        perror("mq_open");
        exit(EXIT_FAILURE);
    }

    /* Retrieve attributes */
    if (mq_getattr(mqd, &attr) == -1) {
        perror("mq_getattr");
        exit(EXIT_FAILURE);
    }

    printf("mq_flags  (O_NONBLOCK set?): %s\n",
           (attr.mq_flags & O_NONBLOCK) ? "YES" : "NO");
    printf("mq_maxmsg (max messages)   : %ld\n", attr.mq_maxmsg);
    printf("mq_msgsize (max msg size)  : %ld bytes\n", attr.mq_msgsize);
    printf("mq_curmsgs (current count) : %ld\n", attr.mq_curmsgs);

    mq_close(mqd);
    exit(EXIT_SUCCESS);
}
Sample Output (Linux defaults):

mq_flags (O_NONBLOCK set?): NO
mq_maxmsg (max messages) : 10
mq_msgsize (max msg size) : 8192 bytes
mq_curmsgs (current count) : 0

Linux Default Values for mq_maxmsg and mq_msgsize

If you pass NULL as the final argument to mq_open() when creating a queue, Linux uses its built-in defaults. These are:

Attribute Linux Default Notes
mq_maxmsg 10 Very low — set explicitly in production
mq_msgsize 8192 bytes 8 KB per message by default
Portability Note: These defaults vary widely across Unix systems. A portable application should always specify explicit values for mq_maxmsg and mq_msgsize instead of relying on implementation defaults.

Modifying Attributes with mq_setattr()

mq_setattr() lets you change the flags on an open queue description. The only flag you can change is O_NONBLOCK. Optionally, it can also return the old attributes at the same time (like an atomic read-then-write).

#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 */

newattr — only the mq_flags field is examined. Other fields are ignored.
oldattr — if not NULL, filled with the previous attributes (same as calling mq_getattr() first).

mq_getattr()
read mq_flags
into attr
modify bit
attr.mq_flags |= O_NONBLOCK
or &= ~O_NONBLOCK
mq_setattr()
write back
modified flags

Example 2: Enable O_NONBLOCK on an Existing Queue

#include <stdio.h>
#include <stdlib.h>
#include <mqueue.h>
#include <fcntl.h>

int main(void)
{
    mqd_t mqd;
    struct mq_attr attr;

    /* Open queue for writing (queue must already exist) */
    mqd = mq_open("/myqueue", O_WRONLY);
    if (mqd == (mqd_t) -1) { perror("mq_open"); exit(1); }

    /* Step 1: read current flags */
    if (mq_getattr(mqd, &attr) == -1) { perror("mq_getattr"); exit(1); }

    /* Step 2: set the O_NONBLOCK bit */
    attr.mq_flags |= O_NONBLOCK;

    /* Step 3: write back — pass NULL since we don't need old attrs */
    if (mq_setattr(mqd, &attr, NULL) == -1) { perror("mq_setattr"); exit(1); }

    printf("O_NONBLOCK enabled on /myqueue\n");
    mq_close(mqd);
    return 0;
}

Example 3: Disable O_NONBLOCK (Switch Back to Blocking)

#include <stdio.h>
#include <stdlib.h>
#include <mqueue.h>
#include <fcntl.h>

void set_blocking(mqd_t mqd)
{
    struct mq_attr attr;

    if (mq_getattr(mqd, &attr) == -1) { perror("mq_getattr"); return; }

    /* Clear the O_NONBLOCK bit using bitwise AND with complement */
    attr.mq_flags &= ~O_NONBLOCK;

    if (mq_setattr(mqd, &attr, NULL) == -1) { perror("mq_setattr"); return; }

    printf("Queue is now BLOCKING\n");
}

int main(void)
{
    mqd_t mqd = mq_open("/myqueue", O_RDONLY | O_NONBLOCK);
    if (mqd == (mqd_t) -1) { perror("mq_open"); exit(1); }

    set_blocking(mqd);   /* switch to blocking */

    mq_close(mqd);
    return 0;
}

Example 4: Save Old Flags and Change Atomically

#include <stdio.h>
#include <stdlib.h>
#include <mqueue.h>
#include <fcntl.h>

int main(void)
{
    mqd_t mqd;
    struct mq_attr newattr, oldattr;

    mqd = mq_open("/myqueue", O_RDWR);
    if (mqd == (mqd_t) -1) { perror("mq_open"); exit(1); }

    /* We want to set O_NONBLOCK but also record what the flags were before */
    if (mq_getattr(mqd, &newattr) == -1) { perror("mq_getattr"); exit(1); }
    newattr.mq_flags |= O_NONBLOCK;

    /* oldattr receives the PREVIOUS state before the change */
    if (mq_setattr(mqd, &newattr, &oldattr) == -1) {
        perror("mq_setattr"); exit(1);
    }

    printf("Previous flags: %ld (O_NONBLOCK was %s)\n",
           oldattr.mq_flags,
           (oldattr.mq_flags & O_NONBLOCK) ? "SET" : "CLEAR");

    printf("Current flags:  %ld (O_NONBLOCK is now SET)\n", newattr.mq_flags);

    mq_close(mqd);
    return 0;
}

# Compile all POSIX MQ programs with -lrt
gcc -o pmsg_getattr pmsg_getattr.c -lrt
gcc -o pmsg_setattr pmsg_setattr.c -lrt

Interview Questions & Answers

Q1. What fields does the mq_attr structure contain and what does each represent?
The mq_attr structure has four fields:

  • mq_flags — open description flags; only O_NONBLOCK is defined.
  • mq_maxmsg — maximum number of messages the queue can hold (set at creation, read-only after).
  • mq_msgsize — maximum size in bytes of a single message (set at creation, read-only after).
  • mq_curmsgs — number of messages currently in the queue; this is a snapshot and may be stale.
Q2. Which fields in mq_attr can be changed after queue creation?
Only mq_flags can be changed via mq_setattr(), and specifically only the O_NONBLOCK bit within it. mq_maxmsg and mq_msgsize are fixed at creation. mq_curmsgs is managed by the kernel.
Q3. What is the correct way to toggle O_NONBLOCK on a POSIX message queue?
The safe portable pattern is: call mq_getattr() to read current mq_flags, modify the O_NONBLOCK bit (set or clear), then call mq_setattr() with the updated structure. You should never construct an mq_attr from scratch and pass it directly to mq_setattr() because a future implementation might define other flags that you would accidentally clear.
Q4. What are the default values of mq_maxmsg and mq_msgsize on Linux?
On Linux, when no attributes are specified at queue creation (NULL passed), the defaults are mq_maxmsg = 10 and mq_msgsize = 8192 bytes. These values are implementation-defined and vary across platforms, so portable programs should always specify explicit values.
Q5. What does the oldattr parameter in mq_setattr() do?
If oldattr is non-NULL, mq_setattr() fills it with the queue’s attributes before applying the change — it is equivalent to doing mq_getattr() and mq_setattr() in one step. This is useful when you want to save the old state so you can restore it later, for example after temporarily enabling non-blocking mode for a critical section.
Q6. Why might mq_curmsgs be inaccurate by the time mq_getattr() returns?
Because other processes may be adding or removing messages concurrently. By the time mq_getattr() returns to your process, the count may have changed. mq_curmsgs is a point-in-time snapshot — it is not an atomic guarantee. For synchronisation, use blocking mq_receive() or notifications instead.
Q7. Does mq_setattr() affect the queue itself or just the open description?
mq_setattr() affects the open message queue description associated with the descriptor mqdes, not the underlying queue object. This means if two processes have the same queue open, changing O_NONBLOCK in one process via its descriptor does not affect the other process’s descriptor. Each open description has its own flags, similar to how O_NONBLOCK on file descriptors works via fcntl().

Leave a Reply

Your email address will not be published. Required fields are marked *