Master Linux Inter‑Process Communication: Pipes, Signals, Shared Memory & More
This guide explains Linux inter‑process communication (IPC) mechanisms—including anonymous and named pipes, signals, file‑based communication, semaphores, various shared‑memory techniques, message queues, TCP/UDP sockets, and Unix domain sockets—providing concepts, typical use cases, and complete C code examples for each method.
Introduction
Linux runs many processes simultaneously. Inter‑Process Communication (IPC) lets processes exchange data. The kernel provides several IPC mechanisms, each suited to different relationships, data volumes, latency, and persistence requirements.
1. Pipes
Anonymous pipe
Concept : A one‑way byte stream that exists only in memory and can be used between related processes (e.g., parent and child). Full‑duplex communication requires two pipes.
#include <unistd.h>
int main() {
int pipefd[2];
pipe(pipefd); // create pipe
if (fork() == 0) { // child
close(pipefd[1]); // close write end
char buf[5];
read(pipefd[0], buf, 5);
} else { // parent
close(pipefd[0]); // close read end
write(pipefd[1], "hello", 5);
}
return 0;
}Named pipe (FIFO)
Concept : A special file in the filesystem that unrelated processes can open for reading or writing.
// server.c
int main() {
const char *fifoPath = "/tmp/my_fifo";
mkfifo(fifoPath, 0666);
char buf[1024];
int fd;
while (1) {
fd = open(fifoPath, O_RDONLY);
read(fd, buf, sizeof(buf));
printf("Received: %s
", buf);
close(fd);
}
return 0;
}
// client.c
int main() {
const char *fifoPath = "/tmp/my_fifo";
char buf[1024];
printf("Enter message: ");
fgets(buf, sizeof(buf), stdin);
int fd = open(fifoPath, O_WRONLY);
write(fd, buf, strlen(buf) + 1);
close(fd);
return 0;
}2. Signals
Concept : Asynchronous notifications sent by the kernel or another process (e.g., SIGINT, SIGTERM). They are useful for handling exceptions, external interrupts, timers, and child‑process status changes.
List signals with kill -l:
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
...Simple handler example :
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void signal_handler(int sig) {
printf("Received signal: %d
", sig);
}
int main() {
signal(SIGINT, signal_handler);
while (1) sleep(1);
return 0;
}3. Files as IPC
Processes can read/write a common file to exchange data. When multiple writers are involved, file locks (fcntl or flock) prevent race conditions.
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
// writer
int main() {
const char *file = "/tmp/ipc_file";
int fd = open(file, O_RDWR | O_CREAT, 0666);
write(fd, "Hello from Process A", 20);
close(fd);
return 0;
}
// reader
int main() {
const char *file = "/tmp/ipc_file";
int fd = open(file, O_RDWR);
char buf[50];
read(fd, buf, 20);
close(fd);
return 0;
}Note : Use fcntl(fd, F_SETLKW, &fl) or flock(fd, LOCK_EX) to serialize concurrent writes.
4. Semaphores
Concept : A counter used to synchronize access to shared resources. POSIX semaphores are preferred; they exist either anonymously (memory‑only) or with a name in the filesystem.
Named‑semaphore example (mutual exclusion for a log file) :
#include <semaphore.h>
#include <fcntl.h>
#include <stdio.h>
int main() {
FILE *log = fopen("logfile.txt", "a");
sem_t *sem = sem_open("/log_semaphore", O_CREAT, 0644, 1);
sem_wait(sem);
fprintf(log, "Log from process %d
", getpid());
fflush(log);
sem_post(sem);
sem_close(sem);
fclose(log);
return 0;
}5. Shared Memory
Shared memory offers the fastest IPC by mapping the same physical memory into multiple processes.
5.1 Anonymous shared memory (mmap)
#include <sys/mman.h>
size_t size = 4096;
void *addr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
/* use *addr */
munmap(addr, size);5.2 File‑backed shared memory
#include <sys/mman.h>
#include <fcntl.h>
size_t size = 4096;
int fd = open("shared_file", O_RDWR|O_CREAT, 0666);
ftruncate(fd, size);
void *addr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
/* use *addr */
munmap(addr, size);
close(fd);5.3 POSIX shared memory
#include <sys/mman.h>
int fd = shm_open("/example_shm", O_CREAT|O_RDWR, 0666);
ftruncate(fd, 4096);
void *addr = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
/* ... */
munmap(addr, 4096);
shm_unlink("/example_shm");5.4 System V shared memory
#include <sys/shm.h>
key_t key = ftok("sharedfile", 'A');
int shmid = shmget(key, 1024, IPC_CREAT|0666);
char *addr = shmat(shmid, NULL, 0);
/* ... */
shmdt(addr);
shmctl(shmid, IPC_RMID, NULL);Example: two unrelated processes synchronizing access to a file‑backed segment with a named semaphore :
// writer.c
int main() {
const char *file = "shared_file";
size_t size = 4096;
int fd = open(file, O_RDWR|O_CREAT, 0666);
void *addr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
sem_t *sem = sem_open("/mysemaphore", O_CREAT, 0666, 1);
sem_wait(sem);
strcpy((char*)addr, "Hello from writer");
sem_post(sem);
munmap(addr, size);
close(fd);
sem_close(sem);
return 0;
}
// reader.c
int main() {
const char *file = "shared_file";
size_t size = 4096;
int fd = open(file, O_RDONLY);
void *addr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
sem_t *sem = sem_open("/mysemaphore", 0);
sem_wait(sem);
printf("Read: %s
", (char*)addr);
sem_post(sem);
munmap(addr, size);
close(fd);
sem_close(sem);
return 0;
}6. Message Queues (System V)
Processes send messages identified by a type; receivers can fetch messages of a specific type.
#include <sys/ipc.h>
#include <sys/msg.h>
struct msgbuf { long mtype; char mtext[100]; };
int main() {
key_t key = ftok("queuefile", 65);
int msqid = msgget(key, IPC_CREAT|0666);
struct msgbuf msg = {1, "Hello World"};
msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
// receiver
msgrcv(msqid, &msg, sizeof(msg.mtext), 1, 0);
printf("Received: %s
", msg.mtext);
msgctl(msqid, IPC_RMID, NULL);
return 0;
}7. Sockets (TCP/UDP)
Sockets enable communication on the same host or across a network. TCP provides reliable, connection‑oriented streams; UDP offers connectionless datagrams.
// TCP server (server.c)
int main() {
int srv = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr = { .sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY, .sin_port = htons(8080) };
bind(srv, (struct sockaddr*)&addr, sizeof(addr));
listen(srv, 3);
while (1) {
int client = accept(srv, NULL, NULL);
char buf[1024];
read(client, buf, sizeof(buf));
printf("Client: %s
", buf);
close(client);
}
close(srv);
return 0;
}
// TCP client (client.c)
int main() {
int sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in srv = { .sin_family = AF_INET, .sin_port = htons(8080) };
inet_pton(AF_INET, "127.0.0.1", &srv.sin_addr);
connect(sock, (struct sockaddr*)&srv, sizeof(srv));
const char *msg = "Hello from client";
send(sock, msg, strlen(msg), 0);
close(sock);
return 0;
}8. Unix Domain Sockets
Local‑only sockets that use a filesystem pathname instead of an IP address, offering higher performance for same‑host IPC.
// server (server.c)
int main() {
int srv = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, "/tmp/unix_socket");
bind(srv, (struct sockaddr*)&addr, sizeof(addr));
listen(srv, 5);
while (1) {
int client = accept(srv, NULL, NULL);
char buf[100];
read(client, buf, sizeof(buf));
printf("Received: %s
", buf);
close(client);
}
close(srv);
unlink("/tmp/unix_socket");
return 0;
}
// client (client.c)
int main() {
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, "/tmp/unix_socket");
connect(sock, (struct sockaddr*)&addr, sizeof(addr));
const char *msg = "Hello from client";
write(sock, msg, strlen(msg));
close(sock);
return 0;
}Note : Remove the socket file with unlink() after the server terminates.
Summary
Linux provides a rich set of IPC mechanisms—from simple pipes and files to powerful sockets and shared‑memory techniques. Selecting the appropriate method depends on process relationships, data size, latency, and persistence needs. Understanding each mechanism’s characteristics enables developers to build robust, efficient inter‑process communication for both small utilities and complex multi‑process applications.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Liangxu Linux
Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
