Skip to content

Day57 - POSIX Message Queue IPC

Overview

This chapter introduces POSIX message queue IPC (mqueue).

Unlike shared memory IPC, POSIX message queues are:

  • kernel-managed
  • message-oriented
  • synchronized by kernel
  • priority-aware
  • asynchronous capable

POSIX mqueue is designed for:

  • producer / consumer IPC
  • middleware event delivery
  • RT-style message passing
  • asynchronous userspace communication

POSIX Message Queue Architecture

sender process
    |
    | mq_send()
    v
+----------------------+
| kernel message queue |
|   /demo_mq           |
+----------------------+
    |
    | mq_receive()
    v
receiver process

The queue object exists inside kernel space.

Processes communicate by accessing the same named queue object.


Queue Naming

Queue names must start with /.

Example:

"/demo_mq"

The name is:

  • not a filesystem path
  • not a UNIX socket path
  • a kernel IPC namespace key

Linux exposes mqueue objects under:

/dev/mqueue

Core APIs

mq_open()

Create or open queue.

mqd_t mq_open(const char *name,
              int oflag,
              mode_t mode,
              struct mq_attr *attr);

mq_send()

Send one message.

mq_send(mq, msg, len, prio);

mq_receive()

Receive one message.

mq_receive(mq, buf, size, &prio);

mq_close()

Close queue descriptor.


Remove queue object name.


Queue Attributes

struct mq_attr

struct mq_attr {
    long mq_flags;
    long mq_maxmsg;
    long mq_msgsize;
    long mq_curmsgs;
};

mq_maxmsg

Maximum number of queued messages.


mq_msgsize

Maximum size of one message.

Receive buffer size must be:

>= mq_msgsize

Otherwise:

mq_receive() -> EMSGSIZE

mq_curmsgs

Current queued message count.


Message-Oriented IPC

Unlike stream sockets or pipes:

pipe/socket:
    byte stream

mqueue:
    message packet

Message boundaries are preserved.


Priority Queue

POSIX mqueue supports message priority.

mq_send(mq, msg, len, priority);

Kernel dequeue order:

  • higher priority first
  • FIFO inside same priority

Example:

prio=10
prio=5
prio=1

Receive order:

10 -> 5 -> 1

Blocking Queue Behavior

Sender

If queue becomes full:

mq_send()
-> blocking sleep

until receiver consumes messages.


Receiver

If queue becomes empty:

mq_receive()
-> blocking sleep

until sender sends messages.


Non-Blocking Queue

Using:

O_NONBLOCK

changes queue behavior.


Non-blocking sender

Queue full:

mq_send()
-> EAGAIN

Non-blocking receiver

Queue empty:

mq_receive()
-> EAGAIN

mq_notify()

POSIX async notification mechanism.

Queue transition:

empty -> non-empty

can trigger:

  • signal notification
  • thread callback

SIGEV_SIGNAL

sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGUSR1;

Kernel sends signal when queue becomes readable.


mq_notify() Characteristics

One-shot registration

After one notification:

registration automatically removed

Must re-register again.


Correct Pattern

1. register notify
2. wait notification
3. drain queue
4. register notify again

epoll + mqueue

Linux mqueue descriptors can be monitored by:

  • poll()
  • select()
  • epoll()

This allows:

everything becomes fd

architecture.


epoll Event Model

epoll_wait()
    -> EPOLLIN
    -> drain mq_receive() until EAGAIN

timerfd Integration

timerfd was integrated into epoll loop for:

  • periodic queue monitoring
  • queue statistics

Example:

mq_curmsgs

printed every second.


signalfd Integration

signalfd replaced traditional signal handlers.

Signals:

  • SIGINT
  • SIGTERM

became normal fd events inside epoll loop.


Unified Linux Event Loop

Final architecture:

epoll
 ├── mqueue
 ├── timerfd
 └── signalfd

This is a classic Linux daemon architecture.


POSIX vs Linux Event Model

mq_notify()

POSIX-style async notification:

kernel pushes signal to userspace

epoll

Linux-style readiness model:

userspace pulls events from kernel

Shared Memory vs Message Queue

Shared Memory

Pros:

  • zero-copy
  • highest throughput
  • lowest latency

Cons:

  • synchronization complexity
  • user-managed queue logic

POSIX Message Queue

Pros:

  • kernel synchronization
  • simple API
  • message boundary
  • priority support

Cons:

  • kernel copy overhead
  • queue capacity limitation
  • lower throughput