Linux C并发编程基础(进程间通信)
·
1.进程间通信的核心分类
首先明确 Linux 支持的主流 IPC 方式,按使用场景和特性分类:
- 管道(匿名管道 / 命名管道):简单易用、单向 / 双向、基于文件描述符,适合简单数据传递的场景;匿名管道适用于父子进程 / 有亲缘关系进程间的通信;命名管道适用于无亲缘关系进程间的通信。
- 消息队列(System V/Posix):按消息类型传递、异步通信、可持久化,适合无亲缘关系进程、需要分类传递数据的场景。
- 共享内存(System V/Posix):最高效(直接访问内存,无数据拷贝)、需同步机制配合,适合高并发、大数据量传递场景(如视频、大数据处理)。
- 信号量(System V/Posix):实现进程间同步、互斥,保护共享资源;配合共享内存、多个进程竞争同一资源的场景(如临界区保护)。
- 信号(Linux 标准信号 / 实时信号):异步通知、简单高效、携带信息少;适合进程异常处理、简单通知场景(如终止进程、定时器通知)。
- Socket(AF_UNIX/AF_INET):支持本地进程(AF_UNIX)、跨主机进程(AF_INET)、通用性强;适合本地无亲缘关系进程、跨服务器分布式进程通信。
2.管道(Pipe)—— 最简单的字节流通信
管道是 Linux 最基础的 IPC 机制,本质是「内核维护的一块缓存」,对外表现为一对文件描述符,进程通过读写文件描述符实现数据传递。分为匿名管道和命名管道两种。
2.1.匿名管道(Anonymous Pipe)
- 核心特性:
- 「匿名」:无实际文件路径,仅存在于内核中,进程退出后管道消失。
- 单向通信:数据只能从一端写入,另一端读出(读端、写端固定)。
- 亲缘关系限制:仅支持父子进程、兄弟进程等有亲缘关系的进程通信(由 fork() 继承文件描述符)。
- 基于字节流:无消息边界,读取数据时按字节流读取,需自行处理数据格式。
- 阻塞特性:读端无数据时阻塞,写端管道满时阻塞;写端关闭后,读端读取到 EOF(0);读端关闭后,写端会收到 SIGPIPE 信号终止。
- 函数原型:
// 成功返回 0,失败返回 -1,设置 errno
int pipe(int pipefd[2]);
- 参数:
- pipefd[2]:输出参数,返回两个文件描述符:
- pipefd[0]:管道读端,用于读取数据。
- pipefd[1]:管道写端,用于写入数据。
- pipefd[2]:输出参数,返回两个文件描述符:
- 注意:创建管道后,通常需要配合 fork() 创建子进程,子进程继承管道文件描述符,再关闭无用的端(如父进程关闭读端,子进程关闭写端,实现单向通信)。
- 实操示例:父子进程通过匿名管道通信
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main() {
int pipefd[2];
char buf[1024];
const char *msg = "Hello, 子进程!这是父进程通过匿名管道发送的消息\n";
// 第一步:创建匿名管道
if (pipe(pipefd) == -1) {
perror("pipe failed");
exit(EXIT_FAILURE);
}
// 第二步:创建子进程
pid_t pid = fork();
if (pid == -1) {
perror("fork failed");
exit(EXIT_FAILURE);
}
if (pid == 0) {
// 子进程:关闭写端,读取管道数据
close(pipefd[1]); // 子进程仅读取,关闭无用的写端
// 阻塞读取管道数据
ssize_t read_len = read(pipefd[0], buf, sizeof(buf) - 1);
if (read_len > 0) {
buf[read_len] = '\0'; // 字符串结束符
printf("【子进程】读取到管道数据:%s", buf);
} else if (read_len == 0) {
printf("【子进程】管道写端已关闭,无更多数据\n");
} else {
perror("read failed");
}
close(pipefd[0]); // 读取完成,关闭读端
exit(EXIT_SUCCESS);
} else {
// 父进程:关闭读端,写入管道数据
close(pipefd[0]); // 父进程仅写入,关闭无用的读端
// 向管道写入数据
ssize_t write_len = write(pipefd[1], msg, strlen(msg));
if (write_len == -1) {
perror("write failed");
} else {
printf("【父进程】成功向管道写入 %zd 字节数据\n", write_len);
}
close(pipefd[1]); // 写入完成,关闭写端(触发子进程 EOF)
// 等待子进程执行完毕
wait(NULL);
exit(EXIT_SUCCESS);
}
}
- 编译运行结果:
gcc anonymous_pipe_demo.c -o anonymous_pipe_demo
./anonymous_pipe_demo
# 输出:
【父进程】成功向管道写入 66 字节数据
【子进程】读取到管道数据:Hello, 子进程!这是父进程通过匿名管道发送的消息
2.2.命名管道(Named Pipe/FIFO)
- 核心特性:
- 「命名」:有实际的文件路径(如 /tmp/my_fifo),存在于文件系统中,进程退出后管道文件仍存在(需手动删除)。
- 双向 / 单向通信:默认单向,可通过打开方式配置为双向。
- 无亲缘关系限制:支持任意两个无亲缘关系的进程通信(只要能访问该管道文件)。
- 其他特性:与匿名管道一致(基于字节流、阻塞特性、依赖文件描述符)。
- 函数原型:
// 成功返回 0,失败返回 -1,设置 errno
int mkfifo(const char *pathname, mode_t mode);
- 参数:
- pathname:管道文件的路径(如 /tmp/my_fifo)。
- mode:管道文件的权限(如 0664,与 chmod 权限一致,受 umask 影响)。
- 注意:命名管道创建后,需要通过 open() 打开(读端用 O_RDONLY,写端用 O_WRONLY),之后通过 read()/write() 读写数据,与普通文件操作一致。
- 实操示例:两个无亲缘关系进程通过命名管道通信
第一步:写进程(fifo_writer.c)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FIFO_PATH "/tmp/my_fifo" // 命名管道路径
#define MODE 0664 // 管道文件权限
int main() {
const char *msg = "Hello, 读进程!这是写进程通过命名管道发送的消息\n";
// 第一步:创建命名管道(若已存在,忽略 EEXIST 错误)
if (mkfifo(FIFO_PATH, MODE) == -1) {
if (errno != EEXIST) { // 排除文件已存在的错误
perror("mkfifo failed");
exit(EXIT_FAILURE);
}
printf("【写进程】命名管道已存在,无需重复创建\n");
} else {
printf("【写进程】成功创建命名管道:%s\n", FIFO_PATH);
}
// 第二步:以只写模式打开命名管道
int fd = open(FIFO_PATH, O_WRONLY);
if (fd == -1) {
perror("open failed");
exit(EXIT_FAILURE);
}
// 第三步:向管道写入数据
ssize_t write_len = write(fd, msg, strlen(msg));
if (write_len == -1) {
perror("write failed");
} else {
printf("【写进程】成功向管道写入 %zd 字节数据\n", write_len);
}
// 第四步:关闭文件描述符,删除管道文件(可选)
close(fd);
unlink(FIFO_PATH); // 删除管道文件
printf("【写进程】操作完成,已删除命名管道\n");
exit(EXIT_SUCCESS);
}
第二步:读进程(fifo_reader.c)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FIFO_PATH "/tmp/my_fifo" // 与写进程一致的管道路径
int main() {
char buf[1024];
// 第一步:以只读模式打开命名管道(若不存在,会阻塞等待管道被创建)
printf("【读进程】等待命名管道创建...\n");
int fd = open(FIFO_PATH, O_RDONLY);
if (fd == -1) {
perror("open failed");
exit(EXIT_FAILURE);
}
printf("【读进程】成功打开命名管道:%s\n", FIFO_PATH);
// 第二步:阻塞读取管道数据
ssize_t read_len = read(fd, buf, sizeof(buf) - 1);
if (read_len > 0) {
buf[read_len] = '\0';
printf("【读进程】读取到管道数据:%s", buf);
} else if (read_len == 0) {
printf("【读进程】管道写端已关闭,无更多数据\n");
} else {
perror("read failed");
}
// 第三步:关闭文件描述符
close(fd);
printf("【读进程】操作完成\n");
exit(EXIT_SUCCESS);
}
第三步:编译运行
# 1. 编译两个进程
gcc fifo_writer.c -o fifo_writer
gcc fifo_reader.c -o fifo_reader
# 2. 先启动读进程(后台运行)
./fifo_reader &
# 3. 再启动写进程
./fifo_writer
# 输出:
【读进程】等待命名管道创建...
【写进程】成功创建命名管道:/tmp/my_fifo
【写进程】成功向管道写入 66 字节数据
【写进程】操作完成,已删除命名管道
【读进程】成功打开命名管道:/tmp/my_fifo
【读进程】读取到管道数据:Hello, 读进程!这是写进程通过命名管道发送的消息
【读进程】操作完成
3.消息队列(Message Queue)—— 按类型传递的异步通信
消息队列是 System V IPC 三大组件之一(另外两个是共享内存、信号量),本质是「内核维护的一个消息链表」,进程可以向队列中写入不同类型的消息,也可以从队列中读取指定类型的消息,实现异步通信。
- 核心特性:
- 异步通信:发送方写入消息后无需等待接收方读取,消息持久化在内核中,接收方按需读取。
- 按类型传递:每个消息都有一个类型标识,接收方可按类型筛选读取,无需按写入顺序读取。
- 无亲缘关系限制:支持任意进程通信(只要拥有相同的键值 key)。
- 局限性:消息大小和队列总长度有内核限制,进程退出后消息队列仍存在(需手动删除,避免资源泄漏)。
- 函数原型:
- 获取一个当前未用的IPC的key

