POSIX Message Queues POSIX vs System V Message Queues

 

POSIX Message Queues
Part 9 — POSIX vs System V Message Queues: Complete Comparison
📬 POSIX
Modern API
🏛️ System V
Legacy API
⚖️ Compare
Pros & Cons

Two Competing APIs for the Same Job

Linux provides two separate message queue APIs: the older System V interface (from the 1980s AT&T UNIX) and the newer POSIX interface (standardised in the 1990s). Both let processes exchange messages, but they differ significantly in design philosophy, API ergonomics, and Linux-specific capabilities.

Understanding the differences is important both for choosing the right API in new projects and for maintaining or porting legacy code.

Key Terms
mq_open / mq_send / mq_receive msgget / msgsnd / msgrcv reference counting mq_notify message priority IPC key mqueue filesystem ipcs / ipcrm poll/select/epoll

1. API Comparison at a Glance
Aspect POSIX Message Queues System V Message Queues
Identifier Human-readable name like /myqueue Integer key (key_t) via ftok()
Open/Create mq_open(name, flags, mode, attr) msgget(key, flags)
Send mq_send(mqd, buf, len, priority) msgsnd(msqid, msgbuf, msgsz, flags)
Receive mq_receive(mqd, buf, bufsize, &prio) msgrcv(msqid, msgbuf, msgsz, type, flags)
Delete mq_unlink(name) msgctl(msqid, IPC_RMID, NULL)
Notification mq_notify() — signal or thread None — must poll or block
Priority Built-in (0 to MQ_PRIO_MAX−1) Via message type field (manual)
Inspection tools ls, cat, rm on mqueue FS ipcs, ipcrm
Reference counting Yes — queue deleted when unlinked and all descriptors closed No — IPC_RMID deletes immediately even if others have it open
poll/select/epoll (Linux) Yes — mqd_t is a real fd No — msqid is not a fd
Persistence Kernel — survives until unlinked or reboot Kernel — survives until IPC_RMID or reboot
Link library -lmqueue (on Linux) No extra library needed

2. Advantages of POSIX Message Queues
✅ Simpler, File-like API
POSIX IPC uses human-readable names (/myqueue) that look like file paths. You don’t need ftok() gymnastics to generate a numeric key. The open/close/unlink model is familiar to anyone who has worked with files.
✅ Reference Counting
A POSIX message queue is not actually deleted when you call mq_unlink(). It remains alive until the last process that has it open calls mq_close(). This makes cleanup safe and predictable. With System V, IPC_RMID deletes the queue immediately — any other process still using it will start getting errors.
✅ Asynchronous Notification (mq_notify)
POSIX queues provide mq_notify() which lets one process be asynchronously notified — via a signal or a new thread — when a message arrives on an empty queue. System V has no equivalent; you must either block in msgrcv() or poll the queue in a loop.
✅ Built-in Message Priority
POSIX queues support message priorities natively. Sending with a higher priority number causes that message to be received first. System V supports type-based ordering, but it requires manual management of type values as a proxy for priority.
✅ I/O Multiplexing (Linux only)
On Linux, POSIX queue descriptors are real file descriptors, so they work with select(), poll(), and epoll(). You can monitor a message queue and a network socket in the same event loop. System V queues are identified by integers, not file descriptors, so this is impossible.

3. When System V Message Queues Are Still Used

Despite POSIX’s advantages, System V message queues remain in use for several reasons:

  • Legacy code — large C codebases written in the 1990s use System V IPC extensively. Porting carries risk and cost.
  • Type-based selective receivemsgrcv() lets you receive only messages of a specific type (or the lowest type ≤ a threshold). POSIX only gives you the highest-priority message available; you cannot selectively receive by message category without extra logic.
  • No extra library — System V IPC is built into the kernel with no link-time dependency. POSIX message queues require -lmqueue on Linux.
  • Availability on older kernels — early embedded Linux kernels sometimes lacked POSIX message queue support.

4. Code Side-by-Side: Sending a Message

The same task — create a queue and send one message — using both APIs:

POSIX Message Queue
#include <mqueue.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    mqd_t mqd;
    struct mq_attr attr = {
        .mq_maxmsg  = 10,
        .mq_msgsize = 256
    };
    const char *msg = "hello";

    /* Create queue with a readable name */
    mq_unlink("/myq");
    mqd = mq_open("/myq",
                  O_CREAT | O_WRONLY,
                  0600, &attr);
    if (mqd == (mqd_t)-1) {
        perror("mq_open"); exit(1);
    }

    /* Send with priority 1 */
    if (mq_send(mqd, msg,
                strlen(msg), 1) == -1) {
        perror("mq_send"); exit(1);
    }

    printf("Sent: %s\n", msg);
    mq_close(mqd);
    mq_unlink("/myq");
    return 0;
}
/* Compile: gcc ... -lmqueue */
System V Message Queue
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct msgbuf {
    long mtype;
    char mtext[256];
};

