POSIX Message Queues Linux IPC: Message-Based Communication Between Processes

 

POSIX Message Queues
Chapter 52 — Linux IPC: Message-Based Communication Between Processes
Part 1
Overview & Concepts
TLPI
Chapter 52
IPC
Inter-Process Comm.

What Are POSIX Message Queues?

POSIX message queues let two or more processes talk to each other by sending and receiving messages. Think of them like a mailbox — one process drops a letter in, another picks it up. The messages stay in the queue until someone reads them, and each message carries a priority number so urgent messages come out first.

They were added to Linux in kernel 2.6.6, and you also need glibc 2.3.4 or later. The kernel feature is controlled by the CONFIG_POSIX_MQUEUE option.

Link your programs with -lrt (real-time library) when using POSIX message queue functions.

Key Terms in This Tutorial

mq_open() mq_send() mq_receive() mq_close() mq_unlink() mq_notify() mq_getattr() mq_setattr() mqd_t mq_attr Priority Queue Reference Counting Async Notification

POSIX vs System V Message Queues — Key Differences

Linux supports two types of message queues: the older System V style and the newer POSIX style. Both allow processes to exchange data as discrete messages, but they work differently in important ways.

Feature System V Message Queue POSIX Message Queue
Identification Integer key (IPC key) Name string (like /myqueue)
Message selection By integer type, flexible selection Strict priority order always
Deletion Deleted immediately with msgctl() Reference counted — deleted after last close
Async notification Not available Available via mq_notify()
API style msgsnd(), msgrcv(), msgctl() mq_send(), mq_receive() — file-like
Descriptor type int (msqid) mqd_t (int on Linux)
Kernel config Always available CONFIG_POSIX_MQUEUE needed

Simple way to remember: POSIX queues are priority-ordered mailboxes with names. System V queues are more like typed bins you pick from.

Reference Counting — What Does It Mean?

POSIX message queues are reference counted. This means the kernel keeps track of how many processes currently have the queue open. When you call mq_unlink() to delete a queue, the kernel does not destroy it immediately if other processes still have it open. It marks the queue for deletion, and the actual destruction happens only after the last process closes it.

Reference Counting Lifecycle
Process A
opens queue
refcount=1
Process B
opens queue
refcount=2
mq_unlink()
marked for
deletion
A & B close
refcount=0
destroyed
Queue data is still accessible to A and B even after unlink — until both close it

The POSIX Message Queue API — All Functions at a Glance

There are 7 key functions you need to know:

mq_open()
Create a new queue or open an existing one. Returns a mqd_t descriptor.
mq_send()
Write a message with a given priority to the queue.
mq_receive()
Read the highest-priority message from the queue. Blocks if empty.
mq_close()
Close the queue descriptor (like close() for files). Does NOT delete the queue.
mq_unlink()
Remove the queue name and schedule deletion once all descriptors are closed.
mq_getattr() / mq_setattr()
Get or change queue attributes like max messages, max message size, current count.
mq_notify()
Register to be notified (via signal or thread) when a message arrives on an empty queue.

Typical Message Queue Usage Flow
mq_open() — create/open queue
mq_send() [Writer]
mq_receive() [Reader]
mq_close() — close descriptor
mq_unlink() — remove queue name

Priority-Based Message Ordering

Every message sent via mq_send() carries a priority number (an unsigned integer). When multiple messages are in the queue, mq_receive() always returns the one with the highest priority first. If two messages have the same priority, they come out in FIFO order (first sent = first received).

This is fundamentally different from System V queues where you pick messages by type. In POSIX queues, the ordering is automatic and strict.

Queue State After 3 mq_send() Calls
Message: “ALARM”Priority 10 ← NEXT OUT
Message: “UPDATE”Priority 5
Message: “LOG”Priority 1 ← LAST OUT
Higher priority number = comes out first

Code Example: Sending messages with different priorities

#include <stdio.h>
#include <string.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>

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

    /* Set queue attributes: max 10 messages, each up to 256 bytes */
    attr.mq_flags   = 0;
    attr.mq_maxmsg  = 10;
    attr.mq_msgsize = 256;
    attr.mq_curmsgs = 0;

    /* Create queue with read+write permission for owner */
    mqd = mq_open("/demo_queue", O_CREAT | O_RDWR, 0600, &attr);
    if (mqd == (mqd_t)-1) {
        perror("mq_open");
        return 1;
    }

    /* Send three messages with different priorities */
    mq_send(mqd, "LOG entry",   9,  1);   /* priority 1  — lowest  */
    mq_send(mqd, "UPDATE data", 11, 5);   /* priority 5  — medium  */
    mq_send(mqd, "ALARM!",      6,  10);  /* priority 10 — highest */

    printf("3 messages sent with priorities 1, 5, and 10\n");

    mq_close(mqd);
    mq_unlink("/demo_queue");
    return 0;
}

Compile: gcc priority_demo.c -o priority_demo -lrt
When you call mq_receive(), “ALARM!” comes out first even though it was sent last.

Queue Naming Rules

POSIX message queues are identified by a name string, unlike System V queues which use integer keys. The name rules are:

  • ✅ Must start with a forward slash: /myqueue
  • ✅ Only one slash — no subdirectories: /myqueue (not /dir/myqueue)
  • ✅ Followed by one or more non-slash characters
  • ✅ On Linux, queues appear under /dev/mqueue/ (mount the mqueue filesystem)
  • ✅ Names are visible system-wide to any process with permission
