System V Message Queues Displaying All Message Queues on the System

 

System V Message Queues
Chapter 46 · Section 46.6 — Displaying All Message Queues on the System
2
Special Operations
2
Code Examples
9+
Interview Qs

Key Concepts in This Section
MSG_INFO MSG_STAT SEM_INFO SHM_INFO entries array _GNU_SOURCE msgctl() maxind ipcs command EINVAL EACCES IPC table

Two Ways to List All IPC Objects

When you run ipcs -q in a terminal, it shows you all System V message queues on the system. How does ipcs get this information? There are two methods the kernel provides:

  1. Method 1: /proc filesystem — Read files under /proc/sysvipc/. This was covered in Section 45.7 of TLPI.
  2. Method 2: Linux-specific msgctl() operations — Use the special MSG_INFO and MSG_STAT operations of msgctl(). This is what Section 46.6 covers, and this is how ipcs actually works on many Linux systems.
Mental Model: The kernel maintains an internal array (the “entries array”) of slots for all IPC objects. Each slot either points to a live IPC object or is empty. MSG_INFO tells you the highest slot index, and MSG_STAT lets you fetch data for any given slot by index.

Understanding the Kernel’s Entries Array

The Linux kernel maintains an internal table of slots for each type of IPC object (message queues, semaphore sets, shared memory segments). Each slot in this table either holds a pointer to a live IPC object or is empty.

Kernel IPC Entries Array (Message Queues)
EMPTY
index 0
EMPTY
index 1
msqid=98306
index 2 ✓
EMPTY
index 3
msqid=163844
index 4 ✓
···
MSG_INFO returns maxind = 4 (highest used index). MSG_STAT(2) fetches msqid 98306. MSG_STAT(4) fetches msqid 163844.

The key insight is that the IPC identifier (msqid) is not the same as the array index. The array index is just a slot number (0, 1, 2, …). The msqid is a larger number computed from the slot index combined with a sequence counter to prevent reuse confusion. MSG_STAT takes an index and returns the msqid as its function return value.

The Two Special msgctl() Operations
MSG_INFO

Purpose: Two things at once:

  1. Fills a struct msginfo with resource consumption statistics for all message queues system-wide (number of queues, total bytes used, etc.).
  2. Returns (as the function’s return value) the index of the highest occupied slot in the entries array. This is called maxind.

Analogy: MSG_INFO is like asking the library “how many shelves do you have, and what is the highest shelf number that has a book on it?” — you use the answer to know how far to scan.

Requires #define _GNU_SOURCE before including headers. The return value of msgctl() when using MSG_INFO is the highest index (maxind), not an error code.
MSG_STAT

Purpose: Retrieve the msqid_ds structure for the IPC object at a given array index (not an msqid). This is how you read metadata for each queue when scanning.

Differs from IPC_STAT in two ways:

IPC_STAT MSG_STAT
First argument IPC identifier (msqid) Array index (0, 1, 2…)
Return value 0 on success The msqid of that slot (if occupied)
On empty slot EINVAL (invalid msqid) EINVAL (empty slot)
No permission EACCES EACCES (ignore and continue scanning)
Also requires #define _GNU_SOURCE.

Algorithm: How to List All Message Queues

The TLPI book describes a two-step algorithm (which is how ipcs works internally):

1
Call msgctl(0, MSG_INFO, (struct msqid_ds*)&info). The return value is maxind — the highest occupied slot index in the message queue entries array. This tells you how far to scan.
2
Loop from index 0 through maxind (inclusive). For each index, call msgctl(index, MSG_STAT, &ds). On success, the return value is the msqid for that slot and ds holds the queue’s metadata. Ignore EINVAL (empty slot) and EACCES (no permission) — these are expected during scanning.
Race Condition Warning: Between the MSG_INFO call and the MSG_STAT loop, another process may create or delete queues. The scan may therefore miss newly created queues or report stale info for deleted ones. This is inherent in the design — the same limitation applies to ipcs. Always treat the output as a snapshot, not a guarantee.
Analogy: This is like asking a phone directory “how many pages are there?” (MSG_INFO) and then reading page by page (MSG_STAT), skipping blank pages. If someone adds or removes entries while you’re reading, you may miss them.

Analogous Operations for Other IPC Types

The same pattern applies to the other two System V IPC mechanisms — semaphore sets and shared memory segments:

IPC Type Info Operation Stat Operation Relevant ctl Function
Message Queues MSG_INFO MSG_STAT msgctl()
Semaphore Sets SEM_INFO SEM_STAT semctl()
Shared Memory SHM_INFO SHM_STAT shmctl()

