在Linux世界里,进程并非孤立存在。无论是后台服务协作(如Web服务器与数据库)、命令行工具联动(如ps | grep),还是复杂应用的模块通信,都离不开进程间通信(IPC,Inter-Process Communication)。
今天我们就来系统梳理Linux中最常用的6种IPC方式,从原理到实例,从流程到适用场景,帮你彻底搞懂进程间如何“对话”。
一、管道:最简单的“单向传送带”
管道是Linux中最古老的IPC方式,本质是内核中的一块缓冲区,类似“传送带”——数据从一端写入,从另一端读出。
1.匿名管道(Pipe):只认“自家人”
特点:
•半双工(数据只能单向流动),需创建两个管道实现双向通信;
•仅用于有亲缘关系的进程(父子、兄弟进程,通过fork继承管道描述符);
•随进程退出自动销毁,不占用磁盘空间。
实例:父子进程用匿名管道聊天
父进程写消息,子进程读消息:
#include#include #include intmain(){ intpipefd[2]; // 管道描述符:[0]读端,[1]写端 pipe(pipefd); // 创建匿名管道 pid_tpid = fork(); // 创建子进程(继承管道描述符) if(pid ==0) { // 子进程:读数据 close(pipefd[1]); // 关闭写端(只需要读) charbuf[100]; read(pipefd[0], buf,sizeof(buf)); // 从管道读 printf("子进程收到:%sn", buf); close(pipefd[0]); }else{ // 父进程:写数据 close(pipefd[0]); // 关闭读端(只需要写) char*msg ="Hi,我是父进程!"; write(pipefd[1], msg,strlen(msg)+1); // 写入管道 close(pipefd[1]); } return0;}
通信流程:

2.命名管道(FIFO):陌生人也能聊
特点:
•与匿名管道功能类似,但通过文件系统中的路径标识(如/tmp/myfifo);
•可用于无亲缘关系的进程(只要知道FIFO路径就能通信);
•是特殊文件(用mkfifo创建),但数据仍存于内存,不落地。
实例:两个独立进程通过FIFO通信
写进程(writer.c):
#include#include #include #include intmain(){ mkfifo("/tmp/myfifo",0666); // 创建FIFO文件 intfd =open("/tmp/myfifo", O_WRONLY); // 打开写端 char*msg ="来自陌生进程的消息"; write(fd, msg,strlen(msg)+1); close(fd); return0;}
读进程(reader.c):
#include#include #include #include intmain(){ intfd =open("/tmp/myfifo", O_RDONLY); // 打开读端(会阻塞到有写端打开) charbuf[100]; read(fd, buf,sizeof(buf)); printf("收到:%sn", buf); close(fd); return0;}
通信流程:

二、信号:进程间的“紧急电报”
信号是Linux中最“轻量”的IPC方式,用于通知进程发生了某种事件(如异常、用户指令),类似“紧急电报”。
特点:
•异步通信(无需进程主动等待);
•携带信息少(仅一个信号编号);
•内核负责递送(进程可注册处理函数)。
常用信号:
•SIGINT(2):用户按Ctrl+C,默认终止进程;
•SIGTERM(15):请求进程终止(默认行为);
•SIGUSR1/SIGUSR2(10/12):用户自定义信号。
实例:进程A给进程B发“自定义电报”
进程B(接收方):
#include#include // 信号处理函数voidhandle_usr1(intsig){ printf("收到SIGUSR1信号!n");}intmain(){ signal(SIGUSR1, handle_usr1); // 注册信号处理函数 printf("我是进程B,PID:%d,等待信号...n",getpid()); while(1); // 无限循环等待 return0;}
进程A(发送方):
#include#include intmain(){ pid_tb_pid =12345; // 进程B的PID(需替换为实际值) kill(b_pid, SIGUSR1); // 发送SIGUSR1信号给B printf("已发送SIGUSR1给进程%dn", b_pid); return0;}
通信流程:

三、共享内存:最快的“公共黑板”
共享内存是速度最快的IPC方式——多个进程直接访问同一块物理内存,无需内核“中转”数据。
特点:
•无数据拷贝(直接操作内存),效率极高;
•需要同步机制(如信号量)防止“同时写”冲突;
•由内核管理(用shmget创建,shmctl销毁)。
实例:两个进程共享一块内存
写进程(shm_write.c):
#include#include #include intmain(){ key_tkey =ftok(".",123); // 生成唯一键值 intshmid =shmget(key,1024, IPC_CREAT|0666); // 创建共享内存(大小1024字节) char*shmaddr =shmat(shmid,NULL,0); // 关联到进程地址空间 strcpy(shmaddr,"共享内存中的数据"); // 写入数据 shmdt(shmaddr); // 解除关联 return0;}
读进程(shm_read.c):
#include#include intmain(){ key_tkey =ftok(".",123); // 相同键值 intshmid =shmget(key,1024,0666); // 获取共享内存 char*shmaddr =shmat(shmid,NULL,0); // 关联到地址空间 printf("读到共享数据:%sn", shmaddr); // 读取数据 shmdt(shmaddr); // 解除关联 shmctl(shmid, IPC_RMID,NULL); // 删除共享内存 return0;}
通信流程:

