Skip to content
Marek Bykowski edited this page May 15, 2026 · 3 revisions

Linux IPC Cheat Sheet


Pipe

Flow

pipe() → fork() → write(fd[1]) / read(fd[0]) → close()

Key Syscalls

Syscall Description
pipe(int fd[2]) Creates kernel buffer; fd[0] = read end, fd[1] = write end
read(fd[0], buf, n) Copies from kernel buffer into buf; blocks if empty; 0 on EOF
write(fd[1], buf, n) Copies buf into kernel buffer; blocks if full
close(fd[n]) Always close the unused end — read end sees EOF only when all write ends are closed
dup2(src, dst) Makes dst point at whatever src points at; used to redirect stdin/stdout to the pipe

Notes

  • Lives entirely in the kernel — no filesystem entry
  • After fork() both processes inherit both ends; manually close the end you do not use
  • dup2(fd[1], STDOUT_FILENO) + exec() is how shells implement cmd1 | cmd2
  • Parent forks twice (one per command), closes both ends, then wait()s — otherwise the reader never sees EOF
int fd[2];
pipe(fd);
if (fork() == 0) {          /* child: reader */
    close(fd[1]);
    read(fd[0], buf, sizeof(buf));
    close(fd[0]);
} else {                    /* parent: writer */
    close(fd[0]);
    write(fd[1], msg, strlen(msg) + 1);
    close(fd[1]);
}

dup2 example — simulates ls | wc -l

fd table before dup2:        fd table after dup2(fd[1], STDOUT_FILENO):

fd 0 → terminal (stdin)      fd 0 → terminal (stdin)
fd 1 → terminal (stdout)     fd 1 → pipe write end   ← redirected
fd 2 → terminal (stderr)     fd 2 → terminal (stderr)
fd 3 → pipe read end         fd 3 → pipe read end
fd 4 → pipe write end        fd 4 → closed
int fd[2];
pipe(fd);

/* child 1: ls — stdout redirected to pipe write end */
if (fork() == 0) {
    dup2(fd[1], STDOUT_FILENO);   /* fd 1 now points at pipe write end */
    close(fd[0]);
    close(fd[1]);                 /* fd 4 no longer needed — fd 1 covers it */
    execlp("ls", "ls", NULL);
}

/* child 2: wc -l — stdin redirected to pipe read end */
if (fork() == 0) {
    dup2(fd[0], STDIN_FILENO);    /* fd 0 now points at pipe read end */
    close(fd[0]);
    close(fd[1]);
    execlp("wc", "wc", "-l", NULL);
}

/* parent closes both ends — otherwise wc -l never sees EOF */
close(fd[0]);
close(fd[1]);
wait(NULL);
wait(NULL);

FIFO (Named Pipe)

Flow

mkfifo() → open() → write() / read() → close() → unlink()

Key Syscalls

Syscall Description
mkfifo(path, mode) Creates a named pipe as a file in the filesystem (e.g. /tmp/myfifo)
umask(0) Call before mkfifo; subtracts permissions from mode (umask(0022) turns 06660644)
open(path, O_WRONLY) Blocks until the other end is also opened
open(path, O_RDONLY) Blocks until the other end is also opened
unlink(path) Removes the FIFO file from the filesystem

Notes

  • Same read/write semantics as a pipe but any two unrelated processes can use it by name
  • open() blocks until both ends are open — built-in synchronization
  • Kernel buffer is the same as a pipe; data is consumed on read
  • Writer is responsible for unlink() cleanup
mkfifo("/tmp/myfifo", 0644);

/* writer process */
int fd = open("/tmp/myfifo", O_WRONLY);
write(fd, msg, strlen(msg) + 1);
close(fd);

/* reader process */
int fd = open("/tmp/myfifo", O_RDONLY);
read(fd, buf, sizeof(buf));
close(fd);

unlink("/tmp/myfifo");

Internet Socket (TCP)

Flow

Server: socket() → bind() → listen() → accept() → read/write() → close()
Client: socket() → connect() → write/read() → close()

Key Syscalls

Syscall Description
socket(AF_INET, SOCK_STREAM, 0) Creates a TCP socket; returns a file descriptor
bind(fd, addr, len) Assigns IP + port to the socket; INADDR_ANY = all interfaces
htons(port) Converts port to network byte order (big-endian)
listen(fd, backlog) Marks socket passive; backlog = max queued pending connections; does not block
accept(fd, NULL, NULL) Blocks until a client connects; returns a new fd for that connection
connect(fd, addr, len) Client initiates connection; blocks until established
inet_addr("127.0.0.1") Converts IP string to binary network address

Notes

  • sockfd keeps listening; connfd returned by accept() is for the individual client
  • Loop accept() to handle multiple clients sequentially
  • read()/write() on connfd work the same as on a pipe fd