int main(void)
{
    key_t key;
    int msqid;
    struct msgbuf buf;

    /* Generate a key from a file path */
    key = ftok("/tmp/myq_key", 42);
    if (key == -1) {
        perror("ftok"); exit(1);
    }

    /* Create queue */
    msqid = msgget(key,
                   IPC_CREAT | 0600);
    if (msqid == -1) {
        perror("msgget"); exit(1);
    }

    buf.mtype = 1;
    strcpy(buf.mtext, "hello");

    /* Send */
    if (msgsnd(msqid, &buf,
               strlen(buf.mtext)+1,
               0) == -1) {
        perror("msgsnd"); exit(1);
    }

    printf("Sent: %s\n", buf.mtext);

    /* Delete immediately */
    msgctl(msqid, IPC_RMID, NULL);
    return 0;
}
/* Compile: gcc ... (no extra lib) */

5. Reference Counting Demo (POSIX Advantage)

This demonstrates that mq_unlink() does not destroy the queue while another process still has it open:

/* mq_refcount_demo.c */
#include <mqueue.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#define QNAME "/refcount_test"

int main(void)
{
    struct mq_attr attr = { 0, 5, 64, 0 };
    mqd_t writer, reader;

    /* Create queue */
    mq_unlink(QNAME);
    writer = mq_open(QNAME, O_CREAT | O_WRONLY, 0600, &attr);
    reader = mq_open(QNAME, O_RDONLY, 0, NULL);

    if (writer == (mqd_t)-1 || reader == (mqd_t)-1) {
        perror("mq_open"); exit(1);
    }

    /* Unlink the name — queue still alive because reader/writer are open */
    mq_unlink(QNAME);
    printf("Unlinked '%s' — but queue is still alive (descriptors open)\n",
           QNAME);

    /* Send and receive still work! */
    mq_send(writer, "test", 4, 1);

    char buf[64];
    ssize_t n = mq_receive(reader, buf, 64, NULL);
    if (n > 0) printf("Received after unlink: %.*s\n", (int)n, buf);

    /* Now close both descriptors — queue is finally destroyed */
    mq_close(writer);
    mq_close(reader);
    printf("Both descriptors closed — queue memory released by kernel.\n");

    return 0;
}
/* With System V, msgctl(IPC_RMID) would destroy the queue
 * immediately, making the msgrcv call fail. */

Interview Questions & Answers
Q1. What are the main differences between POSIX and System V message queues?
POSIX uses readable names and a file-like API; System V uses integer keys from ftok(). POSIX supports reference counting, async notification via mq_notify(), and on Linux uses real file descriptors enabling poll()/epoll(). System V offers type-based selective receive but has no notification mechanism and its IDs are not file descriptors.
Q2. What does “reference counted” mean for POSIX message queues?
It means the queue’s kernel resources are not freed until both: (a) the name has been removed via mq_unlink(), AND (b) all processes that have it open have called mq_close(). Calling mq_unlink() alone just removes the name — existing open descriptors continue to work normally.
Q3. Why can you use poll() on a POSIX queue descriptor on Linux but not on a System V queue ID?
Linux implements POSIX message queue descriptors as real kernel file descriptors, so they participate in all fd-based I/O mechanisms. System V queue identifiers are opaque integers managed by a completely separate kernel subsystem; they are not file descriptors and the poll() system call has no knowledge of them.
Q4. Can you select specific message types with POSIX receive, like you can with System V msgrcv()?
Not directly. POSIX mq_receive() always returns the message with the highest priority (ties broken by arrival order). You cannot request a specific message type. System V msgrcv() accepts a msgtyp argument that lets you receive only messages of a specific type or the lowest type within a range. This is a genuine advantage of System V for certain use cases.
Q5. What happens in System V when you call IPC_RMID while another process is blocked in msgrcv()?
The IPC_RMID call destroys the queue immediately. Any process blocked in msgrcv() on that queue gets EINVAL. This is dangerous in programs where multiple processes share a queue — you must coordinate deletion carefully. With POSIX queues, the reference-counting mechanism prevents this class of bug.
Q6. For new embedded Linux IPC code, which API would you choose and why?
POSIX message queues for most new code because: the API is simpler and more readable, reference counting makes lifetime management safer, mq_notify() enables efficient event-driven designs, and on Linux, epoll integration allows embedding the queue in a broader event loop alongside sockets and timers. Choose System V only if integrating with existing System V code or if type-based selective receive is critical.

Leave a Reply

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