mq_open / mq_close / mq_unlink

 

mq_open / mq_close / mq_unlink
Chapter 52 — Creating, Opening and Removing POSIX Message Queues

mq_open() — Creating or Opening a Queue

mq_open() is the starting point. It either creates a new queue or opens an existing one. The return value is an mqd_t descriptor.

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

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

/* Returns: mqd_t descriptor on success, (mqd_t)-1 on error */

name: The queue name. Must start with /, no other slashes. Example: "/sensor_queue".
oflag: Access mode flags combined with bitwise OR.
mode: Permission bits (like chmod). Only needed when creating.
attr: Pointer to struct mq_attr to set limits. Pass NULL for system defaults.

Flag Meaning
O_RDONLY Open for receiving (reading) only
O_WRONLY Open for sending (writing) only
O_RDWR Open for both sending and receiving
O_CREAT Create the queue if it doesn’t exist. Needs mode and attr.
O_EXCL With O_CREAT: fail with EEXIST if the queue already exists
O_NONBLOCK Non-blocking mode. send/receive return EAGAIN instead of blocking

mq_open(name, oflag, mode, attr)
O_CREAT not set
Open existing queue
ENOENT if not found
O_CREAT | O_EXCL
Create new only
EEXIST if already exists
O_CREAT alone
Create if not exists,
open if already exists

mq_open() — Practical Examples
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>

/* Example 1: Server creates a queue for receiving requests */
mqd_t create_server_queue(const char *name)
{
    struct mq_attr attr;
    attr.mq_flags   = 0;
    attr.mq_maxmsg  = 10;    /* max 10 messages in queue */
    attr.mq_msgsize = 512;   /* max 512 bytes per message */
    attr.mq_curmsgs = 0;

    /* O_EXCL ensures we don't accidentally open a stale queue */
    mqd_t mqd = mq_open(name,
                         O_CREAT | O_EXCL | O_RDONLY,
                         S_IRUSR | S_IWUSR,  /* rw for owner only */
                         &attr);
    if (mqd == (mqd_t)-1) {
        if (errno == EEXIST) {
            fprintf(stderr, "Queue %s already exists! Remove it first.\n", name);
        } else {
            perror("mq_open server");
        }
    }
    return mqd;
}

/* Example 2: Client opens existing queue for sending */
mqd_t open_client_queue(const char *name)
{
    /* Client only needs write access, queue must already exist */
    mqd_t mqd = mq_open(name, O_WRONLY);
    if (mqd == (mqd_t)-1) {
        perror("mq_open client");
    }
    return mqd;
}

/* Example 3: Open or create in non-blocking mode */
mqd_t open_nonblocking(const char *name)
{
    struct mq_attr attr = {0};
    attr.mq_maxmsg  = 5;
    attr.mq_msgsize = 256;

    mqd_t mqd = mq_open(name,
                         O_CREAT | O_RDWR | O_NONBLOCK,
                         0660,
                         &attr);
    return mqd;
}

int main(void)
{
    const char *qname = "/demo_queue";

    /* Clean up any leftover queue from previous run */
    mq_unlink(qname);  /* ignore error if it doesn't exist */

    mqd_t server_mqd = create_server_queue(qname);
    if (server_mqd == (mqd_t)-1)
        return 1;

    printf("Server queue created: fd=%d\n", (int)server_mqd);

    /* Open same queue with write permission for "client" side */
    mqd_t client_mqd = mq_open(qname, O_WRONLY);
    if (client_mqd == (mqd_t)-1) {
        perror("client mq_open");
        mq_close(server_mqd);
        mq_unlink(qname);
        return 1;
    }
    printf("Client opened queue: fd=%d\n", (int)client_mqd);

    mq_close(server_mqd);
    mq_close(client_mqd);
    mq_unlink(qname);
    return 0;
}

mq_close() — Closing a Queue Descriptor
#include <mqueue.h>

int mq_close(mqd_t mqd);
/* Returns: 0 on success, -1 on error */

mq_close() closes the message queue descriptor mqd. Think of it exactly like close() for file descriptors.

Important points:

• The queue itself is not removed from the kernel. Only the descriptor is closed.

• If the process registered for notification via mq_notify() on this descriptor, calling mq_close() deregisters that notification.

• All descriptors are automatically closed when the process exits, but the queue persists until mq_unlink() is called.

• Reference counting: the kernel keeps a count. Closing the last descriptor does not destroy the queue.

/* Always check return value */
if (mq_close(mqd) == -1)
    perror("mq_close");

/* Typical cleanup pattern in production code */
void cleanup_queue(mqd_t mqd, const char *name, int do_unlink)
{
    if (mqd != (mqd_t)-1) {
        if (mq_close(mqd) == -1)
            perror("mq_close");
    }
    if (do_unlink && name != NULL) {
        if (mq_unlink(name) == -1 && errno != ENOENT)
            perror("mq_unlink");
    }
}

mq_unlink() — Removing a Queue
#include <mqueue.h>

int mq_unlink(const char *name);
/* Returns: 0 on success, -1 on error (ENOENT if name not found) */

mq_unlink() removes the queue name from the system. This is analogous to unlink() for files.

How deletion actually works (reference counting):

Step 1
Process A: mq_open()
ref_count = 1
Process B: mq_open()
ref_count = 2
Step 2
Process A: mq_unlink()
Name removed from /dev/mqueue/
ref_count = 2 (still alive!)
Step 3
Process A: mq_close() → ref=1
Process B: mq_close() → ref=0
Queue memory freed!
/* Example: Safe unlink with existence check */
#include <mqueue.h>
#include <errno.h>
#include <stdio.h>

