Master Linux Inter‑Process Communication: Pipes, Signals, Shared Memory, and More
This article provides a comprehensive guide to Linux inter‑process communication (IPC), covering pipes, named pipes, signals, file‑based communication, semaphores, various shared‑memory techniques, message queues, sockets, and Unix domain sockets, each explained with concepts, typical use‑cases, diagrams and complete C code examples.
Introduction
In Linux, a process is an instance of a running program, and when multiple processes need to exchange data they must use an Inter‑Process Communication (IPC) mechanism. The article surveys the most common IPC methods, explains their underlying concepts, shows typical usage scenarios, and provides ready‑to‑run C examples.
1. Pipes (Anonymous and Named)
Concept
Pipes allow one‑way data flow between processes. Anonymous pipes work only between related processes (e.g., parent‑child), while named pipes (FIFOs) appear as files in the filesystem and can be used by unrelated processes.
Anonymous Pipe Example
#include <unistd.h>
int main() {
int pipefd[2];
pipe(pipefd); // create anonymous pipe
if (fork() == 0) { // child
close(pipefd[1]); // close write end
char buf[5];
read(pipefd[0], buf, 5);
// ... process data ...
} else { // parent
close(pipefd[0]); // close read end
write(pipefd[1], "hello", 5);
// ...
}
return 0;
}Named Pipe (FIFO) Example
// server.c
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
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
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
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
Signals are asynchronous notifications sent to a process to indicate events such as interrupts, exceptions, or termination requests. They are useful for error handling, external interruptions (e.g., Ctrl‑C), process control, timers, and child‑process status changes.
#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); // register handler for Ctrl‑C
while (1) {
sleep(1);
}
return 0;
}3. Files
Regular files can serve as a simple IPC medium: one process writes data, another reads it. This approach works for both related and unrelated processes but requires explicit synchronization (e.g., file locks) to avoid race conditions.
// writer.c
#include <fcntl.h>
#include <unistd.h>
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.c
#include <fcntl.h>
#include <unistd.h>
int main() {
const char *file = "/tmp/ipc_file";
int fd = open(file, O_RDONLY);
char buf[50];
read(fd, buf, 20);
// process buf ...
close(fd);
return 0;
}Note: When multiple writers access the same file concurrently, use fcntl or flock to acquire file locks and guarantee data integrity.
4. Semaphores
Semaphores provide counting‑based synchronization. POSIX named semaphores are created with sem_open and can be shared between unrelated processes.
// process1.c (writer)
#include <semaphore.h>
#include <stdio.h>
int main() {
sem_t *sem = sem_open("/log_semaphore", O_CREAT, 0644, 1);
FILE *log = fopen("logfile.txt", "a");
sem_wait(sem);
fprintf(log, "Log message from Process 1
");
fflush(log);
sem_post(sem);
sem_close(sem);
fclose(log);
return 0;
}5. Shared Memory
Shared memory offers the fastest IPC because processes access the same physical memory region. Four common variants are covered:
Anonymous shared memory – created with mmap using MAP_ANONYMOUS; suitable for related processes.
File‑backed shared memory – a regular file is mapped with mmap, providing persistence.
POSIX shared memory – uses shm_open and mmap for named objects.
System V shared memory – uses shmget, shmat, etc., for legacy systems.
Anonymous Shared Memory Example
#include <sys/mman.h>
int main() {
size_t size = 4096;
void *addr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
// use *addr as shared data
munmap(addr, size);
return 0;
}POSIX Shared Memory with Semaphore Example
#define SHM_NAME "/example_shm"
#define SHM_SIZE 4096
#define SEM_NAME "/example_sem"
#include <fcntl.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <stdio.h>
int main() {
int shm_fd = shm_open(SHM_NAME, O_CREAT|O_RDWR, 0666);
ftruncate(shm_fd, SHM_SIZE);
void *ptr = mmap(0, SHM_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, shm_fd, 0);
sem_t *sem = sem_open(SEM_NAME, O_CREAT, 0666, 1);
sem_wait(sem);
sprintf(ptr, "Hello from writer process!");
printf("Writer wrote: %s
", (char*)ptr);
sem_post(sem);
munmap(ptr, SHM_SIZE);
close(shm_fd);
sem_close(sem);
return 0;
}6. Message Queues
Message queues allow asynchronous, typed messages to be sent between processes. The System V API uses msgget, msgsnd, and msgrcv.
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
struct message { long mtype; char mtext[100]; };
int main() {
key_t key = ftok("queuefile", 65);
int msgid = msgget(key, 0666|IPC_CREAT);
struct message msg;
msg.mtype = 1;
sprintf(msg.mtext, "Hello World");
msgsnd(msgid, &msg, sizeof(msg.mtext), 0);
printf("Sent: %s
", msg.mtext);
return 0;
}7. Sockets
Sockets support both local IPC and network communication. TCP provides reliable, connection‑oriented streams, while UDP offers connectionless datagrams.
// TCP server (server.c)
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
int main() {
int server_fd = 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(server_fd, (struct sockaddr*)&addr, sizeof(addr));
listen(server_fd, 3);
while (1) {
int client = accept(server_fd, NULL, NULL);
char buffer[1024] = {0};
read(client, buffer, 1024);
printf("Message from client: %s
", buffer);
close(client);
}
close(server_fd);
return 0;
}8. Unix Domain Sockets
Unix domain sockets are local‑only sockets that bypass the network stack, offering higher performance for same‑host IPC.
// server (unix_server.c)
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdio.h>
int main() {
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un addr = { .sun_family = AF_UNIX };
strcpy(addr.sun_path, "/tmp/unix_socket");
bind(fd, (struct sockaddr*)&addr, sizeof(addr));
listen(fd, 5);
while (1) {
int client = accept(fd, NULL, NULL);
char buf[100];
read(client, buf, sizeof(buf));
printf("Received: %s
", buf);
close(client);
}
close(fd);
unlink("/tmp/unix_socket");
return 0;
}Conclusion
Linux provides a rich set of IPC mechanisms, each suited to different performance, complexity, and relationship requirements. By understanding the concepts, typical scenarios, and concrete code patterns presented above, developers can select the most appropriate method—whether a simple pipe for parent‑child communication or a full‑featured socket for cross‑machine messaging—and implement reliable data exchange between processes.
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.
