IPC Mechanisms
Persistence
semget · shmget
What is System V IPC?
System V IPC stands for System V Inter-Process Communication. It is a set of three communication mechanisms that allow multiple processes running on the same Linux system to talk to each other, share data, and coordinate their work.
The name “System V” comes from where these mechanisms first appeared — they were developed for UNIX System V in the early 1980s. Before that, they were used in internal UNIX implementations for database and transaction-processing systems in telephone company record keeping. Around 1983, they became part of mainstream UNIX with System V.
Because SUSv3 (Single UNIX Specification v3) requires these mechanisms for XSI (X/Open System Interface) conformance, System V IPC is sometimes also called XSI IPC.
CONFIG_SYSVIPC. On most Linux systems it is enabled by default.System V IPC provides exactly three mechanisms. Think of them as three different tools for different jobs:
A message queue is like a post box maintained by the kernel. Processes can drop messages into it (write) and pick them out (read). Unlike a pipe, message queues preserve message boundaries — each read gets exactly one whole message, never a partial one or a mix of two.
Each message has:
- A data payload (arbitrary bytes)
- An integer type field — you can ask the kernel “give me only messages of type 5”
A semaphore is a kernel-maintained integer visible to all processes with the right permissions. Its purpose is synchronization — making sure multiple processes don’t step on each other’s toes when accessing a shared resource.
A process signals its peers by adjusting the semaphore value. The classic use case: “I’m using the printer, hands off” — increment the semaphore. “Done, someone else can use it” — decrement it.
Shared memory maps the same physical memory pages into the virtual address space of two or more processes. Once attached, any process can read or write that memory directly — no system call is needed for each access.
This makes shared memory the fastest IPC mechanism. The moment one process writes to shared memory, the change is immediately visible to all other processes sharing that segment.
Each IPC mechanism has its own set of system calls and header file. The table below shows the full API at a glance (from Table 45-1 in TLPI):
| Interface | Message Queues | Semaphores | Shared Memory |
|---|---|---|---|
| Header file | <sys/msg.h> |
<sys/sem.h> |
<sys/shm.h> |
| Data structure | msqid_ds |
semid_ds |
shmid_ds |
| Create / Open | msgget() |
semget() |
shmget() + shmat() |
| Close object | (none) | (none) | shmdt() |
| Control ops | msgctl() |
semctl() |
shmctl() |
| Perform IPC | msgsnd() / msgrcv() |
semop() |
Direct memory access |
ipc(2) handles all System V IPC operations. The individual functions like msgget() are actually C library wrappers over this one system call. (Alpha and IA-64 are exceptions where they are real individual syscalls.)Each IPC mechanism has a get call (msgget(), semget(), shmget()). These work like open() for files — given a key, they return an identifier.
- If no object with that key exists +
IPC_CREATset → creates new object, returns new identifier - If object with that key exists → opens it, returns its existing identifier
The identifier is different from a file descriptor. A file descriptor is private to a process, but an IPC identifier is a system-wide property of the object itself. All processes accessing the same object use the same identifier.
The simplest way to create a System V IPC object — here a message queue:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(void) {
key_t key = 12345; /* integer key — any agreed-upon number */
int id;
/* Create a new message queue (or open if it already exists)
Permissions: owner can read and write (S_IRUSR | S_IWUSR) */
id = msgget(key, IPC_CREAT | S_IRUSR | S_IWUSR);
if (id == -1) {
perror("msgget");
exit(EXIT_FAILURE);
}
printf("Message queue created. Identifier = %d\n", id);
/* The identifier is system-wide — any other process can use
the same key to get the same identifier */
return 0;
}
Message queue created. Identifier = 65536The identifier value depends on the kernel’s internal algorithm (explained in the algorithm tutorial).
These two flags control whether a get call creates a new object or opens an existing one:
IPC_CREAT— likeO_CREATforopen(): create if not existsIPC_EXCL— likeO_EXCLforopen(): fail if already exists
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(void) {
key_t key = 99999;
int id;
/* IPC_CREAT | IPC_EXCL: create, but FAIL if already exists */
id = msgget(key, IPC_CREAT | IPC_EXCL | 0600);
if (id == -1) {
if (errno == EEXIST) {
printf("Queue with this key already exists!\n");
} else {
perror("msgget");
}
exit(EXIT_FAILURE);
}
printf("New queue created exclusively. ID = %d\n", id);
/* Now delete the queue we just created */
if (msgctl(id, IPC_RMID, NULL) == -1) {
perror("msgctl IPC_RMID");
exit(EXIT_FAILURE);
}
printf("Queue deleted successfully.\n");
return 0;
}
ctl() call.#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#define SHM_SIZE 4096 /* 4 KB shared memory segment */
int main(void) {
key_t key = 55555;
int shmid;
/* Create a shared memory segment */
shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0600);
if (shmid == -1) {
perror("shmget");
exit(EXIT_FAILURE);
}
printf("Shared memory created. ID = %d\n", shmid);
/* Attach the shared memory to this process's address space */
void *addr = shmat(shmid, NULL, 0);
if (addr == (void *) -1) {
perror("shmat");
exit(EXIT_FAILURE);
}
printf("Shared memory attached at address: %p\n", addr);
/* Write something into shared memory */
char *msg = (char *)addr;
snprintf(msg, 64, "Hello from PID %d", getpid());
printf("Written: %s\n", msg);
/* Detach (does NOT delete the segment) */
if (shmdt(addr) == -1) {
perror("shmdt");
exit(EXIT_FAILURE);
}
printf("Shared memory detached.\n");
/* Delete the shared memory segment */
if (shmctl(shmid, IPC_RMID, NULL) == -1) {
perror("shmctl IPC_RMID");
exit(EXIT_FAILURE);
}
printf("Shared memory deleted.\n");
return 0;
}
IPC_RMID schedules deletion — the actual removal happens only when all processes have called shmdt(). For message queues and semaphores, deletion is immediate.System V IPC objects have kernel persistence. This means once created, an IPC object keeps living in the kernel until:
- It is explicitly deleted using the
IPC_RMIDoperation, OR - The system is shut down
- There are system-imposed limits on the number of IPC objects of each type
- If you forget to delete unused objects, you can hit these limits and cause application errors
- Message queues and semaphores are “connectionless” — the kernel doesn’t track which processes have them open, making it hard to know when it’s safe to delete them