/* Valid names */
mqd_t q1 = mq_open("/sensor_data",     O_RDWR, ...);
mqd_t q2 = mq_open("/cmd_queue",       O_RDWR, ...);
mqd_t q3 = mq_open("/app_log_queue",   O_RDWR, ...);

/* Invalid — these will fail */
mqd_t bad1 = mq_open("noSlash",        O_RDWR, ...); /* missing leading / */
mqd_t bad2 = mq_open("/dir/queue",     O_RDWR, ...); /* two slashes      */

Linux tip: Mount the mqueue filesystem to inspect queues from the shell:
mount -t mqueue none /dev/mqueue
Then ls /dev/mqueue/ shows all queues and cat /dev/mqueue/myqueue shows its attributes.

Complete Hello World: Writer and Reader

Let’s write two programs that communicate through a POSIX message queue.

writer.c — sends a message

#include <stdio.h>
#include <string.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>

#define QUEUE_NAME  "/hello_queue"
#define MSG_SIZE    256

int main(void)
{
    mqd_t mqd;
    struct mq_attr attr;
    const char *msg = "Hello from writer!";

    attr.mq_flags   = 0;
    attr.mq_maxmsg  = 5;
    attr.mq_msgsize = MSG_SIZE;
    attr.mq_curmsgs = 0;

    /* Create queue (or open if already exists) */
    mqd = mq_open(QUEUE_NAME, O_CREAT | O_WRONLY, 0644, &attr);
    if (mqd == (mqd_t)-1) {
        perror("mq_open writer");
        return 1;
    }

    /* Send message with priority 5 */
    if (mq_send(mqd, msg, strlen(msg) + 1, 5) == -1) {
        perror("mq_send");
        mq_close(mqd);
        return 1;
    }

    printf("Sent: %s\n", msg);
    mq_close(mqd);
    return 0;
}

reader.c — receives the message

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

#define QUEUE_NAME  "/hello_queue"
#define MSG_SIZE    256

int main(void)
{
    mqd_t mqd;
    char buf[MSG_SIZE + 1];
    unsigned int priority;
    ssize_t bytes;

    /* Open existing queue for reading */
    mqd = mq_open(QUEUE_NAME, O_RDONLY);
    if (mqd == (mqd_t)-1) {
        perror("mq_open reader");
        return 1;
    }

    /* Receive message — blocks until one is available */
    bytes = mq_receive(mqd, buf, MSG_SIZE, &priority);
    if (bytes == -1) {
        perror("mq_receive");
        mq_close(mqd);
        return 1;
    }
    buf[bytes] = '\0';

    printf("Received (priority %u): %s\n", priority, buf);

    mq_close(mqd);
    mq_unlink(QUEUE_NAME);   /* cleanup: remove the queue */
    return 0;
}

How to run:
gcc writer.c -o writer -lrt
gcc reader.c -o reader -lrt
./writer → then ./reader

Async Notification — A Unique POSIX Feature

One thing POSIX message queues can do that System V queues cannot is asynchronous notification. Instead of blocking in mq_receive() waiting for a message, your process can register a callback. When a message arrives on an otherwise empty queue, the kernel wakes your process either by:

Signal delivery
Kernel sends you a real-time signal (e.g., SIGRTMIN) when a message arrives
Thread invocation
Kernel starts a new thread in your process that runs your callback function

This is covered in detail in Part 4 of this series (mq_notify). For now, remember that this exists and makes POSIX queues much more flexible for event-driven programs.

Interview Questions — POSIX Message Queues Overview
Q1: What are the three main differences between POSIX and System V message queues?
(1) POSIX queues are reference counted — deletion is deferred until last close. SysV queues are deleted immediately. (2) POSIX queues always deliver messages in strict priority order. SysV queues allow selection by integer type. (3) POSIX queues support async notification via mq_notify(). SysV queues have no such feature.
Q2: If two messages have the same priority, in what order are they received?
FIFO order — the message that was sent first is received first. Priority is the primary sort key; arrival time is the secondary sort key.
Q3: What does “reference counted” mean in the context of POSIX message queues?
The kernel keeps a count of how many processes have the queue open. mq_unlink() marks the queue name for deletion and removes it from the filesystem, but the actual queue data is only freed when the last open descriptor is closed (refcount reaches zero).
Q4: What kernel version introduced POSIX message queue support in Linux?
Linux kernel 2.6.6. You also need glibc 2.3.4 or later and the kernel must be built with CONFIG_POSIX_MQUEUE=y.
Q5: What linker flag is required when compiling programs that use POSIX message queues?
-lrt (the POSIX real-time library). Example: gcc myprogram.c -o myprogram -lrt
Q6: How are POSIX message queues named, and where are they visible in the filesystem?
Names must start with a single slash and contain no further slashes, e.g., /myqueue. On Linux they appear under /dev/mqueue/ when the mqueue filesystem is mounted.
Q7: What type does mq_open() return, and what is it on Linux specifically?
It returns mqd_t. On Linux, mqd_t is defined as int. On Solaris it is void *. SUSv3 guarantees it is not an array type, so it can be used in assignments and passed by value.

Leave a Reply

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