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

电子说

1.4w人已加入

描述

在 Linux 世界里,进程并非孤立存在。无论是后台服务协作(如 Web 服务器与数据库)、命令行工具联动(如ps | grep),还是复杂应用的模块通信,都离不开进程间通信(IPC,Inter-Process Communication)

今天我们就来系统梳理 Linux 中最常用的 6 种 IPC 方式,从原理到实例,从流程到适用场景,帮你彻底搞懂进程间如何 “对话”。

一、管道:最简单的“单向传送带”

管道是 Linux 中最古老的 IPC 方式,本质是内核中的一块缓冲区,类似 “传送带”—— 数据从一端写入,从另一端读出。

1. 匿名管道(Pipe):只认 “自家人”

特点

•半双工(数据只能单向流动),需创建两个管道实现双向通信;

•仅用于有亲缘关系的进程(父子、兄弟进程,通过 fork 继承管道描述符);

•随进程退出自动销毁,不占用磁盘空间。

实例:父子进程用匿名管道聊天

父进程写消息,子进程读消息:

 

#include #include #include int main() {    int pipefd[2];  // 管道描述符:[0]读端,[1]写端    pipe(pipefd);   // 创建匿名管道    pid_t pid = fork();  // 创建子进程(继承管道描述符)    if (pid == 0) {      // 子进程:读数据        close(pipefd[1]);  // 关闭写端(只需要读)        char buf[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]);    }    return 0;}

 

通信流程

Linux

2. 命名管道(FIFO):陌生人也能聊

特点

•与匿名管道功能类似,但通过文件系统中的路径标识(如/tmp/myfifo);

•可用于无亲缘关系的进程(只要知道 FIFO 路径就能通信);

•是特殊文件(用mkfifo创建),但数据仍存于内存,不落地。

实例:两个独立进程通过 FIFO 通信

写进程(writer.c):

 

#include #include #include #include int main() {    mkfifo("/tmp/myfifo", 0666);  // 创建FIFO文件    int fd = open("/tmp/myfifo", O_WRONLY);  // 打开写端    char *msg = "来自陌生进程的消息";    write(fd, msg, strlen(msg)+1);    close(fd);    return 0;}

 

读进程(reader.c):

 

#include #include #include #include int main() {    int fd = open("/tmp/myfifo", O_RDONLY);  // 打开读端(会阻塞到有写端打开)    char buf[100];    read(fd, buf, sizeof(buf));    printf("收到:%sn", buf);    close(fd);    return 0;}

 

通信流程

Linux

二、信号:进程间的“紧急电报”

信号是 Linux 中最 “轻量” 的 IPC 方式,用于通知进程发生了某种事件(如异常、用户指令),类似 “紧急电报”。

特点

•异步通信(无需进程主动等待);

•携带信息少(仅一个信号编号);

•内核负责递送(进程可注册处理函数)。

常用信号

•SIGINT(2):用户按Ctrl+C,默认终止进程;

•SIGTERM(15):请求进程终止(默认行为);

•SIGUSR1/SIGUSR2(10/12):用户自定义信号。

实例:进程 A 给进程 B 发 “自定义电报”

进程 B(接收方):

 

#include #include // 信号处理函数void handle_usr1(int sig) {    printf("收到SIGUSR1信号!n");}int main() {    signal(SIGUSR1, handle_usr1);  // 注册信号处理函数    printf("我是进程B,PID:%d,等待信号...n", getpid());    while(1);  // 无限循环等待    return 0;}

 

进程 A(发送方):

 

#include #include int main() {    pid_t b_pid = 12345;  // 进程B的PID(需替换为实际值)    kill(b_pid, SIGUSR1);  // 发送SIGUSR1信号给B    printf("已发送SIGUSR1给进程%dn", b_pid);    return 0;}

 

通信流程

Linux

三、共享内存:最快的“公共黑板”

共享内存是速度最快的 IPC 方式 —— 多个进程直接访问同一块物理内存,无需内核 “中转” 数据。

特点

•无数据拷贝(直接操作内存),效率极高;

•需要同步机制(如信号量)防止“同时写” 冲突;

•由内核管理(用shmget创建,shmctl销毁)。

实例:两个进程共享一块内存

写进程(shm_write.c):

 

#include #include #include int main() {    key_t key = ftok(".", 123);  // 生成唯一键值    int shmid = shmget(key, 1024, IPC_CREAT|0666);  // 创建共享内存(大小1024字节)    char *shmaddr = shmat(shmid, NULL, 0);  // 关联到进程地址空间    strcpy(shmaddr, "共享内存中的数据");  // 写入数据    shmdt(shmaddr);  // 解除关联    return 0;}

 

读进程(shm_read.c):

 

#include #include int main() {    key_t key = ftok(".", 123);  // 相同键值    int shmid = shmget(key, 1024, 0666);  // 获取共享内存    char *shmaddr = shmat(shmid, NULL, 0);  // 关联到地址空间    printf("读到共享数据:%sn", shmaddr);  // 读取数据    shmdt(shmaddr);  // 解除关联    shmctl(shmid, IPC_RMID, NULL);  // 删除共享内存    return 0;}

 