- 获取消息队列ID

- 发送和接受消息

- 设置或者获取消息队列的相关属性

// 1. 创建/获取消息队列(成功返回消息队列 ID,失败返回 -1)
int msgget(key_t key, int msgflg);
// 2. 向消息队列发送消息(成功返回 0,失败返回 -1)
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
// 3. 从消息队列接收消息(成功返回读取的消息长度,失败返回 -1)
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
// 4. 控制消息队列(删除、获取状态,成功返回 0,失败返回 -1)
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
- 关键说明:
- key_t key:用于标识消息队列的唯一键值,通常通过 ftok() 生成。
- 消息结构需自定义,必须以 long msg_type 开头(消息类型),后续为消息数据。
- msgctl() 的 cmd 参数传入 IPC_RMID 可删除消息队列。
- 实操示例:两个进程通过消息队列传递消息
自定义消息结构:
struct msg_buf {
long msg_type; // 消息类型(必须 >= 1)
char msg_data[1024]; // 消息数据
};
发送进程:
- 用 ftok() 生成 key。
- 用 msgget() 创建 / 获取消息队列。
- 填充消息结构(设置类型和数据),用 msgsnd() 发送消息。
接收进程:
- 用相同的 key 获取消息队列。
- 用 msgrcv() 按指定类型读取消息。
最后用 msgctl() 删除消息队列。
- 示例:
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
#include <sys/ipc.h>
#include <sys/msg.h>
//定义消息结构体
typedef struct _msg{
long mtype; //接收者类型
char mtext[100];//消息内容,可以有多个成员
}MSG;
long msg_type=10;
int main()
{
//获取KEY值
key_t key=ftok("./",1);
//创建消息队列
int msg_id=msgget(key,IPC_CREAT | 0777);
pid_t son=fork();
if(son==0){
printf("子进程,ID为%d\n",getpid());
while(1){
MSG new_msg;
new_msg.mtype=msg_type;
scanf("%s",new_msg.mtext);
msgsnd(msg_id,&new_msg,sizeof(MSG)-sizeof(long),0);
if(strcmp(new_msg.mtext,"exit")==0){
break;
}
}
}
else if(son>0){
printf("父进程,ID为%d\n",getpid());
while(1){
MSG new_msg;
msgrcv(msg_id,&new_msg,sizeof(MSG)-sizeof(long),msg_type,0);
printf("from son:%s\n",new_msg.mtext);
if(strcmp(new_msg.mtext,"exit")==0){
break;
}
}
wait(NULL);
}
else if(son<0){
perror("fork failed\n");
}
return 0;
}
4.共享内存(Shared Memory)—— 最高效的大数据通信
共享内存是所有 IPC 方式中效率最高的一种,本质是「内核分配一块物理内存,映射到多个进程的虚拟地址空间」,进程直接访问该内存区域,无需数据拷贝(其他 IPC 方式均需内核中转,存在数据拷贝)。
- 核心特性:
- 高效性:无数据拷贝,进程直接读写内存,适合大数据量、高并发场景。
- 无同步机制:共享内存本身不提供同步和互斥(多个进程同时读写会导致数据错乱),需配合信号量或互斥锁使用。
- 无亲缘关系限制:支持任意进程通信(通过相同 key 关联)。
- 局限性:进程退出后共享内存仍存在(需手动删除),内存大小受系统限制。
- 函数原型:
- 获取共享内存ID

