Sending Messages: mq_send() Priorities, Blocking & Non-Blocking

 

Sending Messages: mq_send()
Chapter 52 · File 2 of 3 | Priorities, Blocking & Non-Blocking

Sending a Message to a Queue

Once a POSIX message queue is open, you put messages into it using mq_send(). Unlike System V message queues (which always deliver in FIFO order), POSIX queues support message priorities. Every message has a numeric priority, and higher-priority messages are always received before lower-priority ones — regardless of the order they were sent.

mq_send() Function Signature

#include <mqueue.h>

int mq_send(
mqd_t mqdes, /* message queue descriptor */
const char *msg_ptr, /* pointer to message buffer */
size_t msg_len, /* length of message in bytes */
unsigned int msg_prio /* message priority (0 = lowest) */
);

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

Parameter Type Description
mqdes mqd_t Queue descriptor from mq_open() — must be open for writing (O_WRONLY or O_RDWR)
msg_ptr const char * Pointer to the message data buffer. Can be any binary or text data.
msg_len size_t Length of the message. Must be ≤ mq_msgsize. Zero-length is allowed.
msg_prio unsigned int Priority of the message. Higher = more urgent. Range: 0 to MQ_PRIO_MAX−1.

How Message Priorities Work

Each message has a non-negative integer priority. When a receiver calls mq_receive(), it always gets the oldest message with the highest priority. Within the same priority level, messages are delivered in FIFO order. Priority 0 is the lowest — higher numbers mean higher urgency.

Queue state after sending: “A” prio=5, “B” prio=10, “C” prio=5, “D” prio=1
Receive Order Message Priority Reason
1st “B” 10 Highest priority
2nd “A” 5 Next priority; sent before “C”
3rd “C” 5 Same priority as “A”, sent after
4th “D” 1 Lowest priority

Priority Range: MQ_PRIO_MAX

The maximum allowed priority value is defined by the constant MQ_PRIO_MAX (or via sysconf(_SC_MQ_PRIO_MAX)). POSIX requires it to be at least 32. The actual range varies significantly across platforms.

Platform MQ_PRIO_MAX Valid Range
Linux 32768 0 to 32767
Solaris 32 0 to 31
Tru64 256 0 to 255
POSIX minimum 32 0 to 31 (guaranteed)
Portability Tip: If you need your code to run on multiple platforms, keep priorities in the range 0–31 to stay within the guaranteed POSIX minimum. For Linux-only code, you can use up to 32767.

Error Conditions

Error Cause Fix
EMSGSIZE msg_len > mq_msgsize of the queue Check queue’s mq_msgsize via mq_getattr() before sending
EAGAIN Queue full & O_NONBLOCK set Retry later or use blocking mode
EINVAL msg_prio ≥ MQ_PRIO_MAX Use a valid priority value
EBADF mqdes not valid or not open for write Open with O_WRONLY or O_RDWR

Blocking vs Non-Blocking Behaviour

When the queue is full (mq_curmsgs == mq_maxmsg), the behaviour of mq_send() depends on the O_NONBLOCK flag:

O_NONBLOCK Queue Full Behaviour Use Case
NOT set (blocking) mq_send() blocks (sleeps) until a slot becomes free Simple pipelines where blocking is fine
SET (non-blocking) Returns immediately with errno = EAGAIN Event loops, real-time systems, polling

Code Examples

Example 1: Simple Blocking Send

/* sender.c — sends a single text message with default priority 0 */
/* compile: gcc -o sender sender.c -lrt */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mqueue.h>
#include <fcntl.h>

#define QUEUE_NAME  "/demo_queue"
#define MSG         "Hello from sender"
#define PRIORITY    0

int main(void)
{
    mqd_t mqd;

    /* Open existing queue for writing (blocking mode) */
    mqd = mq_open(QUEUE_NAME, O_WRONLY);
    if (mqd == (mqd_t) -1) {
        perror("mq_open");
        exit(EXIT_FAILURE);
    }

    /* Send message — will block if queue is full */
    if (mq_send(mqd, MSG, strlen(MSG), PRIORITY) == -1) {
        perror("mq_send");
        mq_close(mqd);
        exit(EXIT_FAILURE);
    }

    printf("Sent: \"%s\" with priority %d\n", MSG, PRIORITY);
    mq_close(mqd);
    return 0;
}

