Creating & Opening a Message Queue

 

Chapter 46 · File 2 of 6

Creating & Opening a Message Queue

msgget() · IPC Keys · Flags · Permissions

Before you can send or receive any messages, you need a queue. The msgget() system call either creates a new queue or opens an existing one. This file covers everything about msgget — from the basic API signature to key generation strategies.

Section 46.1 — The msgget() System Call

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflg);
/* Returns: queue identifier (msqid) on success, -1 on error */

It takes two arguments:

  • key — a unique identifier for the queue. Two processes that pass the same key will get the same queue.
  • msgflg — a bitmask combining permission bits AND control flags (like IPC_CREAT).

On success, it returns the msqid — an integer that uniquely identifies the queue. Use this in every subsequent call.

Understanding IPC Keys

The key_t key argument is how processes agree on which queue to use. There are three common ways to get a key:

Three Ways to Get an IPC Key
Method How Best for
IPC_PRIVATE Use the constant IPC_PRIVATE as key (value = 0) Parent-child communication; the created queue ID is passed via fork or env var
ftok() Derive key from a filename + project ID Unrelated processes that both know the same file path
Hard-coded key Pick a number manually (e.g., 1234) Simple tests; not recommended for production

The ftok() Function

ftok() generates a key from a file path + a 1-byte project ID. Two processes calling ftok() with the same arguments get the same key — that’s how they find each other’s queue.

#include <sys/ipc.h>

key_t ftok(const char *pathname, int proj_id);
/* Returns a key_t. The file must exist. proj_id uses only low 8 bits. */

Gotcha: ftok() uses the file’s inode number and device number to generate the key. If the file is deleted and recreated, it gets a new inode → different key even with the same path! Always use a file that won’t be recreated (like /etc/passwd or your application’s binary).

The msgflg Argument

msgflg is a bitwise OR of permission bits and control flags:

Flag Value Meaning
IPC_CREAT 01000 Create queue if it doesn’t exist
IPC_EXCL 02000 Fail with EEXIST if queue already exists (used with IPC_CREAT)
0666 octal rwx permission bits — same as file permissions
0600 octal Only owner can read/write
msgget() Decision Logic
Call: msgget(key, flags)
↓ Does a queue with this key already exist?
YES →
• If IPC_CREAT + IPC_EXCL → EEXIST error
• Otherwise → return existing queue ID
NO →
• If IPC_CREAT set → create new queue, return new ID
• If IPC_CREAT not set → ENOENT error

Code Example 1 — Create with IPC_PRIVATE

The simplest case: create a private queue and use it in the same process (or pass the ID to a child via fork).

/* create_private.c */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/wait.h>
#include <unistd.h>

struct Msg { long mtype; char mtext[64]; };

int main(void)
{
    /* IPC_PRIVATE always creates a NEW queue — guaranteed unique */
    int msqid = msgget(IPC_PRIVATE, IPC_CREAT | 0666);
    if (msqid == -1) { perror("msgget"); exit(1); }

    printf("[Parent] Created queue ID = %d\n", msqid);

    pid_t pid = fork();
    if (pid == -1) { perror("fork"); exit(1); }

    if (pid == 0) {
        /* Child: send a message */
        struct Msg m = { .mtype = 1 };
        snprintf(m.mtext, sizeof(m.mtext), "Hello from child PID %d", getpid());
        if (msgsnd(msqid, &m, sizeof(m.mtext), 0) == -1)
            perror("msgsnd");
        else
            printf("[Child]  Sent: '%s'\n", m.mtext);
        exit(0);
    } else {
        /* Parent: wait for child, then receive */
        wait(NULL);
        struct Msg rcv;
        if (msgrcv(msqid, &rcv, sizeof(rcv.mtext), 0, 0) == -1)
            perror("msgrcv");
        else
            printf("[Parent] Got:  '%s'\n", rcv.mtext);

        /* Clean up */
        msgctl(msqid, IPC_RMID, NULL);
        printf("[Parent] Queue removed.\n");
    }
    return 0;
}

Code Example 2 — Using ftok() for Unrelated Processes

This pattern is used when two completely separate programs (not parent-child) need to share a queue. Both agree on the same file and project ID.

/* queue_server_setup.c — Server creates the queue */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define PROJ_ID  'A'           /* Single char used as project identifier */
#define KEY_FILE "/tmp/myapp"  /* Must exist; both server and client use this */