- 对共享内存进行映射,或者解除映射

- 获取或者设置共享内存的相关属性

// 1. 创建/获取共享内存(成功返回共享内存 ID,失败返回 -1)
int shmget(key_t key, size_t size, int shmflg);
// 2. 将共享内存映射到进程虚拟地址空间(成功返回映射地址,失败返回 (void*)-1)
void *shmat(int shmid, const void *shmaddr, int shmflg);
// 3. 解除共享内存与进程的映射(成功返回 0,失败返回 -1)
int shmdt(const void *shmaddr);
// 4. 控制共享内存(删除、获取状态,成功返回 0,失败返回 -1)
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
- 核心流程:
- 进程 A 用 shmget() 创建共享内存(指定大小和权限)。
- 进程 A 用 shmat() 将共享内存映射到自身虚拟地址空间。
- 进程 B 用相同 key 获取共享内存,并用 shmat() 映射到自身地址空间。
- 进程 A/B 直接读写映射地址(共享内存),配合信号量实现同步。
- 通信完成后,用 shmdt() 解除映射,用 shmctl() 删除共享内存
- 示例:
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#define SCHEME_SIZE 1024
int main()
{
//获取KEY值
key_t key=ftok("./",1);
//创建共享内存
int shm_id=shmget(key,SCHEME_SIZE,IPC_CREAT | 0777);
pid_t son=fork();
if(son==0){
printf("子进程,ID为%d\n",getpid());
char *buf=(char *)shmat(shm_id,NULL,0);
while(1){
scanf("%s",buf);
if(strcmp(buf,"exit")==0){
break;
}
}
shmdt(buf);
shmctl(shm_id,IPC_RMID,NULL);
}
else if(son>0){
printf("父进程,ID为%d\n",getpid());
char *buf=(char *)shmat(shm_id,NULL,0);
while(1){
if(strlen(buf)>0){
printf("from son:%s\n",buf);
if(strcmp(buf,"exit")==0){
break;
}
}
sleep(1);
}
shmdt(buf);
shmctl(shm_id,IPC_RMID,NULL);
wait(NULL);
}
else if(son<0){
perror("fork failed\n");
shmctl(shm_id,IPC_RMID,NULL);
}
return 0;
}
5.信号(Signal)—— 最简单的异步通知通信
信号是 Linux 进程间最基础的异步通知机制,用于「通知进程发生了某个事件」,进程可以对信号做出三种响应:默认处理、忽略、自定义处理。
- 核心特性:
- 异步性:信号可随时发送给进程,进程无需主动轮询,收到信号后中断当前执行流程,处理信号函数。
- 携带信息少:仅传递信号编号,无法传递复杂数据(部分实时信号支持携带少量数据)。
- 通用性强:支持所有进程通信,还可用于内核向进程发送通知(如段错误 SIGSEGV)。
- 常用标准信号(共 31 个,无实时性):