通信流程

Linux

四、消息队列:带“标签” 的 “邮件箱”

消息队列是内核中的消息链表,每个消息有“类型标签”,进程可按类型接收,类似 “带标签的邮件箱”。

特点

•数据有结构(消息类型 + 数据),支持按类型读取;

•异步通信(发送方无需等待接收方);

•有大小限制(内核参数MSGMAX控制单条消息最大长度)。

实例:按类型发送 / 接收消息

发送进程(msg_send.c):

 

#include #include #include // 消息结构(必须以long mtype开头)struct msgbuf {    long mtype;  // 消息类型(正数)    char mtext[100];  // 消息内容};int main() {    key_t key = ftok(".", 456);    int msqid = msgget(key, IPC_CREAT|0666);  // 创建消息队列    struct msgbuf msg;    msg.mtype = 1;  // 类型为1    strcpy(msg.mtext, "类型1的消息");    msgsnd(msqid, &msg, sizeof(msg.mtext), 0);  // 发送消息    return 0;}

 

接收进程(msg_recv.c):

 

#include #include struct msgbuf {    long mtype;    char mtext[100];};int main() {    key_t key = ftok(".", 456);    int msqid = msgget(key, 0666);  // 获取消息队列    struct msgbuf msg;    msgrcv(msqid, &msg, sizeof(msg.mtext), 1, 0);  // 只接收类型1的消息    printf("收到类型%d的消息:%sn", msg.mtype, msg.mtext);    msgctl(msqid, IPC_RMID, NULL);  // 删除消息队列    return 0;}

 

通信流程

Linux

五、信号量:进程同步的“红绿灯”

信号量不是用于传递数据,而是控制多个进程对共享资源的访问(如共享内存、文件),类似“红绿灯”。

核心概念

•信号量值(semaphore):>=0 的整数,代表 “可用资源数”;

•P 操作(等待):信号量值 - 1,若值 < 0 则阻塞;

•V 操作(释放):信号量值 + 1,若有进程阻塞则唤醒。

实例:用信号量保护共享内存

(基于共享内存,添加信号量控制):

 

#include // 定义P/V操作(简化版)void P(int semid) {    struct sembuf s = {0, -1, 0};  // 第0个信号量,-1(P操作)    semop(semid, &s, 1);}void V(int semid) {    struct sembuf s = {0, 1, 0};   // +1(V操作)    semop(semid, &s, 1);}// 初始化信号量(值为1,代表互斥)int init_sem() {    key_t key = ftok(".", 789);    int semid = semget(key, 1, IPC_CREAT|0666);    semctl(semid, 0, SETVAL, 1);  // 第0个信号量值设为1    return semid;}// 写进程在访问共享内存前P,写完V;读进程同理

 

同步流程

Linux

六、Socket:跨主机通信的 “万能接口”

Socket(套接字)是最灵活的 IPC 方式,不仅支持同一主机的进程通信,还能跨网络(如服务器与客户端)。

特点

•支持 TCP(可靠、面向连接)和 UDP(不可靠、无连接);

•本地通信可用AF_UNIX协议(通过文件路径标识);

•网络通信用AF_INET协议(通过 IP + 端口标识)。

实例:本地 Socket 通信(AF_UNIX)

服务器(sock_server.c):

 

#include #include #include #include #include int main() {    int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);  // 创建本地套接字    struct sockaddr_un addr;    addr.sun_family = AF_UNIX;    strcpy(addr.sun_path, "/tmp/mysock");  // 本地路径    bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));  // 绑定    listen(sockfd, 5);  // 监听    int connfd = accept(sockfd, NULL, NULL);  // 接受连接    char buf[100];    read(connfd, buf, sizeof(buf));    printf("收到:%sn", buf);    close(connfd);    close(sockfd);    unlink("/tmp/mysock");  // 删除套接字文件    return 0;}

 

客户端(sock_client.c):

 

#include #include #include #include #include int main() {    int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);    struct sockaddr_un addr;    addr.sun_family = AF_UNIX;    strcpy(addr.sun_path, "/tmp/mysock");    connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));  // 连接服务器    char *msg = "本地Socket消息";    write(sockfd, msg, strlen(msg)+1);    close(sockfd);    return 0;}

 

通信流程

Linux

总结:如何选择合适的 IPC 方式?

方式 速度 复杂度 适用场景
匿名管道 父子进程简单单向通信
命名管道 无亲缘关系进程简单通信
信号 快(异步) 事件通知(如异常、退出)
共享内存 最快 高(需同步) 高频、大数据量共享
消息队列 需按类型传递消息的场景
信号量 进程同步与互斥(配合其他 IPC)
Socket 较慢 跨主机或复杂通信(如网络服务)

进程间通信是 Linux 开发的核心基础,理解每种方式的优缺点,才能在实际场景中 “对症下药”。比如日志收集可用命名管道,实时数据共享用共享内存 + 信号量,网络服务则离不开 Socket。

你在开发中用过哪种 IPC 方式?遇到过哪些坑?欢迎在评论区交流~

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分