四、消息队列:带“标签”的“邮件箱”
消息队列是内核中的消息链表,每个消息有“类型标签”,进程可按类型接收,类似“带标签的邮件箱”。
特点:
•数据有结构(消息类型+数据),支持按类型读取;
•异步通信(发送方无需等待接收方);
•有大小限制(内核参数MSGMAX控制单条消息最大长度)。
实例:按类型发送/接收消息
发送进程(msg_send.c):
#include#include #include // 消息结构(必须以long mtype开头)structmsgbuf{ longmtype; // 消息类型(正数) charmtext[100]; // 消息内容};intmain(){ key_tkey =ftok(".",456); intmsqid =msgget(key, IPC_CREAT|0666); // 创建消息队列 structmsgbufmsg; msg.mtype =1; // 类型为1 strcpy(msg.mtext,"类型1的消息"); msgsnd(msqid, &msg,sizeof(msg.mtext),0); // 发送消息 return0;}
接收进程(msg_recv.c):
#include#include structmsgbuf{ longmtype; charmtext[100];};intmain(){ key_tkey =ftok(".",456); intmsqid =msgget(key,0666); // 获取消息队列 structmsgbufmsg; msgrcv(msqid, &msg,sizeof(msg.mtext),1,0); // 只接收类型1的消息 printf("收到类型%d的消息:%sn", msg.mtype, msg.mtext); msgctl(msqid, IPC_RMID,NULL); // 删除消息队列 return0;}
通信流程:

五、信号量:进程同步的“红绿灯”
信号量不是用于传递数据,而是控制多个进程对共享资源的访问(如共享内存、文件),类似“红绿灯”。
核心概念:
•信号量值(semaphore):>=0的整数,代表“可用资源数”;
•P操作(等待):信号量值- 1,若值< 0 则阻塞;
•V操作(释放):信号量值+ 1,若有进程阻塞则唤醒。
实例:用信号量保护共享内存
(基于共享内存,添加信号量控制):
#include// 定义P/V操作(简化版)voidP(intsemid){ structsembufs = {0,-1,0}; // 第0个信号量,-1(P操作) semop(semid, &s,1);}voidV(intsemid){ structsembufs = {0,1,0}; // +1(V操作) semop(semid, &s,1);}// 初始化信号量(值为1,代表互斥)intinit_sem(){ key_tkey =ftok(".",789); intsemid =semget(key,1, IPC_CREAT|0666); semctl(semid,0, SETVAL,1); // 第0个信号量值设为1 returnsemid;}// 写进程在访问共享内存前P,写完V;读进程同理
同步流程:

六、Socket:跨主机通信的“万能接口”
Socket(套接字)是最灵活的IPC方式,不仅支持同一主机的进程通信,还能跨网络(如服务器与客户端)。
特点:
•支持TCP(可靠、面向连接)和UDP(不可靠、无连接);
•本地通信可用AF_UNIX协议(通过文件路径标识);
实例:本地Socket通信(AF_UNIX)
服务器(sock_server.c):
#include#include #include #include #include intmain(){ intsockfd =socket(AF_UNIX, SOCK_STREAM,0); // 创建本地套接字 structsockaddr_unaddr; addr.sun_family = AF_UNIX; strcpy(addr.sun_path,"/tmp/mysock"); // 本地路径 bind(sockfd, (structsockaddr*)&addr,sizeof(addr)); // 绑定 listen(sockfd,5); // 监听 intconnfd =accept(sockfd,NULL,NULL); // 接受连接 charbuf[100]; read(connfd, buf,sizeof(buf)); printf("收到:%sn", buf); close(connfd); close(sockfd); unlink("/tmp/mysock"); // 删除套接字文件 return0;}
客户端(sock_client.c):
#include#include #include #include #include intmain(){ intsockfd =socket(AF_UNIX, SOCK_STREAM,0); structsockaddr_unaddr; addr.sun_family = AF_UNIX; strcpy(addr.sun_path,"/tmp/mysock"); connect(sockfd, (structsockaddr*)&addr,sizeof(addr)); // 连接服务器 char*msg ="本地Socket消息"; write(sockfd, msg,strlen(msg)+1); close(sockfd); return0;}
通信流程:

总结:如何选择合适的IPC方式?
| 方式 | 速度 | 复杂度 | 适用场景 |
| 匿名管道 | 中 | 低 | 父子进程简单单向通信 |
| 命名管道 | 中 | 中 | 无亲缘关系进程简单通信 |
| 信号 | 快(异步) | 低 | 事件通知(如异常、退出) |
| 共享内存 | 最快 | 高(需同步) | 高频、大数据量共享 |
| 消息队列 | 中 | 中 | 需按类型传递消息的场景 |
| 信号量 | 快 | 中 | 进程同步与互斥(配合其他IPC) |
| Socket | 较慢 | 高 | 跨主机或复杂通信(如网络服务) |
进程间通信是Linux开发的核心基础,理解每种方式的优缺点,才能在实际场景中“对症下药”。比如日志收集可用命名管道,实时数据共享用共享内存+信号量,网络服务则离不开Socket。
你在开发中用过哪种IPC方式?遇到过哪些坑?欢迎在评论区交流~
-
Linux
+关注
关注
88文章
11627浏览量
217887 -
IPC
+关注
关注
3文章
375浏览量
54540
发布评论请先 登录

Linux进程间通信(IPC)全解析:从管道到 Socket,一篇讲透
评论