Message Queue Attributes mq_attr structure

 

52.4 – Message Queue Attributes
mq_attr structure  |  mq_getattr() & mq_setattr()  |  Chapter 52 – POSIX MQ

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

Structure Definition
#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 mq_setattr(). Only O_NONBLOCK is defined by SUSv3.

|
Queue Object Level
mq_maxmsg mq_msgsize mq_curmsgs

mq_maxmsg and mq_msgsize are set at creation and are immutable. mq_curmsgs is dynamic — it changes as messages are sent/received. All three describe the queue object, not the description.

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 for mq_maxmsg
  • /proc/sys/fs/mqueue/msgsize_default — default value for mq_msgsize
  • /proc/sys/fs/mqueue/msg_max — upper limit for mq_maxmsg
  • /proc/sys/fs/mqueue/msgsize_max — upper limit for mq_msgsize

mq_getattr() – Reading Queue Attributes

Function Prototype
#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

Function Prototype
#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.

Important: 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

Example 1: Create a Queue With Custom Attributes

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

Example 2: Read Attributes with mq_getattr()

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

Example 3: Toggle O_NONBLOCK with mq_setattr()

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, &current);
    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, &current);
    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;
}

Example 4: Command-Line Queue Creator (from TLPI Listing 52-2)

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

Example 5: Reading /proc System-Wide Limits

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);
    }
}

Example 6: Practical – Check Queue Before Sending

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

Q1. What are the four fields of struct mq_attr and what does each mean?

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).

Q2. Can you change mq_maxmsg after a queue is created?

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.

Q3. What happens if you pass NULL as the mq_attr pointer to mq_open()?

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.

Q4. What is the purpose of mq_setattr() and what can it actually change?

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.

Q5. Where does the kernel store mq_maxmsg and mq_msgsize — in the description or in the queue?

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.

Q6. What does mq_curmsgs represent and when is it useful?

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.

Q7. How do you find the system-wide limits for mq_maxmsg and mq_msgsize on Linux?

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.

Q8. What does the oldattr parameter in mq_setattr() do?

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.

Q9. Why must mq_maxmsg and mq_msgsize both be greater than 0?

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.

Q10. What happens if you try to send a message larger than mq_msgsize?

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.

Leave a Reply

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