All six constants require #define _GNU_SOURCE to be defined. The algorithm to enumerate all objects is identical across all three types — call the INFO variant to get maxind, then loop with the STAT variant.

Code Example 1 — List All Message Queues (like ipcs -q)

This program implements the two-step algorithm described above. It mirrors the svmsg_ls program from Listing 46-6 in TLPI.

#define _GNU_SOURCE          /* Required for MSG_INFO, MSG_STAT, struct msginfo */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/msg.h>

int main(void)
{
    int maxind;           /* Highest occupied index in entries array */
    int msqid;            /* IPC identifier returned by MSG_STAT */
    int idx;              /* Loop index for scanning */
    struct msginfo info;  /* Receives resource stats from MSG_INFO */
    struct msqid_ds ds;   /* Receives queue metadata from MSG_STAT */

    /*
     * Step 1: MSG_INFO
     *   - Fills 'info' with system-wide resource statistics
     *   - Return value = highest occupied slot index (maxind)
     *   - First argument is ignored (pass 0)
     *   - Cast msginfo ptr to (struct msqid_ds *) — required by API signature
     */
    maxind = msgctl(0, MSG_INFO, (struct msqid_ds *) &info);
    if (maxind == -1) {
        perror("msgctl MSG_INFO");
        exit(EXIT_FAILURE);
    }
    printf("maxind: %d\n\n", maxind);

    /*
     * Step 2: Loop from 0 to maxind, using MSG_STAT for each index.
     *
     * MSG_STAT:
     *   - First arg is the array INDEX (not a queue ID)
     *   - On success: fills 'ds', returns the msqid of that slot
     *   - On empty slot: returns -1, errno = EINVAL  → skip
     *   - On no permission: returns -1, errno = EACCES → skip
     */
    printf("%-8s %-10s %-14s %s\n", "index", "ID", "key", "messages");
    printf("%-8s %-10s %-14s %s\n", "-----", "--", "---", "--------");

    for (idx = 0; idx <= maxind; idx++) {
        msqid = msgctl(idx, MSG_STAT, &ds);

        if (msqid == -1) {
            if (errno == EINVAL || errno == EACCES)
                continue;   /* Empty slot or no permission — normal, skip */
            perror("msgctl MSG_STAT");
            continue;
        }

        /* Live queue found: print its details */
        printf("%-8d %-10d 0x%08lx     %lu\n",
               idx,
               msqid,
               (unsigned long) ds.msg_perm.__key,
               (unsigned long) ds.msg_qnum);
    }

    return 0;
}
Compile: gcc -D_GNU_SOURCE -o list_queues list_queues.c && ./list_queues
Create some queues first: ipcmk -Q (if available) or use msgget() in another program. Compare output with ipcs -q.

Code Example 2 — Show Resource Usage with MSG_INFO

This example focuses on using MSG_INFO to print system-wide message queue resource statistics, not just the list of queues. Useful for monitoring queue pressure in production systems.

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/msg.h>

int main(void)
{
    struct msginfo info;
    int maxind;

    /*
     * MSG_INFO fills struct msginfo with system-wide resource usage:
     *   msgpool  - number of message queue headers allocated
     *   msgmap   - number of entries in message map
     *   msgmax   - max bytes per message (same as MSGMAX)
     *   msgmnb   - max bytes per queue (same as MSGMNB)
     *   msgmni   - max queues on system (same as MSGMNI)
     *   msgssz   - message segment size
     *   msgtql   - max total messages
     *   msgseg   - max number of message segments
     *
     * The RETURN VALUE is the highest occupied index in the entries array.
     */
    maxind = msgctl(0, MSG_INFO, (struct msqid_ds *) &info);
    if (maxind == -1) {
        perror("msgctl MSG_INFO");
        exit(EXIT_FAILURE);
    }

    printf("===== Message Queue Resource Info (MSG_INFO) =====\n\n");
    printf("Highest occupied entry index : %d\n", maxind);
    printf("Max queues on system (msgmni): %d\n", info.msgmni);
    printf("Max bytes per message (msgmax): %d\n", info.msgmax);
    printf("Max bytes per queue  (msgmnb): %d\n", info.msgmnb);
    printf("Max total messages   (msgtql): %d\n", info.msgtql);
    printf("\n");
    printf("To list all queues, scan indices 0..%d using MSG_STAT.\n", maxind);
    return 0;
}
Use Case: In a system monitoring tool or watchdog process, you can call MSG_INFO periodically to check if the number of live message queues is growing unexpectedly, which may indicate a resource leak (a process creating queues without deleting them).