- 函数原型:
- 向指定进程发送信号

- 捕捉一个特定信号

- 自己给自己发送一个指定信号

- 将本进程挂起,直至收到一个信号

- 信号集操作函数

- 阻塞或者解除阻塞一个或多个信号

- 给某进程发送一个指定的信号,同时携带一些数据

- 捕捉一个指定信号,且可以通过扩展响应函数来获取信号携带的额外数据

// 1. 注册信号处理函数(设置信号的响应方式)
void (*signal(int sig, void (*handler)(int)))(int);
// 2. 向指定进程发送信号
int kill(pid_t pid, int sig);
// 3. 自定义信号处理函数(用于捕获信号后执行自定义逻辑)
void handler(int sig) {
printf("收到信号:%d\n", sig);
}
- 实操示例:父进程向子进程发送信号
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
// 子进程的信号处理函数
void sig_handler(int sig) {
if (sig == SIGUSR1) { // 自定义用户信号 1
printf("【子进程】收到父进程发送的 SIGUSR1 信号(%d),即将退出\n", sig);
exit(EXIT_SUCCESS);
}
}
int main() {
pid_t pid = fork();
if (pid == -1) {
perror("fork failed");
exit(EXIT_FAILURE);
}
if (pid == 0) {
// 子进程:注册 SIGUSR1 信号的处理函数
signal(SIGUSR1, sig_handler);
printf("【子进程】PID:%d,等待父进程发送信号...\n", getpid());
// 无限循环,等待信号
while (1) {
sleep(1);
}
} else {
// 父进程:睡眠 3 秒后,向子进程发送 SIGUSR1 信号
printf("【父进程】PID:%d,睡眠 3 秒后发送信号...\n", getpid());
sleep(3);
// 向子进程发送 SIGUSR1 信号
if (kill(pid, SIGUSR1) == -1) {
perror("kill failed");
} else {
printf("【父进程】成功向子进程 %d 发送 SIGUSR1 信号\n", pid);
}
// 等待子进程退出
wait(NULL);
printf("【父进程】子进程已退出\n");
}
return 0;
}
6.本地 Socket(AF_UNIX)—— 最通用的本地进程通信
Socket 不仅支持跨主机的网络通信(AF_INET),还支持本地进程间通信(AF_UNIX,也叫 AF_LOCAL),本质是「基于文件系统的套接字文件」,具有通用性强、支持双向通信、无数据大小限制等优点,是本地无亲缘关系进程通信的首选之一。
- 核心特性:
- 通用性强:API 与网络 Socket 一致,便于扩展为跨主机通信。
- 双向通信:支持全双工通信,进程可同时读写。
- 无亲缘关系限制:支持任意本地进程通信(通过套接字文件关联)。
- 基于字节流 / 数据报:支持两种通信模式,灵活适配不同场景。
- 核心流程(AF_UNIX 字节流模式):
- 服务端:
- 创建套接字:socket(AF_UNIX, SOCK_STREAM, 0)。
- 绑定套接字文件:bind()(指定套接字文件路径)。
- 监听连接:listen()。
- 接受连接:accept()。
- 读写数据:read()/write()。
- 客户端:
- 创建套接字:socket(AF_UNIX, SOCK_STREAM, 0)。
- 连接服务端:connect()(指定相同的套接字文件路径)。
- 读写数据:read()/write()。
- 通信完成后,关闭套接字,删除套接字文件。
- 服务端:
- 示例:
- 服务端:
void init_server()
{
struct sockaddr_un addr_server, addr_client;
int addrlen = sizeof(struct sockaddr_un);
server_fd = socket( AF_UNIX, SOCK_STREAM, 0);
if(server_fd<0)
return;
unlink("/var/run/unixsock");
memset( &addr_server, 0, sizeof( addr_server ) );
addr_server.sun_family = AF_UNIX;
strncpy( addr_server.sun_path, "/var/run/unixsock", sizeof( addr_server.sun_path ) - 1 );
if(bind( server_fd, ( struct sockaddr * )&addr_server, addrlen ) < 0)
return;
if(listen( server_fd, 10 ) < 0)
return;
}
void recv_data()
{
int clientfd;
struct sockaddr_un addrclient;
int addrlen = sizeof(struct sockaddr_un);
char buf[1024]={0};
int readlen;
bool flag;
while(1)
{
clientfd=accept( server_fd, ( struct sockaddr * )&addrclient,(socklen_t *)&addrlen );
if(clientfd<0)
return;
flag=true;
while(flag)
{
readlen=read(clientfd,buf,sizeof(buf));
if(readlen<=0)
break;
qDebug().noquote()<<QString(buf);
memset(buf,0,sizeof (buf));
}
close(clientfd);
}
}
- 客户端:
void init_client(void)
{
struct stat buf;
if(access(FILE_CLIENT,F_OK) < 0){
printf("%s is not access\n",FILE_CLIENT);
return;
}
if(stat(FILE_CLIENT,&buf) != 0){
return;
}
if(!S_ISSOCK(buf.st_mode)){
printf("%s is not a socket file\n",FILE_CLIENT);
return;
}
client_fd = socket(AF_UNIX,SOCK_STREAM,0);
if(client_fd < 0){
printf("create socket failed\n");
return;
}
struct sockaddr_un addr_server;
int addrlen = sizeof(struct sockaddr_un);
memset( &addr_server, 0, sizeof( addr_server ) );
addr_server.sun_family = AF_UNIX;
strncpy( addr_server.sun_path, FILE_CLIENT, sizeof( addr_server.sun_path ) - 1 );
if(connect( client_fd, ( struct sockaddr * )&addr_server, addrlen ) < 0){
printf("connect failed\n");
return;
}
client_connect_flag = 1;
}
7.信号量(System V/Posix)
(1)获取信号量ID
(2)对信号量进行P/V操作,或者等零操作
信号量操作结构体的定义如下:
struct sembuf
{
unsigned short sem_num; /*信号量元素序号(数组下标)*/
short sem_op;/*操作参数*/
short sem_flg;/*操作选项*/
};
请注意:信号量元素的序号从0开始,实际上就是数组下标。
根据sem_op的数值,信号量操作分成3种情况:
1.当sem_op大于0时:进行V操作,即信号量元素的值(semval)将会被加上sem_op 的值。如果SEM_UNDO被设置了,那么该V操作将会被系统记录。V操作永远不会导致进程阻塞。
2.当sem_op等于0时:进行等零操作,如果此时semval恰好为0,则semop()立即成功返回,否则如果IPC_NOWAIT被设置,则立即出错返回并将errno设置为EAGAIN,否则将使得进程进入睡眠,直到以下情况发生:
- semval变为0。
- 信号量被删除。(将导致semop()出错退出,错误码为EIDRM)
- 收到信号。(将导致semop()出错退出,错误码为EINTR)
3.当sem_op小于0时:进行P操作,即信号量元素的值(semval)将会被减去sem_op 的绝对值。如果semval大于或等于sem_op的绝对值,则semop()立即成功返回,semval的值将减去sem_op的绝对值,并且如果SEM_UNDO被设置了,那么该P操作将会被系统记录。如果semval小于sem_ op的绝对值并且设置了IPC_NOWAIT,那么semop()将会出错返回且将错误码置为EAGAIN,否则将使得进程进入睡眠,直到以下情况发生:
- semval的值变得大于或者等于sem_op的绝对值。
- 信号量被删除。(将导致semop()出错退出,错误码为EIDRM)
- 收到信号。(将导致semop()出错退出,错误码为EINTR)
(3)获取或者设置信号量的相关属性
8.各类 IPC 方式对比与选型建议
8.1.核心对比总结

8.2.选型建议
- 简单父子进程通信:优先匿名管道(最简单)。
- 无亲缘关系进程简单数据传递:优先命名管道(无需复杂 API)。
- 无亲缘关系进程分类传递数据:优先消息队列(异步按类型传递)。
- 高并发、大数据量传递(如视频、大数据):优先共享内存 + 信号量(最高效)。
- 简单异步通知(如进程终止、异常通知):优先信号(最轻量)。
- 本地进程复杂通信、需兼容跨主机扩展:优先本地 Socket(通用性最强)。
9.核心总结
- Linux 进程间通信的核心是依赖内核中转或共享内核资源,突破进程地址空间的独立性限制。
- 主流 IPC 分为管道类、消息类、共享内存类、信号类、Socket 类,各有优劣,需按场景选型。
- 效率优先级:共享内存 > 信号 > 管道 / 命名管道 > 消息队列 > 本地 Socket。
- 易用性优先级:匿名管道 > 信号 > 命名管道 > 消息队列 > 本地 Socket > 共享内存。
- 生产环境中,共享内存必须配合信号量实现同步,避免数据竞争;所有 System V IPC 资源(消息队列、共享内存、信号量)使用后必须手动删除,避免资源泄漏。
更多推荐




所有评论(0)