int main(void)
{
    /* Step 1: Make sure the key file exists */
    FILE *f = fopen(KEY_FILE, "w");
    if (!f) { perror("fopen"); exit(1); }
    fclose(f);

    /* Step 2: Generate key from file + project ID */
    key_t key = ftok(KEY_FILE, PROJ_ID);
    if (key == -1) { perror("ftok"); exit(1); }
    printf("Generated key = 0x%x\n", (unsigned int)key);

    /* Step 3: Create the queue — IPC_EXCL ensures we don't reuse a stale one */
    int msqid = msgget(key, IPC_CREAT | IPC_EXCL | 0660);
    if (msqid == -1) {
        if (errno == EEXIST) {
            /* Queue already exists — open it */
            msqid = msgget(key, 0660);
            printf("Opened existing queue ID = %d\n", msqid);
        } else {
            perror("msgget");
            exit(1);
        }
    } else {
        printf("Created new queue ID = %d\n", msqid);
    }

    /* Now use msqid for sending/receiving... */

    return 0;
}
/* queue_client_setup.c — Client opens the SAME queue */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>

#define PROJ_ID  'A'
#define KEY_FILE "/tmp/myapp"

int main(void)
{
    /* Same key generation as server */
    key_t key = ftok(KEY_FILE, PROJ_ID);
    if (key == -1) { perror("ftok"); exit(1); }

    /* Open existing queue — NO IPC_CREAT, so fails if not created yet */
    int msqid = msgget(key, 0660);
    if (msqid == -1) {
        if (errno == ENOENT)
            fprintf(stderr, "Queue not found! Start server first.\n");
        else
            perror("msgget");
        exit(1);
    }
    printf("Client opened queue ID = %d\n", msqid);

    /* Now use msqid ... */
    return 0;
}

Code Example 3 — Checking If a Queue Exists

/* check_queue.c — Check if a queue exists without creating it */
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int queue_exists(key_t key)
{
    /* msgget with no IPC_CREAT: succeeds if queue exists, fails with ENOENT if not */
    int id = msgget(key, 0);
    if (id == -1) {
        if (errno == ENOENT)
            return 0;  /* Does not exist */
        perror("msgget");
        return -1;     /* Some other error */
    }
    return 1;  /* Exists */
}

int main(void)
{
    key_t key = ftok("/tmp/myapp", 'A');
    int result = queue_exists(key);
    if (result == 1)
        printf("Queue exists!\n");
    else if (result == 0)
        printf("Queue does not exist.\n");
    else
        printf("Error checking queue.\n");
    return 0;
}

Viewing Active Queues — ipcs & ipcrm

The ipcs command lists all active System V IPC objects (queues, semaphores, shared memory). Use it to check for leftover queues.

$ ipcs -q           # Show all message queues

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x00000000 65536      ravi       666        64           1
0x41020a52 65537      ravi       660        0            0

$ ipcrm -q 65536    # Remove queue with ID 65536
$ ipcrm -Q 0x41020a52  # Remove queue with this key
Always clean up! If your program crashes before calling msgctl(IPC_RMID), the queue remains. Running ipcs -q after testing is good practice. On a server, leaked queues accumulate until the kernel limit is hit.

Interview Questions — msgget() & IPC Keys

Q1. What does msgget() return? Is it a file descriptor? It returns an integer called the message queue identifier (msqid). It is NOT a file descriptor. It cannot be used with read(), write(), select(), or any FD-based calls.
Q2. What is the difference between IPC_CREAT and IPC_EXCL? IPC_CREAT creates the queue if it doesn’t exist, or opens it if it does. IPC_EXCL (used together with IPC_CREAT) causes msgget to fail with EEXIST if the queue already exists — guaranteeing you get a freshly created queue.
Q3. What is ftok() and why might it fail? ftok() generates a key_t from a filename and project ID using the file’s inode and device number. It fails if the file doesn’t exist (ENOENT). It can also produce collisions if different paths accidentally share the same inode+device combination.
Q4. When would you use IPC_PRIVATE? When the communicating processes are related (parent-child via fork). The parent creates the queue with IPC_PRIVATE, then passes the msqid to the child as part of inheritance across fork. IPC_PRIVATE always creates a new unique queue.
Q5. What errno is set when msgget() is called without IPC_CREAT but the queue doesn’t exist? ENOENT.
Q6. How do you view all active message queues on a Linux system? Using the ipcs -q command. To remove a queue from the shell: ipcrm -q msqid or ipcrm -Q key.
Q7. What is the purpose of the permission bits in msgflg? They work like Unix file permissions (octal, e.g. 0666). They control which users and groups can send messages to (write) or receive from (read) the queue. Checked by the kernel on each msgsnd/msgrcv call.

Leave a Reply

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