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
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.
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.
opens queue
refcount=1
opens queue
refcount=2
marked for
deletion
refcount=0
destroyed
There are 7 key functions you need to know:
mqd_t descriptor.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.
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.
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.
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
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:
Kernel sends you a real-time signal (e.g., SIGRTMIN) when a message arrives
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.
-lrt (the POSIX real-time library). Example: gcc myprogram.c -o myprogram -lrt/myqueue. On Linux they appear under /dev/mqueue/ when the mqueue filesystem is mounted.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.