/* server */
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
listen(sockfd, 5);
int connfd = accept(sockfd, NULL, NULL);
read(connfd, buf, sizeof(buf));
write(connfd, reply, sizeof(reply));
close(connfd);
close(sockfd);

/* client */
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
connect(sockfd, (struct sockaddr *)&addr, sizeof(addr));
write(sockfd, msg, sizeof(msg));
read(sockfd, buf, sizeof(buf));
close(sockfd);

Shared Memory (POSIX)

Flow

shm_open() → ftruncate() → mmap() → read/write ptr → munmap() → shm_unlink()

Key Syscalls

Syscall Description
shm_open(name, O_CREAT|O_RDWR, mode) Creates/opens a shared memory object by name; visible under /dev/shm
ftruncate(fd, size) Sets the size of the object; must call before mmap — object starts at 0 bytes
mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0) Maps the object into this process's virtual address space; returns a pointer
munmap(ptr, size) Unmaps from this process's address space
shm_unlink(name) Removes the object from the system; one process is responsible for this

Notes

  • Fastest IPC — no kernel buffer; both processes map the same physical memory page
  • Two processes find the same object by name (same principle as opening a file by name)
  • No built-in synchronization — concurrent writes to the same region require a semaphore
  • Link with -lrt
/* writer */
int fd = shm_open("/myshm", O_CREAT | O_RDWR, 0644);
ftruncate(fd, sizeof(struct shared));
struct shared *p = mmap(NULL, sizeof(struct shared),
                        PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
memcpy(p->data, msg, strlen(msg) + 1);
munmap(p, sizeof(struct shared));
shm_unlink("/myshm");

/* reader */
int fd = shm_open("/myshm", O_RDONLY, 0);
struct shared *p = mmap(NULL, sizeof(struct shared),
                        PROT_READ, MAP_SHARED, fd, 0);
close(fd);
printf("%s\n", p->data);
munmap(p, sizeof(struct shared));

Semaphore (POSIX Named)

Flow

sem_open() → sem_wait() → [critical section] → sem_post() → sem_close() → sem_unlink()

Key Syscalls

Syscall Description
sem_open(name, O_CREAT, mode, value) Creates/opens a named semaphore; value=1 = binary semaphore (mutex)
sem_wait(sem) Decrements semaphore; blocks if 0 — this is the lock
sem_post(sem) Increments semaphore; unblocks a waiting process — this is the unlock
sem_close(sem) Closes handle in this process; does not remove the semaphore
sem_unlink(name) Removes the semaphore from the system

Notes

  • Binary semaphore (value=1) acts as a mutex — only one process in the critical section at a time
  • Classic race without semaphore: both processes read counter=5, both write counter=6 — one increment lost
  • Link with -lrt -lpthread
sem_t *sem = sem_open("/mysem", O_CREAT, 0644, 1);

sem_wait(sem);      /* lock */
(*counter)++;       /* critical section */
sem_post(sem);      /* unlock */

sem_close(sem);
sem_unlink("/mysem");

Signals

Flow

Receiver: sigaction() → pause()/loop → handler runs on signal arrival
Sender:   kill(pid, sig)

Key Syscalls

Syscall Description
sigaction(signum, &sa, NULL) Registers a handler for a signal; preferred over signal()
sigemptyset(&sa.sa_mask) No signals blocked while handler runs
pause() Suspends process until any signal arrives; returns after handler runs
kill(pid, sig) Sends any signal to a process by PID; name is misleading — not just for termination

Common Signals

Signal Value Description
SIGINT 2 Ctrl+C — interrupt from terminal
SIGTERM 15 Polite shutdown request
SIGKILL 9 Forceful kill — cannot be caught or ignored
SIGUSR1 10 User-defined — free to use for any purpose
SIGUSR2 12 User-defined — free to use for any purpose

Notes

  • Signals carry no data — notification only
  • SIGKILL and SIGSTOP cannot be caught, blocked, or ignored
  • Avoid printf() inside signal handlers — not async-signal-safe; use write() instead
/* receiver */
static void handler(int sig) { write(1, "got signal\n", 11); }

struct sigaction sa;
sa.sa_handler = handler;
sa.sa_flags   = 0;
sigemptyset(&sa.sa_mask);
sigaction(SIGUSR1, &sa, NULL);
while (1) pause();

/* sender */
kill(target_pid, SIGUSR1);

IPC Comparison

Mechanism Direction Filesystem Speed Sync needed Scope
Pipe One-way No Fast No Parent/child only
FIFO One-way Yes Fast No Any processes
Socket (TCP) Two-way No Moderate No Network or local
Shared Memory Two-way No Fastest Yes Any processes
Semaphore No data No Any processes
Signal No data No Fast No Any processes

Clone this wiki locally