mq_receive()
Intermediate
52 – TLPI
What is mq_receive()?
Once messages are placed on a POSIX message queue using mq_send(), a process on the other end needs to read them. That is exactly what mq_receive() does. It reads the highest-priority message that is currently sitting on the queue. If two messages share the same priority, the one that arrived first (FIFO order) is returned first.
Think of a POSIX message queue like a post office sorting room where letters are arranged by urgency (priority). The clerk (mq_receive()) always picks the most urgent letter from the top of the pile.
#include <mqueue.h>
ssize_t mq_receive(mqd_t mqdes,
char *msg_ptr,
size_t msg_len,
unsigned int *msg_prio);
/* Returns: number of bytes in received message on success, -1 on error */
/* Link with: -lrt (e.g. gcc prog.c -lrt) */
| Parameter | Type | Meaning |
|---|---|---|
mqdes |
mqd_t | Message queue descriptor returned by mq_open() |
msg_ptr |
char * | Pointer to buffer where the received message will be stored |
msg_len |
size_t | Size of the buffer — MUST be ≥ mq_msgsize attribute of the queue |
msg_prio |
unsigned int * | If not NULL, priority of received message is stored here |
Messages are always retrieved in descending priority order, not insertion order.
The buffer size you pass as msg_len must be at least as large as the queue’s mq_msgsize attribute. If it is smaller, mq_receive() fails with EMSGSIZE. You should always call mq_getattr() first to find the correct buffer size.
/* mq_msgsize = 256 */
mq_receive(mqd, buf, 64, NULL);
/* FAILS: EMSGSIZE */
mq_getattr(mqd, &attr);
char *buf = malloc(attr.mq_msgsize);
mq_receive(mqd, buf, attr.mq_msgsize, NULL);
/* Works */
priority message
Return byte count
→ Block until
message arrives
→ Return -1
errno=EAGAIN
Note: Unlike pipes, there is no EOF concept on an empty queue. If there are no writers, mq_receive() still blocks — it does not return 0 (end-of-file).
A simple receiver that reads one message and prints its content and priority.
/* pmsg_receive_basic.c
* Compile: gcc pmsg_receive_basic.c -o pmsg_receive_basic -lrt
* Run: ./pmsg_receive_basic /myqueue
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mqueue.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "Usage: %s /queue-name\n", argv[0]);
exit(EXIT_FAILURE);
}
/* Open the queue read-only */
mqd_t mqd = mq_open(argv[1], O_RDONLY);
if (mqd == (mqd_t)-1) {
perror("mq_open");
exit(EXIT_FAILURE);
}
/* Get queue attributes so we know the max message size */
struct mq_attr attr;
if (mq_getattr(mqd, &attr) == -1) {
perror("mq_getattr");
exit(EXIT_FAILURE);
}
printf("Queue: max %ld messages, max msg size = %ld bytes\n",
attr.mq_maxmsg, attr.mq_msgsize);
/* Allocate buffer of exactly mq_msgsize bytes */
char *buffer = malloc(attr.mq_msgsize);
if (buffer == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
unsigned int priority;
/* This call BLOCKS if queue is empty */
ssize_t bytes_read = mq_receive(mqd, buffer, attr.mq_msgsize, &priority);
if (bytes_read == -1) {
perror("mq_receive");
free(buffer);
exit(EXIT_FAILURE);
}
printf("Received %zd bytes at priority %u\n", bytes_read, priority);
printf("Message: %.*s\n", (int)bytes_read, buffer);
free(buffer);
mq_close(mqd);
return 0;
}
Use O_NONBLOCK when you do not want to block waiting for a message.
/* pmsg_receive_nonblock.c
* Compile: gcc pmsg_receive_nonblock.c -o pmsg_receive_nonblock -lrt
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <mqueue.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "Usage: %s /queue-name\n", argv[0]);
exit(EXIT_FAILURE);
}
/* Open with O_NONBLOCK: calls return immediately if queue empty */
mqd_t mqd = mq_open(argv[1], O_RDONLY | O_NONBLOCK);
if (mqd == (mqd_t)-1) {
perror("mq_open");
exit(EXIT_FAILURE);
}
struct mq_attr attr;
mq_getattr(mqd, &attr);
char *buffer = malloc(attr.mq_msgsize);
if (!buffer) { perror("malloc"); exit(EXIT_FAILURE); }
unsigned int priority;
ssize_t n = mq_receive(mqd, buffer, attr.mq_msgsize, &priority);
if (n == -1) {
if (errno == EAGAIN) {
/* Queue was empty — not an error, just no data available */
printf("Queue is empty (EAGAIN). No message received.\n");
} else {
perror("mq_receive");
}
} else {
printf("Received %zd bytes, priority=%u: %.*s\n",
n, priority, (int)n, buffer);
}
free(buffer);
mq_close(mqd);
return 0;
}
Loop with O_NONBLOCK to read every message currently in the queue.
/* drain_queue.c
* Reads all available messages until EAGAIN, then exits.
* Compile: gcc drain_queue.c -o drain_queue -lrt
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <mqueue.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "Usage: %s /queue-name\n", argv[0]);
exit(EXIT_FAILURE);
}
mqd_t mqd = mq_open(argv[1], O_RDONLY | O_NONBLOCK);
if (mqd == (mqd_t)-1) { perror("mq_open"); exit(EXIT_FAILURE); }
struct mq_attr attr;
mq_getattr(mqd, &attr);
char *buf = malloc(attr.mq_msgsize);
if (!buf) { perror("malloc"); exit(EXIT_FAILURE); }
int count = 0;
unsigned int prio;
while (1) {
ssize_t n = mq_receive(mqd, buf, attr.mq_msgsize, &prio);
if (n == -1) {
if (errno == EAGAIN) {
/* No more messages */
break;
}
perror("mq_receive");
break;
}
count++;
printf("[%d] priority=%-3u bytes=%-4zd msg=%.*s\n",
count, prio, n, (int)n, buf);
}
printf("Total messages drained: %d\n", count);
free(buf);
mq_close(mqd);
return 0;
}
A complete working example: the sender creates the queue and sends 3 messages with different priorities. The receiver reads them in priority order.
/* sender.c — Creates queue, sends 3 messages with different priorities.
* Compile: gcc sender.c -o sender -lrt
* Run first: ./sender /testqueue
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
int main(void)
{
const char *qname = "/testqueue";
/* Create queue: max 10 messages, each up to 256 bytes */
struct mq_attr attr = { .mq_flags = 0,
.mq_maxmsg = 10,
.mq_msgsize = 256,
.mq_curmsgs = 0 };
mqd_t mqd = mq_open(qname, O_CREAT | O_WRONLY, 0644, &attr);
if (mqd == (mqd_t)-1) { perror("mq_open"); exit(EXIT_FAILURE); }
/* Send messages: (content, priority) */
struct { const char *msg; unsigned int prio; } msgs[] = {
{ "msg-a (prio 5)", 5 },
{ "msg-b (prio 0)", 0 },
{ "msg-c (prio 10)", 10 },
};
for (int i = 0; i < 3; i++) {
if (mq_send(mqd, msgs[i].msg, strlen(msgs[i].msg) + 1,
msgs[i].prio) == -1) {
perror("mq_send");
} else {
printf("Sent: \"%s\" at priority %u\n",
msgs[i].msg, msgs[i].prio);
}
}
mq_close(mqd);
printf("All messages sent.\n");
return 0;
}
/* receiver.c — Reads all messages (comes out in priority order).
* Compile: gcc receiver.c -o receiver -lrt
* Run after sender: ./receiver /testqueue
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <mqueue.h>
#include <fcntl.h>
int main(void)
{
const char *qname = "/testqueue";
mqd_t mqd = mq_open(qname, O_RDONLY | O_NONBLOCK);
if (mqd == (mqd_t)-1) { perror("mq_open"); exit(EXIT_FAILURE); }
struct mq_attr attr;
mq_getattr(mqd, &attr);
char *buf = malloc(attr.mq_msgsize);
unsigned int prio;
int n;
while (1) {
n = mq_receive(mqd, buf, attr.mq_msgsize, &prio);
if (n == -1) {
if (errno == EAGAIN) break;
perror("mq_receive"); break;
}
printf("Received [priority=%u]: %s\n", prio, buf);
}
/* Expected output (priority order, not send order):
* Received [priority=10]: msg-c (prio 10)
* Received [priority=5]: msg-a (prio 5)
* Received [priority=0]: msg-b (prio 0)
*/
free(buf);
mq_close(mqd);
mq_unlink(qname); /* Remove queue from filesystem */
return 0;
}
Key Terms
The call fails immediately with errno set to EMSGSIZE. The buffer size (msg_len) must be greater than or equal to the queue’s mq_msgsize attribute. Always call mq_getattr() first and allocate a buffer of exactly attr.mq_msgsize bytes.
Always in descending priority order. The message with the highest numeric priority is returned first. If two messages share the same priority, they are returned in FIFO (first-in, first-out) order.
It blocks indefinitely until a message becomes available. Unlike reading from a pipe, it does not return 0 (EOF) just because there are no writers attached.
It returns -1 and sets errno to EAGAIN (also reported as EWOULDBLOCK on some systems).
Call mq_getattr(mqd, &attr) and read attr.mq_msgsize. Then allocate a buffer of that size. In cooperating-process applications where the message size is agreed upon in advance, mq_getattr() can be skipped.
Pass a pointer to an unsigned int as the fourth argument (msg_prio). After a successful call, that variable holds the priority of the message that was received. Pass NULL if you do not need the priority.
A pipe reader sees end-of-file (read returns 0) when there are no more writers. A POSIX message queue has no such EOF concept. If the queue is empty and no writer is currently sending, mq_receive() simply blocks — it does not signal end-of-file.
On older Linux systems (glibc < 2.17), yes — you must link with -lrt to get the POSIX real-time library. On modern systems (glibc ≥ 2.17), the MQ functions are included in glibc directly, so -lrt is technically optional but harmless to include.