void safe_unlink(const char *name)
{
    if (mq_unlink(name) == -1) {
        if (errno == ENOENT) {
            printf("Queue %s does not exist, nothing to remove.\n", name);
        } else {
            perror("mq_unlink");
        }
    } else {
        printf("Queue %s unlinked successfully.\n", name);
    }
}

/* Example: Registering cleanup with atexit */
#include <stdlib.h>

static const char *g_qname = NULL;

void cleanup_on_exit(void)
{
    if (g_qname != NULL)
        mq_unlink(g_qname);
}

int main(void)
{
    g_qname = "/atexit_queue";
    atexit(cleanup_on_exit);

    struct mq_attr attr = {.mq_maxmsg=5, .mq_msgsize=64};
    mqd_t mqd = mq_open(g_qname, O_CREAT|O_RDWR, 0600, &attr);
    if (mqd == (mqd_t)-1) { perror("mq_open"); return 1; }

    printf("Queue created. Will be unlinked on exit.\n");
    /* ... do work ... */
    mq_close(mqd);
    return 0; /* atexit will call cleanup_on_exit */
}

Complete Example — Proper Open/Close/Unlink with Error Handling
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>

#define QNAME    "/ep_demo"
#define MAXMSG   8
#define MSGSIZE  128

static mqd_t s_mqd = (mqd_t)-1;

/* Called on abnormal exit */
static void cleanup(void)
{
    if (s_mqd != (mqd_t)-1)
        mq_close(s_mqd);
    mq_unlink(QNAME);
}

int main(void)
{
    struct mq_attr attr = {
        .mq_flags   = 0,
        .mq_maxmsg  = MAXMSG,
        .mq_msgsize = MSGSIZE,
        .mq_curmsgs = 0,
    };

    atexit(cleanup);

    /* Remove any stale queue from a previous crash */
    mq_unlink(QNAME);

    /* Create new queue */
    s_mqd = mq_open(QNAME,
                     O_CREAT | O_EXCL | O_RDWR,
                     S_IRUSR | S_IWUSR,
                     &attr);
    if (s_mqd == (mqd_t)-1) {
        perror("mq_open");
        exit(EXIT_FAILURE);
    }
    printf("[+] Queue '%s' created (fd=%d)\n", QNAME, (int)s_mqd);

    /* Send a test message */
    const char *msg = "Test message";
    if (mq_send(s_mqd, msg, strlen(msg)+1, 1) == -1) {
        perror("mq_send");
        exit(EXIT_FAILURE);
    }
    printf("[+] Sent: \"%s\"\n", msg);

    /* Receive it back */
    char buf[MSGSIZE];
    unsigned int prio;
    if (mq_receive(s_mqd, buf, MSGSIZE, &prio) == -1) {
        perror("mq_receive");
        exit(EXIT_FAILURE);
    }
    printf("[+] Received: \"%s\" (prio=%u)\n", buf, prio);

    /* Explicit cleanup (atexit would also do this) */
    mq_close(s_mqd);
    s_mqd = (mqd_t)-1;
    mq_unlink(QNAME);
    printf("[+] Queue removed. Exiting.\n");
    return 0;
}

Common Errors and What They Mean
errno Function Cause
ENOENT mq_open, mq_unlink Queue doesn’t exist and O_CREAT not specified
EEXIST mq_open O_CREAT | O_EXCL used but queue already exists
EACCES mq_open Permission denied (check mode bits and uid)
EINVAL mq_open Name invalid (missing leading slash) or attr values out of range
EMFILE mq_open Process has too many open file descriptors
ENFILE mq_open System-wide file descriptor limit reached
ENOSPC mq_open Kernel limit on number of message queues exceeded

Interview Questions — mq_open / mq_close / mq_unlink

Q1: What is the difference between mq_close() and mq_unlink()?
mq_close() closes the per-process descriptor (like closing a file) but the queue remains in the kernel. mq_unlink() removes the queue name and schedules the queue for destruction, but the queue memory is only freed when all open descriptors are also closed.

Q2: When would you use O_EXCL with mq_open()?
When the server process wants to guarantee it is creating a fresh queue and not accidentally opening a stale one from a previous crash. If the queue already exists, mq_open() fails with EEXIST, prompting cleanup before retry.

Q3: Can two processes open the same POSIX MQ with different access modes?
Yes. Process A can open it with O_RDONLY (receiver) and Process B with O_WRONLY (sender). The queue supports multiple simultaneous descriptors.

Q4: What happens to a POSIX MQ when the creating process exits without calling mq_unlink()?
All file descriptors are closed automatically on exit, but the queue persists in the kernel. Other processes can still open it. The queue stays until someone explicitly calls mq_unlink() or the system reboots.

Q5: What is the second parameter of mq_open() when NOT creating?
Only the 2-argument form is used: mq_open(name, oflag) — no mode and no attr. These are only needed when O_CREAT is specified.

Q6: What error is returned if you try to open a queue for writing but the queue was created with mode 0444?
EACCES (Permission denied). The mode bits are checked by the kernel just like file permissions.

Q7: After mq_unlink(), can other processes still use the queue?
Yes, if they already have it open. The name is removed (new opens will fail with ENOENT) but existing descriptors remain valid until they are closed. This is the same semantics as unlink() on files.

Next: Message Queue Attributes

Learn how to configure and query queue properties with mq_getattr() and mq_setattr().

← Back to Intro Go to File 3 →

Leave a Reply

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