How ipcs Uses This Internally

The ipcs utility is a standard tool on every Linux system. When you run ipcs -q, it internally performs exactly the two-step algorithm above:

  1. Calls msgctl(MSG_INFO) to discover maxind
  2. Loops with msgctl(MSG_STAT) to read each live queue

The shell session in TLPI Listing 46-6’s commentary confirms this — the output of the custom svmsg_ls program exactly matches ipcs -q output, showing the same msqid values, keys, and message counts.

Shell Commands for Reference:
ipcs -q — list all message queues
ipcs -q -i <msqid> — detailed info for one queue
ipcrm -q <msqid> — remove a specific queue
ipcrm --all=msg — remove all message queues (use with caution!)

Interview Questions
Q1. What are the two ways to list all System V IPC objects on a Linux system?
Method 1: Read the /proc filesystem — specifically /proc/sysvipc/msg, /proc/sysvipc/sem, and /proc/sysvipc/shm. These files contain tabular data about all live IPC objects. Method 2: Use the Linux-specific msgctl() (or semctl/shmctl) operations MSG_INFO and MSG_STAT — this is what the ipcs command uses internally.
Q2. What is the difference between IPC_STAT and MSG_STAT?
Both retrieve the msqid_ds structure. The key differences are: IPC_STAT takes an IPC identifier (msqid) as the first argument and returns 0 on success. MSG_STAT takes an array index as the first argument and returns the msqid of that slot on success (or -1 on error). IPC_STAT is for querying a known queue. MSG_STAT is for scanning all queues by index.
Q3. What does MSG_INFO return and what does it fill?
MSG_INFO fills a struct msginfo with system-wide resource usage statistics for message queues (limits like msgmni, msgmax, msgmnb, and current usage statistics). Its return value is the highest occupied slot index (maxind) in the kernel’s message queue entries array. This is used as the upper bound for scanning with MSG_STAT.
Q4. Why must we ignore EINVAL and EACCES when looping with MSG_STAT?
When scanning all indices from 0 to maxind, many slots may be empty — no queue exists at that index. An empty slot causes MSG_STAT to fail with EINVAL. Some live queues may have permissions that deny the calling process access, causing EACCES. Both of these are expected and normal during an exhaustive scan. Ignoring them allows the scan to continue past these “holes” in the table.
Q5. What is the difference between an IPC identifier (msqid) and an array index?
The array index is a simple slot number (0, 1, 2, …) in the kernel’s internal entries array. The msqid (IPC identifier) is a larger number computed as index + (sequence_counter × MSGMNI). The sequence counter increments each time a slot is reused, ensuring that a stale msqid from a deleted queue cannot accidentally refer to a new queue occupying the same slot. MSG_STAT takes an index; all other operations take an msqid.
Q6. What feature-test macro is required for MSG_INFO and MSG_STAT?
_GNU_SOURCE must be defined before including the System V IPC header files. This is because MSG_INFO, MSG_STAT, and the associated struct msginfo are Linux/GNU extensions, not part of the POSIX standard. The same applies to SEM_INFO, SEM_STAT, SHM_INFO, and SHM_STAT.
Q7. Is there a race condition in the MSG_INFO + MSG_STAT scanning algorithm? Explain.
Yes. The two steps are not atomic. Between calling MSG_INFO (to get maxind) and completing the MSG_STAT scan loop, other processes may create or delete queues. A newly created queue at an index higher than maxind will be missed. A queue deleted before MSG_STAT reaches its index will appear as an EINVAL (empty slot). The scan provides a point-in-time snapshot, not a consistent view. This is a known limitation acknowledged in TLPI.
Q8. How does this mechanism generalize to semaphores and shared memory?
The exact same pattern applies: For semaphores: semctl(0, SEM_INFO, ...) to get maxind, then semctl(idx, SEM_STAT, ...) to scan. For shared memory: shmctl(0, SHM_INFO, ...) to get maxind, then shmctl(idx, SHM_STAT, ...) to scan. All six constants (MSG_INFO, MSG_STAT, SEM_INFO, SEM_STAT, SHM_INFO, SHM_STAT) require _GNU_SOURCE.
Q9. What shell command is equivalent to the MSG_INFO/MSG_STAT scan program?
The ipcs -q command. It lists all System V message queues with their msqid, key, owner, permissions, and message count — exactly what the two-step scan produces. ipcs without flags shows all three IPC types. ipcs -q -i <msqid> shows detailed info for a specific queue (equivalent to IPC_STAT for that queue).

Leave a Reply

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