POSIX IPC vs System V IPC

 

POSIX IPC vs System V IPC
Complete Comparison — Interface · Portability · Naming · Reference Counting | Chapter 51 · File 3 of 3

← Chapter Index | ← File 1 | ← File 2 | File 3: Comparison

Overview

Linux supports two families of IPC mechanisms: the older System V IPC (inherited from UNIX System V) and the newer POSIX IPC (defined by POSIX.1b). Both provide message queues, semaphores, and shared memory — but they have very different interfaces, naming models, and portability characteristics. This file compares them side by side.

1. Quick Comparison at a Glance
Feature System V IPC POSIX IPC
Object Identification Integer keys (key_t) String names (/name)
Open / Create API msgget(), semget(), shmget() mq_open(), sem_open(), shm_open()
Interface Similarity to Files Low — different model High — open/close/unlink
Reference Counting No Yes
Deletion Simplicity Hard (must check usage) Easy (unlink when done)
SUSv3 Mandatory? Yes (mandatory) Optional component
Portability High — nearly all UNIX Lower — some gaps
CLI Management ipcs / ipcrm ls / rm (non-standard)
ACL Support (Linux) No Yes (shm + sem, kernel 2.6.19+)

2. Naming: Keys vs Names

One of the most visible differences between System V and POSIX IPC is how objects are identified.

2.1 System V IPC — Integer Keys

System V IPC uses integer keys of type key_t. You generate a key using ftok() from a pathname and a project ID, or use the special key IPC_PRIVATE.

#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    key_t key;
    int msqid;

    /*
     * System V approach: generate a key from a path and project ID.
     * Both communicating processes must use the SAME path + proj_id
     * to get the SAME key.
     */
    key = ftok("/tmp/my_app", 'A');
    if (key == -1) { perror("ftok"); exit(1); }

    printf("System V key: %d (0x%x)\n", (int)key, (unsigned int)key);

    /* Create or open the message queue using the key */
    msqid = msgget(key, IPC_CREAT | 0644);
    if (msqid == -1) { perror("msgget"); exit(1); }

    printf("System V message queue ID: %d\n", msqid);

    /* Delete — no reference counting, deleted immediately */
    msgctl(msqid, IPC_RMID, NULL);
    printf("Queue deleted.\n");
    return 0;
}

Problems with System V keys:

  • ftok() can produce the same key for different inputs (collision)
  • You must manage key uniqueness yourself across your application
  • The integer key is harder to read and debug than a string name

2.2 POSIX IPC — String Names

POSIX IPC uses human-readable string names starting with /. Two processes sharing /my_queue clearly know they are talking to the same object.

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

int main(void)
{
    /*
     * POSIX approach: use a clear string name.
     * Any process that knows the name can open it.
     * No key generation step needed.
     */
    mqd_t mqd = mq_open("/my_queue", O_CREAT | O_RDWR, 0644, NULL);
    if (mqd == (mqd_t)-1) { perror("mq_open"); exit(1); }

    printf("POSIX message queue '/my_queue' opened.\n");

    mq_close(mqd);
    mq_unlink("/my_queue");
    return 0;
}

3. Interface Design — Consistency with Files

POSIX IPC follows the same model as regular files: open → use → close → unlink. System V IPC uses a different model with get / ctl / op style calls that are inconsistent with the file API.

System V IPC Model
/* Create / Open */
key = ftok(path, id);
id = msgget(key, flags);
/* Use */
msgsnd(id, &msg, size, 0);
msgrcv(id, &msg, size, 0, 0);
/* Delete (immediate) */
msgctl(id, IPC_RMID, NULL);
/* No close() concept */
POSIX IPC Model (like files)
/* Open / Create */
mqd = mq_open(name, flags);
/* Use */
mq_send(mqd, msg, size, 0);
mq_receive(mqd, buf, size, 0);
/* Close (per-process) */
mq_close(mqd);
/* Unlink (remove name) */
mq_unlink(name);

The POSIX model is easier to understand because every Linux/UNIX programmer already knows the open/close/unlink file model. The System V API requires learning an entirely separate set of concepts.

4. Reference Counting — POSIX Wins for Deletion Safety

System V IPC has no reference counting. If you call IPC_RMID on a System V queue while another process is using it, the object is deleted immediately. That other process will get an error on its next operation.

POSIX IPC has reference counting. Calling mq_unlink() only removes the name. The object stays alive until all processes close it. This makes POSIX IPC much safer to use in multi-process applications.