Example 2: Non-Blocking Send with EAGAIN Handling

/* nonblock_sender.c — uses O_NONBLOCK, retries if queue full */
/* compile: gcc -o nonblock_sender nonblock_sender.c -lrt */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <mqueue.h>
#include <fcntl.h>

#define QUEUE_NAME  "/demo_queue"
#define MAX_RETRIES 5

int main(void)
{
    mqd_t mqd;
    const char *msg = "Non-blocking message";
    int retries = 0;

    /* Open with O_NONBLOCK so mq_send never blocks */
    mqd = mq_open(QUEUE_NAME, O_WRONLY | O_NONBLOCK);
    if (mqd == (mqd_t) -1) { perror("mq_open"); exit(1); }

    while (retries < MAX_RETRIES) {
        if (mq_send(mqd, msg, strlen(msg), 5) == 0) {
            printf("Sent successfully on attempt %d\n", retries + 1);
            break;
        }

        if (errno == EAGAIN) {
            /* Queue is full — wait a bit and retry */
            printf("Queue full (attempt %d), retrying...\n", retries + 1);
            sleep(1);
            retries++;
        } else {
            /* A real error */
            perror("mq_send");
            break;
        }
    }

    if (retries == MAX_RETRIES)
        fprintf(stderr, "Failed after %d retries\n", MAX_RETRIES);

    mq_close(mqd);
    return 0;
}

Example 3: Send Messages with Different Priorities

/* priority_sender.c — sends 3 messages with different priorities */
/* compile: gcc -o priority_sender priority_sender.c -lrt */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mqueue.h>
#include <fcntl.h>

#define QUEUE_NAME "/prio_queue"
#define MAX_MSG    10
#define MSG_SIZE   128

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

    /* Create a fresh queue */
    attr.mq_flags   = 0;
    attr.mq_maxmsg  = MAX_MSG;
    attr.mq_msgsize = MSG_SIZE;
    attr.mq_curmsgs = 0;

    mqd = mq_open(QUEUE_NAME, O_CREAT | O_WRONLY, 0644, &attr);
    if (mqd == (mqd_t) -1) { perror("mq_open"); exit(1); }

    /* Send in this order — but receiver will get them in priority order */
    if (mq_send(mqd, "LOW priority msg",   16,  1) == -1) perror("send LOW");
    if (mq_send(mqd, "HIGH priority msg",  17, 10) == -1) perror("send HIGH");
    if (mq_send(mqd, "MED priority msg",   16,  5) == -1) perror("send MED");
    if (mq_send(mqd, "URGENT priority msg",20, 20) == -1) perror("send URGENT");

    printf("Sent 4 messages with priorities: 1, 10, 5, 20\n");
    printf("Receiver will get them as: URGENT(20), HIGH(10), MED(5), LOW(1)\n");

    mq_close(mqd);
    return 0;
}

Example 4: Sending a Struct as a Message

/* struct_sender.c — send a C struct as binary message data */
/* compile: gcc -o struct_sender struct_sender.c -lrt */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mqueue.h>
#include <fcntl.h>

#define QUEUE_NAME "/struct_queue"

/* Define the message payload structure */
typedef struct {
    int  sensor_id;
    float temperature;
    int  alarm_level;   /* 0=normal 1=warn 2=critical */
} SensorData;

