mqueue FS
/proc/mounts
poll/select/epoll
Linux Goes Beyond the Standard
POSIX defines the core message queue API. Linux adds several non-standard but highly useful features on top:
- Message queues are exposed as files in a virtual filesystem (
mqueue), so you can inspect them with standard tools likels,cat, andrm. - Message queue descriptors are implemented as real file descriptors, so you can monitor them with
select(),poll(), andepoll().
These features are Linux-specific. They are not available on other UNIX implementations that follow only the POSIX standard.
On Linux, POSIX IPC objects (message queues, shared memory, semaphores) are implemented inside virtual filesystems. For message queues, this filesystem type is called mqueue. Once you mount it, every message queue appears as a regular file in the mount directory.
Mount it like this (requires root):
# Create a mount point
mkdir /dev/mqueue
# Mount the mqueue filesystem
mount -t mqueue none /dev/mqueue
The first argument to -t is the filesystem type: mqueue. The source (here none) can be any string — it only appears in /proc/mounts and the mount command output. It has no functional meaning.
QSIZE (bytes of data), NOTIFY_PID, NOTIFY type, SIGNOVerify the mount:
cat /proc/mounts | grep mqueue
# Output:
# none /dev/mqueue mqueue rw 0 0
Check directory permissions:
ls -ld /dev/mqueue
# Output:
# drwxrwxrwt 2 root root 40 Jul 26 12:09 /dev/mqueue
# ^
# The 't' means sticky bit is set
The sticky bit (t in permissions) means that even though the directory is world-writable, an unprivileged process can only delete (unlink) message queues it owns. This is the same behaviour as /tmp.
Once the filesystem is mounted, queue operations map directly to filesystem operations:
# Create a queue (using a helper program or from C code)
./pmsg_create -c /newq
# List all queues
ls /dev/mqueue
# Output: newq
# Delete a queue using rm (equivalent to mq_unlink)
rm /dev/mqueue/newq
Using rm to delete a queue is exactly equivalent to calling mq_unlink("/newq") in C. Both remove the queue name from the filesystem.
/* Equivalent in C */
#include <mqueue.h>
int main(void) {
/* These two lines do the same thing as: rm /dev/mqueue/newq */
if (mq_unlink("/newq") == -1)
perror("mq_unlink");
return 0;
}
Each queue’s virtual file contains a single line of text with status information. You can read it with cat:
# Create queue and write 7 bytes
./pmsg_create -c /mq
./pmsg_send /mq abcdefg
cat /dev/mqueue/mq
# Output:
# QSIZE:7 NOTIFY:0 SIGNO:0 NOTIFY_PID:0
| Field | Meaning |
|---|---|
QSIZE |
Total bytes of data currently in the queue (sum of all message bodies) |
NOTIFY_PID |
PID of the process registered for notification (0 = none registered) |
NOTIFY |
Notification method: 0=SIGEV_SIGNAL, 1=SIGEV_NONE, 2=SIGEV_THREAD |
SIGNO |
Signal number used when NOTIFY=0 (SIGEV_SIGNAL); 0 otherwise |
Example with signal notification registered (SIGUSR1 = signal 10 on x86):
# Start signal-based notify program in background
./mq_notify_sig /mq &
# PID 18158
cat /dev/mqueue/mq
# QSIZE:7 NOTIFY:0 SIGNO:10 NOTIFY_PID:18158
# NOTIFY=0 means SIGEV_SIGNAL; SIGNO=10 means SIGUSR1
Example with thread notification registered:
# Start thread-based notify program
./mq_notify_thread /mq &
# PID 18160
cat /dev/mqueue/mq
# QSIZE:7 NOTIFY:2 SIGNO:0 NOTIFY_PID:18160
# NOTIFY=2 means SIGEV_THREAD
Reading these fields in C (reading the file programmatically):
#include <stdio.h>
#include <stdlib.h>
void print_queue_info(const char *qname)
{
char path[256];
FILE *fp;
char buf[256];
/* Build path like /dev/mqueue/myqueue (strip leading slash from qname) */
snprintf(path, sizeof(path), "/dev/mqueue/%s",
qname[0] == '/' ? qname + 1 : qname);
fp = fopen(path, "r");
if (!fp) { perror("fopen"); return; }
if (fgets(buf, sizeof(buf), fp))
printf("Queue '%s': %s", qname, buf);
fclose(fp);
}
int main(void) {
print_queue_info("/myqueue");
return 0;
}
On Linux, a message queue descriptor is a genuine file descriptor. This means you can pass it to poll(), select(), or epoll_ctl() just like a socket or pipe file descriptor.
This solves a classic problem with System V message queues: you cannot easily wait for input on both a System V message queue and a socket (or pipe) in the same select() call. With POSIX message queues on Linux, you can.
descriptor (fd)
select() /
epoll
detected
Example using poll() to monitor a message queue and a pipe simultaneously:
#include <mqueue.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MQ_NAME "/poll_demo_mq"
#define MSG_SIZE 256
int main(void)
{
mqd_t mqd;
struct mq_attr attr = { 0, 10, MSG_SIZE, 0 };
int pipefd[2];
struct pollfd fds[2];
char buf[MSG_SIZE];
/* Create queue (non-blocking) */
mq_unlink(MQ_NAME);
mqd = mq_open(MQ_NAME, O_CREAT | O_RDONLY | O_NONBLOCK, 0600, &attr);
if (mqd == (mqd_t)-1) { perror("mq_open"); exit(1); }
/* Create a pipe to simulate another fd source */
if (pipe(pipefd) == -1) { perror("pipe"); exit(1); }
/* Set up poll: watch mqd (fd) and read-end of pipe */
fds[0].fd = (int)mqd; /* On Linux, mqd_t is a real fd */
fds[0].events = POLLIN;
fds[1].fd = pipefd[0];
fds[1].events = POLLIN;
printf("Waiting for data on MQ or pipe...\n");
/* In a real program this would loop; here just one iteration */
int ret = poll(fds, 2, 5000); /* 5-second timeout */
if (ret == -1) { perror("poll"); }
else if (ret == 0) { printf("Timeout — no data arrived.\n"); }
else {
if (fds[0].revents & POLLIN) {
ssize_t n = mq_receive(mqd, buf, MSG_SIZE, NULL);
if (n > 0) printf("MQ message: %.*s\n", (int)n, buf);
}
if (fds[1].revents & POLLIN) {
ssize_t n = read(pipefd[0], buf, MSG_SIZE);
if (n > 0) printf("Pipe data: %.*s\n", (int)n, buf);
}
}
mq_close(mqd);
mq_unlink(MQ_NAME);
close(pipefd[0]); close(pipefd[1]);
return 0;
}
gcc -o mq_poll_demo mq_poll_demo.c -lmqueue
./mq_poll_demo
Example using epoll with a message queue:
#include <mqueue.h>
#include <sys/epoll.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MQ_NAME "/epoll_mq"
#define MSG_SIZE 256
int main(void)
{
mqd_t mqd;
struct mq_attr attr = { 0, 10, MSG_SIZE, 0 };
int epfd;
struct epoll_event ev, events[4];
char buf[MSG_SIZE];
mq_unlink(MQ_NAME);
mqd = mq_open(MQ_NAME, O_CREAT | O_RDONLY | O_NONBLOCK, 0600, &attr);
if (mqd == (mqd_t)-1) { perror("mq_open"); exit(1); }
epfd = epoll_create1(0);
if (epfd == -1) { perror("epoll_create1"); exit(1); }
ev.events = EPOLLIN;
ev.data.fd = (int)mqd;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, (int)mqd, &ev) == -1) {
perror("epoll_ctl"); exit(1);
}
printf("epoll waiting on message queue...\n");
int nfds = epoll_wait(epfd, events, 4, 5000); /* 5-second timeout */
for (int i = 0; i < nfds; i++) {
if (events[i].data.fd == (int)mqd) {
ssize_t n = mq_receive(mqd, buf, MSG_SIZE, NULL);
if (n > 0) printf("Got message: %.*s\n", (int)n, buf);
}
}
close(epfd);
mq_close(mqd);
mq_unlink(MQ_NAME);
return 0;
}
poll(), select(), or epoll() is Linux-specific. POSIX does not require that mqd_t be implemented as a file descriptor. Code using this feature will not compile or run on AIX, Solaris, or macOS./dev/mqueue), each message queue appears as a file in that directory. You can list queues with ls, inspect their status with cat, and delete them with rm./tmp — you cannot delete someone else’s queue without privilege.QSIZE (total bytes in the queue), NOTIFY_PID (PID registered for notification, or 0), NOTIFY (notification method: 0=signal, 1=none, 2=thread), and SIGNO (signal number if notification is via signal).select(), poll(), and epoll() alongside sockets, pipes, and other file descriptors. This lets you wait for messages on a queue and network I/O in the same event loop — something impossible with System V message queues, which are not file descriptors.mqd_t to be a file descriptor. Using poll() or epoll() on a message queue descriptor is a Linux-specific feature. Code relying on this will fail on other UNIX systems such as macOS or Solaris./etc/fstab:
none /dev/mqueue mqueue defaults 0 0
This mounts the filesystem automatically at boot time.