/* DANGER: System V IPC — no reference counting */
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    key_t key = ftok("/tmp", 'X');
    int msqid = msgget(key, IPC_CREAT | 0644);

    pid_t pid = fork();
    if (pid == 0) {
        /* Child: sleep then try to use the queue */
        sleep(1);
        /* By now, parent has deleted the queue.
         * Any attempt to use msqid will fail with EINVAL. */
        struct { long mtype; char mtext[64]; } buf;
        ssize_t r = msgrcv(msqid, &buf, sizeof(buf.mtext), 0, 0);
        if (r == -1)
            perror("Child msgrcv — queue was deleted!");
        exit(0);
    }

    /* Parent: delete queue immediately (dangerous with child using it) */
    msgctl(msqid, IPC_RMID, NULL);
    printf("Parent: deleted queue (child still running!)\n");
    wait(NULL);
    return 0;
}
/* SAFE: POSIX IPC — reference counting protects the object */
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(void)
{
    mqd_t mqd = mq_open("/safe_demo", O_CREAT | O_RDWR, 0644, NULL);

    pid_t pid = fork();
    if (pid == 0) {
        /* Child: sleep then receive */
        sleep(1);
        mqd_t child_mqd = mq_open("/safe_demo", O_RDONLY);
        if (child_mqd == (mqd_t)-1) {
            /* Name is gone, but if we had it open before unlink, it works */
            printf("Child: name already unlinked (expected)\n");
        }
        /* Child uses its own descriptor safely */
        char buf[64];
        unsigned int prio;
        if (mq_receive(mqd, buf, sizeof(buf), &prio) != -1)
            printf("Child: received '%s'\n", buf);
        mq_close(mqd);
        exit(0);
    }

    /* Parent: send then unlink — child still has descriptor, it's safe */
    const char *msg = "safe message";
    mq_send(mqd, msg, strlen(msg)+1, 0);
    mq_unlink("/safe_demo"); /* Name removed, object lives on for child */
    printf("Parent: unlinked, but child descriptor still valid\n");
    mq_close(mqd);

    wait(NULL);
    return 0;
}
gcc -o safe_demo safe_demo.c -lrt
./safe_demo

5. Portability — System V Wins

Despite POSIX IPC being the more modern and cleaner interface, System V IPC is more portable in practice. Here is why:

5.1 SUSv3 Status

Mechanism System V SUSv3 Status POSIX SUSv3 Status
Message Queues Mandatory Optional
Semaphores Mandatory Optional
Shared Memory Mandatory Optional

5.2 Linux Kernel Support History

POSIX IPC was added to Linux in stages. Not all features were available in older kernels:

Linux Kernel Version Feature Added
2.4 POSIX Shared Memory (shm_open) added
2.6 Full POSIX Semaphore implementation available
2.6.6 POSIX Message Queues (mq_open) added
2.6.19 ACL support for POSIX shm and semaphores

System V IPC has been in Linux since the very early kernel versions and is available on virtually every UNIX-like system. If your code needs to run on many platforms — including old kernels and non-Linux UNIX systems — System V IPC is the safer choice.

5.3 Non-Standard Naming Details