int main(void)
{
    mqd_t mqd;
    struct mq_attr attr;
    SensorData reading = { .sensor_id = 42, .temperature = 38.5f, .alarm_level = 1 };

    attr.mq_flags   = 0;
    attr.mq_maxmsg  = 10;
    attr.mq_msgsize = sizeof(SensorData);   /* size the queue for our struct */
    attr.mq_curmsgs = 0;

    mqd = mq_open(QUEUE_NAME, O_CREAT | O_WRONLY, 0644, &attr);
    if (mqd == (mqd_t) -1) { perror("mq_open"); exit(1); }

    /* Cast struct pointer to char* — mq_send treats it as raw bytes */
    if (mq_send(mqd, (char *)&reading, sizeof(reading), reading.alarm_level) == -1) {
        perror("mq_send");
        mq_close(mqd);
        exit(1);
    }

    printf("Sent sensor data: id=%d temp=%.1f alarm=%d (priority=%d)\n",
           reading.sensor_id, reading.temperature,
           reading.alarm_level, reading.alarm_level);

    mq_close(mqd);
    return 0;
}

Example 5: Check Queue Limits Before Sending

/* safe_sender.c — always check mq_msgsize before sending */
/* compile: gcc -o safe_sender safe_sender.c -lrt */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mqueue.h>
#include <fcntl.h>

int safe_send(mqd_t mqd, const char *data, size_t len, unsigned int prio)
{
    struct mq_attr attr;

    /* Read current attributes to check the size limit */
    if (mq_getattr(mqd, &attr) == -1) {
        perror("mq_getattr");
        return -1;
    }

    if ((long)len > attr.mq_msgsize) {
        fprintf(stderr,
                "Error: message size %zu exceeds mq_msgsize %ld\n",
                len, attr.mq_msgsize);
        return -1;
    }

    return mq_send(mqd, data, len, prio);
}

int main(void)
{
    mqd_t mqd = mq_open("/safe_queue", O_WRONLY);
    if (mqd == (mqd_t) -1) { perror("mq_open"); exit(1); }

    const char *msg = "Checked message";
    if (safe_send(mqd, msg, strlen(msg), 3) == 0)
        printf("Message sent safely\n");

    mq_close(mqd);
    return 0;
}

Interview Questions & Answers

Q1. What happens if you call mq_send() with msg_len greater than mq_msgsize?
The call fails immediately with errno = EMSGSIZE. The message queue kernel enforces the size limit strictly. You should always check the queue’s mq_msgsize via mq_getattr() before sending to avoid this error.
Q2. How are messages with the same priority ordered in a POSIX message queue?
Within the same priority level, messages are ordered in FIFO (first-in, first-out) order — the message that was sent first is received first. So mq_send() places a new message after all existing messages of the same priority.
Q3. What error does mq_send() return when the queue is full and O_NONBLOCK is set?
errno = EAGAIN. This means “try again later” — the operation would have blocked but non-blocking mode prevented it. Your code should catch this error and implement a retry strategy (sleep and retry, use a timer, or drop the message based on application logic).
Q4. How does POSIX message queue priority compare to System V message type?
They serve different purposes. POSIX priorities control delivery order — the highest-priority message is always delivered next. System V message types control selective reception — the receiver can pick messages of a specific type or all types up to a given value. POSIX priority is simpler and better for urgent-message scenarios; System V type is better when the receiver needs to filter by message category.
Q5. Is msg_prio = 0 a valid priority for mq_send()? What does it mean?
Yes, 0 is valid and it represents the lowest possible priority. If your application does not need message priorities, always sending with priority 0 gives you simple FIFO behaviour — messages are received in the order they were sent.
Q6. Can you send a zero-length message using mq_send()?
Yes. Passing msg_len = 0 is permitted by POSIX. A zero-length message can be used as a signal or notification without any payload — similar to a semaphore post. The receiver will receive a message with length 0.
Q7. What is MQ_PRIO_MAX and how does it differ on Linux vs other platforms?
MQ_PRIO_MAX is the maximum number of distinct priority levels, defined in <mqueue.h> or queryable via sysconf(_SC_MQ_PRIO_MAX). POSIX requires at least 32. On Linux it is 32768, on Solaris 32, on Tru64 256. Valid priorities range from 0 to MQ_PRIO_MAX − 1. Passing a value ≥ MQ_PRIO_MAX causes EINVAL.

Leave a Reply

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