
进程间通信(Interprocess Communication,简称 IPC)是指多个进程之间相互交换数据、共享信息或同步操作的过程。由于每个进程都有自己独立的地址空间,不像线程共享同一地址空间,进程之间的通信需要特定的机制。单个进程内部的模块(如函数)之间的通信相对简单,可以通过全局变量、参数传递等方式实现,但进程间通信相对复杂,因为不同进程彼此隔离,不共享内存。
虽然大多数普通应用程序是单进程、多线程的,不需要进程间通信的复杂性,但在一些复杂或大型应用中,尤其是服务器或图形用户界面(GUI)程序,可能会使用多进程架构来提升性能或简化设计。这种情况下,IPC 机制就变得非常重要。
Linux 系统继承了 UNIX 系统的丰富 IPC 机制,并对其进行了扩展和改进。UNIX 系统的进程间通信大致可以分为两大流派:
Linux 将这两大体系都继承了下来,并在此基础上发展了 POSIX IPC。POSIX(Portable Operating System Interface for Unix)标准化了多种 UNIX 系统功能,包括进程间通信,弥补了 System V IPC 的一些不足。

UNIX 传统的 IPC 机制包括管道、FIFO 和信号,这些机制最早由 UNIX 系统引入,适用于简单的单机进程间通信。
SIGINT(Ctrl+C 发送的中断信号)。System V IPC 是 UNIX 的增强版本,主要包括信号量、消息队列和共享内存,适合需要更复杂的进程同步与数据共享的场景。
POSIX IPC 是 System V IPC 的改进版本,旨在解决 System V IPC 在灵活性和可移植性上的一些不足。POSIX 标准为 UNIX 系统间的兼容性提供了统一的接口,使得程序可以更方便地在不同的 UNIX 系统间移植。
套接字是一种既可以用于本地进程间通信,也可以用于网络通信的机制,支持双向数据传输。基于套接字的 IPC 可以实现非常灵活的通信模式,例如客户端-服务器架构 ,适合在多台计算机之间传递数据。
各类 IPC 机制的对比和应用场景:

管道是一种半双工(单向)的通信方式,通常用于父子进程之间的通信。一个进程可以向管道写入数据,另一个进程从管道读取数据。Linux 提供了无名管道和命名管道两种类型。
示例:
#include <stdio.h>
#include <unistd.h>
int main() {
int fd[2];
pipe(fd); // 创建无名管道
if (fork() == 0) { // 子进程
close(fd[0]); // 关闭读取端
write(fd[1], "Hello, parent!", 15);
close(fd[1]);
} else { // 父进程
char buffer[20];
close(fd[1]); // 关闭写入端
read(fd[0], buffer, sizeof(buffer));
printf("Received: %s\n", buffer);
close(fd[0]);
}
return 0;
}消息队列是一种先进先出的队列,允许进程以消息的形式发送和接收数据。消息队列可以支持多种类型的消息,通过消息类型实现多种目的的通信。
示例:进程A可以向队列发送一个带有特定类型的消息,而进程B可以根据消息类型进行处理。
#include <sys/msg.h>
#include <stdio.h>
struct msgbuf {
long mtype;
char mtext[100];
};
int main() {
key_t key = ftok("msgqueue", 65);
int msgid = msgget(key, 0666 | IPC_CREAT);
struct msgbuf message;
message.mtype = 1; // 消息类型
snprintf(message.mtext, sizeof(message.mtext), "Hello Message Queue");
msgsnd(msgid, &message, sizeof(message.mtext), 0);
return 0;
}共享内存是最快的 IPC 机制之一,因为进程之间直接访问同一块内存区域,而不需要拷贝数据。通常使用 shmget()、shmat() 和 shmdt() 函数进行共享内存的创建和访问。
示例:
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>
int main() {
key_t key = ftok("shmfile",65);
int shmid = shmget(key, 1024, 0666|IPC_CREAT);
char *str = (char*) shmat(shmid, (void*)0, 0);
strcpy(str, "Hello Shared Memory");
printf("Data written in memory: %s\n", str);
shmdt(str);
return 0;
}信号量是一种用于进程同步的机制,通常用于控制多个进程对共享资源的访问。 嵌入式系统中,信号量通常用来避免多个进程同时访问同一资源,防止数据竞争。
示例:信号量可以通过 semget() 和 semop() 函数来操作,用于锁定或解锁资源。
#include <sys/sem.h>
#include <stdio.h>
int main() {
key_t key = ftok("semfile",65);
int semid = semget(key, 1, 0666 | IPC_CREAT);
struct sembuf sem_lock = {0, -1, 0}; // 减1操作
struct sembuf sem_unlock = {0, 1, 0}; // 加1操作
semop(semid, &sem_lock, 1); // 上锁
printf("Critical section\n");
semop(semid, &sem_unlock, 1); // 解锁
return 0;
}套接字不仅支持本地进程间通信,还可以用于网络通信。基于套接字的 IPC 支持双向通信,比较灵活,适合嵌入式系统中进程之间需要频繁且复杂的数据交互的情况。
示例:
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
int main() {
int sv[2];
socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
if (fork() == 0) { // 子进程
close(sv[0]);
write(sv[1], "Hello from child", 16);
close(sv[1]);
} else { // 父进程
char buffer[20];
close(sv[1]);
read(sv[0], buffer, sizeof(buffer));
printf("Received: %s\n", buffer);
close(sv[0]);
}
return 0;
}信号是用来通知进程发生某种事件的机制。进程可以捕获、忽略或处理信号,典型的信号包括 SIGINT(中断信号)和 SIGKILL(杀死进程信号)。
示例:处理 SIGINT 信号(Ctrl+C)。
#include <signal.h>
#include <stdio.h>
void sigint_handler(int sig) {
printf("Caught signal %d\n", sig);
}
int main() {
signal(SIGINT, sigint_handler);
while (1) {
printf("Running...\n");
sleep(1);
}
return 0;
}进程间通信的机制多种多样,选择合适的方式取决于应用场景的需求。
原作者:不脱发的程序猿。原文发表于 2025-07-15,来源:https://blog.csdn.net/m0_38106923/article/details/142632282 。原文采用 CC BY-SA 4.0 协议。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。