What You Will Learn
This file covers the fundamentals of POSIX IPC — what the three mechanisms are, how to open/create IPC objects, how permissions are set and checked, and how to properly close IPC objects when done.
POSIX IPC consists of three separate mechanisms. Each solves a different communication problem between processes:
| Mechanism | Open Function | Purpose | Close / Remove |
|---|---|---|---|
| Message Queue | mq_open() | Exchange messages between processes | mq_close() / mq_unlink() |
| Semaphore | sem_open() | Synchronize access to shared resources | sem_close() / sem_unlink() |
| Shared Memory | shm_open() | Share a memory region between processes | munmap() / shm_unlink() |
All three follow the same file-like model: create with a name, open it, use it, close it, and unlink (delete) it when done.
Every POSIX IPC mechanism has a corresponding open call. The general signature pattern looks like this:
/* General pattern for POSIX IPC open calls */
/* Message Queue */
mqd_t mq_open(const char *name, int oflag, ...);
/* Named Semaphore */
sem_t *sem_open(const char *name, int oflag, ...);
/* Shared Memory */
int shm_open(const char *name, int oflag, mode_t mode);
2.1 The name Parameter
IPC objects are identified by a name — a string that looks like a file path. On Linux:
- The name must start with a forward slash (
/) - Only one slash is used (no subdirectories)
- Example:
/my_queue,/app_sem,/shared_buf
/* Correct IPC object names on Linux */
const char *mq_name = "/my_message_queue";
const char *sem_name = "/app_semaphore";
const char *shm_name = "/shared_buffer";
/* WRONG — subdirectories not allowed */
const char *bad_name = "/app/my_queue"; /* ERROR on Linux */
2.2 The oflag Parameter
The oflag controls how the object is opened. It works just like the flags in open() for files:
| Flag | Meaning |
|---|---|
| O_RDONLY | Open for reading only |
| O_WRONLY | Open for writing only |
| O_RDWR | Open for both reading and writing |
| O_CREAT | Create the object if it does not exist |
| O_EXCL | Used with O_CREAT — fail if object already exists |
| O_NONBLOCK | Non-blocking open (message queues) |
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
mqd_t mqd;
/*
* O_CREAT | O_RDWR:
* Create the queue if it doesn't exist.
* Open it for both reading and writing.
* 0644: rw-r--r-- permissions on the queue object.
*/
mqd = mq_open("/demo_queue", O_CREAT | O_RDWR, 0644, NULL);
if (mqd == (mqd_t)-1) {
perror("mq_open");
exit(EXIT_FAILURE);
}
printf("Message queue opened successfully. Descriptor: %d\n", (int)mqd);
mq_close(mqd);
mq_unlink("/demo_queue");
return 0;
}
Compile and run:
gcc -o demo_mq demo_mq.c -lrt
./demo_mq
2.3 O_CREAT + O_EXCL — Exclusive Creation
If you use O_CREAT | O_EXCL together, the call fails with EEXIST if the object already exists. This is useful when only one process should create the object.
#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
int main(void)
{
sem_t *sem;
/* Try to create exclusively — will fail if already exists */
sem = sem_open("/exclusive_sem", O_CREAT | O_EXCL, 0600, 1);
if (sem == SEM_FAILED) {
if (errno == EEXIST) {
printf("Semaphore already exists — someone else created it.\n");
/* Open without O_CREAT to use the existing one */
sem = sem_open("/exclusive_sem", 0);
if (sem == SEM_FAILED) {
perror("sem_open");
exit(EXIT_FAILURE);
}
} else {
perror("sem_open");
exit(EXIT_FAILURE);
}
} else {
printf("Semaphore created successfully by this process.\n");
}
sem_close(sem);
return 0;
}
When you create a POSIX IPC object (using O_CREAT), you must specify a mode — the permission bits for the new object. These work exactly like file permissions.
3.1 Permission Bits
| Octal | Symbolic | Meaning |
|---|---|---|
| 0400 | S_IRUSR | Owner read |
| 0200 | S_IWUSR | Owner write |
| 0040 | S_IRGRP | Group read |
| 0020 | S_IWGRP | Group write |
| 0004 | S_IROTH | Others read |
| 0002 | S_IWOTH | Others write |
Important: Execute permission (0100, 0010, 0001) has no meaning for POSIX IPC objects. Setting it is allowed but ignored.
3.2 How umask Affects Permissions
The mode you specify is not used directly. The kernel applies the process’s umask to it:
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int fd;
/*
* Request permissions: 0660 (owner rw, group rw, others none)
* If umask is 0022, the 0020 (group write) bit will be removed.
* Actual result: 0640 (owner rw, group r, others none)
*/
fd = shm_open("/perm_demo", O_CREAT | O_RDWR, 0660);
if (fd == -1) {
perror("shm_open");
exit(EXIT_FAILURE);
}
printf("Shared memory object created.\n");
printf("Requested mode: 0660, actual depends on current umask.\n");
/* Tip: check with ls -l /dev/shm/perm_demo after running */
close(fd);
shm_unlink("/perm_demo");
return 0;
}
3.3 Ownership of a New IPC Object
When a new POSIX IPC object is created, its ownership is set to the effective user ID and effective group ID of the creating process. On Linux specifically, it uses the process’s filesystem IDs (which normally equal the effective IDs).
3.4 ACL Support (Linux 2.6.19+)
Since Linux kernel 2.6.19, Access Control Lists (ACLs) can be used to set fine-grained permissions on:
- POSIX shared memory objects
- Named semaphores
ACLs are not currently supported for POSIX message queues.
When a process finishes using a POSIX IPC object, it must close it. This tells the kernel to release any per-process resources associated with the object.
| IPC Type | Close Call | Notes |
|---|---|---|
| Message Queue | mq_close(mqd) | Closes the message queue descriptor |
| Semaphore | sem_close(sem) | Releases the semaphore from this process |
| Shared Memory | munmap(addr, len) | Unmaps the region — no separate close call |
Key point: Closing an IPC object does not delete it. The object continues to exist until it is explicitly unlinked. Other processes can still open it after you close it.
IPC objects are automatically closed when a process:
- Terminates (normal exit or signal)
- Calls
exec()to execute a new program
#include <mqueue.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
/* --- Message Queue --- */
mqd_t mqd = mq_open("/close_demo_mq", O_CREAT | O_RDWR, 0644, NULL);
if (mqd == (mqd_t)-1) { perror("mq_open"); exit(1); }
printf("MQ opened.\n");
if (mq_close(mqd) == -1) { perror("mq_close"); }
else printf("MQ closed. Object still exists in kernel.\n");
/* mq_unlink removes the name. The object itself is gone when */
/* all processes that had it open have closed it. */
mq_unlink("/close_demo_mq");
printf("MQ unlinked.\n\n");
/* --- Named Semaphore --- */
sem_t *sem = sem_open("/close_demo_sem", O_CREAT, 0600, 1);
if (sem == SEM_FAILED) { perror("sem_open"); exit(1); }
printf("Semaphore opened.\n");
if (sem_close(sem) == -1) { perror("sem_close"); }
else printf("Semaphore closed.\n");
sem_unlink("/close_demo_sem");
printf("Semaphore unlinked.\n\n");
/* --- Shared Memory --- */
int fd = shm_open("/close_demo_shm", O_CREAT | O_RDWR, 0644);
if (fd == -1) { perror("shm_open"); exit(1); }
ftruncate(fd, 4096);
void *addr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) { perror("mmap"); exit(1); }
close(fd); /* The fd can be closed after mmap — mapping stays */
printf("Shared memory mapped.\n");
strcpy((char *)addr, "Hello from shared memory!");
printf("Wrote: %s\n", (char *)addr);
/* munmap is the "close" for shared memory */
if (munmap(addr, 4096) == -1) { perror("munmap"); }
else printf("Shared memory unmapped.\n");
shm_unlink("/close_demo_shm");
printf("Shared memory unlinked.\n");
return 0;
}
Compile:
gcc -o close_demo close_demo.c -lrt -lpthread
./close_demo
On Linux, POSIX IPC functions live in the realtime library (librt). You must link against it with -lrt. For semaphores, you also need -lpthread.
# Message queues
gcc -o mq_prog mq_prog.c -lrt
# Semaphores
gcc -o sem_prog sem_prog.c -lrt -lpthread
# Shared memory
gcc -o shm_prog shm_prog.c -lrt
# All three (safe default)
gcc -o ipc_prog ipc_prog.c -lrt -lpthread
Without -lrt, you will get a linker error like:
undefined reference to `mq_open'
undefined reference to `shm_open'
POSIX Message Queues, POSIX Named Semaphores, and POSIX Shared Memory. All three use a file-like model with open/close/unlink calls.
It must start with a single forward slash and contain no further slashes. For example, /my_queue is valid but /app/queue is not.
It makes the open call fail with EEXIST if the object already exists. This ensures only one process creates the object and avoids accidental reuse of a leftover object.
The kernel applies the process’s umask to the requested mode: effective = mode & ~umask. So if you request 0666 and the umask is 0022, the actual permissions will be 0644.
No. Execute permission has no meaning for POSIX IPC objects. You can set the bits, but the kernel ignores them.
There is no shm_close(). Shared memory is closed by unmapping the region with munmap(addr, length). The underlying file descriptor returned by shm_open() can be closed with the regular close() after mmap() is called.
-lrt links the realtime library. For semaphores, you also need -lpthread. Without -lrt the linker will report undefined references to functions like mq_open and shm_open.
They are automatically closed, just as open file descriptors are closed on exec. The IPC objects themselves are not deleted — only the process’s references to them are released.
Linux 2.6.19. ACLs are supported for POSIX shared memory and named semaphores, but not for POSIX message queues.