Different UNIX implementations use different conventions for POSIX IPC object names and file-system paths. For example:

  • On Linux, message queues appear under /dev/mqueue/
  • On macOS, they are not accessible as files at all
  • Some systems require a double slash prefix (//name)

This makes writing portable POSIX IPC programs slightly more work than writing portable System V IPC programs.

/* Portable name helper — works around naming quirks */

#ifdef __linux__
  #define IPC_NAME(n)  "/" n          /* Linux: single slash */
#elif defined(__APPLE__)
  #define IPC_NAME(n)  "/" n          /* macOS: same on Darwin */
#else
  #define IPC_NAME(n)  "/" n          /* Adjust per platform */
#endif

/* Usage: */
mqd_t mqd = mq_open(IPC_NAME("my_queue"), O_CREAT | O_RDWR, 0644, NULL);

6. Which Should You Use? A Practical Decision Guide

Situation Recommended Reason
Linux-only application POSIX IPC Cleaner API, reference counting, easier debugging
Must run on multiple UNIX systems System V IPC Universally available, mandatory in SUSv3
Learning / new development POSIX IPC Simpler, consistent with file model, modern standard
Maintaining legacy code System V IPC Legacy code already uses it; no reason to rewrite
Old Linux kernel (< 2.6.6) System V IPC POSIX message queues not available before 2.6.6
/*
 * Side-by-side: same task (create a semaphore, lock, unlock, delete)
 * implemented in System V and POSIX styles.
 */

/* ========== SYSTEM V STYLE ========== */
#include <sys/sem.h>
#include <sys/ipc.h>

void sysv_demo(void)
{
    union semun { int val; struct semid_ds *buf; unsigned short *array; } arg;

    key_t key = ftok("/tmp", 'S');
    int semid = semget(key, 1, IPC_CREAT | 0600);

    arg.val = 1; /* binary semaphore — initial value 1 */
    semctl(semid, 0, SETVAL, arg);

    /* Lock (decrement) */
    struct sembuf op = { 0, -1, 0 };
    semop(semid, &op, 1);
    printf("SysV: locked\n");

    /* Unlock (increment) */
    op.sem_op = 1;
    semop(semid, &op, 1);
    printf("SysV: unlocked\n");

    /* Delete immediately — no reference counting */
    semctl(semid, 0, IPC_RMID);
    printf("SysV: deleted\n");
}

/* ========== POSIX STYLE ========== */
#include <semaphore.h>

void posix_demo(void)
{
    sem_t *sem = sem_open("/posix_compare", O_CREAT, 0600, 1);

    sem_wait(sem);   /* lock */
    printf("POSIX: locked\n");

    sem_post(sem);   /* unlock */
    printf("POSIX: unlocked\n");

    sem_close(sem);
    sem_unlink("/posix_compare"); /* object destroyed when refcount = 0 */
    printf("POSIX: unlinked\n");
}

int main(void)
{
    sysv_demo();
    printf("\n");
    posix_demo();
    return 0;
}
gcc -o compare_ipc compare_ipc.c -lrt -lpthread
./compare_ipc

7. Chapter 51 Summary

POSIX IPC is the modern alternative to System V IPC, providing three mechanisms: message queues, semaphores, and shared memory. Here are the key takeaways from Chapter 51:

  • POSIX IPC uses string names and a file-like open/close/unlink model that is easier to understand than System V IPC’s key/get/ctl model.
  • POSIX IPC objects are reference counted. Unlink removes the name; the object is destroyed only when all processes close it. This simplifies safe deletion.
  • POSIX IPC objects have kernel persistence — they survive process termination but not system reboot.
  • On Linux, POSIX IPC objects live in virtual filesystems (/dev/mqueue, /dev/shm) and can be managed with ls and rm.
  • System V IPC is more portable — it is mandatory in SUSv3 and present on all UNIX systems. POSIX IPC is optional in SUSv3 and was added to Linux in stages (shm in 2.4, semaphores in 2.6, queues in 2.6.6).
  • Compile POSIX IPC programs on Linux with -lrt (and -lpthread for semaphores).

🎯 Interview Questions — POSIX vs System V IPC
Q1: What are the three POSIX IPC mechanisms and their System V counterparts?

POSIX message queues ↔ System V message queues; POSIX named semaphores ↔ System V semaphores; POSIX shared memory ↔ System V shared memory.

Q2: How does POSIX IPC identify objects compared to System V IPC?

POSIX IPC uses string names starting with / (e.g., /my_queue). System V IPC uses integer keys of type key_t, typically generated with ftok().

Q3: Why is deletion safer with POSIX IPC than System V IPC?

POSIX IPC has reference counting. Calling mq_unlink() only removes the name; the object lives until all processes close it. System V IPC has no reference counting — IPC_RMID deletes the object immediately even if other processes are using it.

Q4: In which Linux kernel version was POSIX message queue support first added?

Linux 2.6.6. POSIX shared memory was added in 2.4 and full POSIX semaphores in 2.6.

Q5: Why is System V IPC sometimes preferred over POSIX IPC despite its older, more complex API?

Portability. System V IPC is mandatory in SUSv3 and available on virtually every UNIX system. POSIX IPC is optional in SUSv3 and not supported by some UNIX implementations.

Q6: What are the System V IPC management commands? Is there an equivalent for POSIX IPC?

System V has ipcs (list) and ipcrm (delete). POSIX IPC has no standard commands. On Linux you use ls /dev/mqueue/ and ls /dev/shm/ to list, and rm to delete.

Q7: What do you mean when you say POSIX IPC is “more consistent with the traditional UNIX file model”?

POSIX IPC uses the same open/close/unlink pattern that UNIX programmers already know from working with files. Objects are named with strings, opened with a function similar to open(), closed with a close function, and deleted with an unlink function. System V IPC uses an entirely different get/ctl/op naming convention.

Q8: A POSIX semaphore was created but the process crashed. How does this affect the next run of the application?

The semaphore still exists in the kernel (kernel persistence). The next run should use O_CREAT | O_EXCL to detect this: if sem_open() fails with EEXIST, call sem_unlink() to remove the leftover object before creating a fresh one. Register signal handlers so cleanup happens even on crashes.

Leave a Reply

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