欢迎光临
我们一直在努力

【Linux】一篇文章搞懂进程间通信方式

1. 进程间通信的概念

进程是操作系统资源分配的基本单位,每个进程拥有独立的虚拟地址空间,进程间无法直接访问彼此的内存数据。进程间通信(IPC,InterProcess Communication)是操作系统提供的核心机制:内核在系统空间开辟专用缓冲区,允许进程将数据从用户空间拷贝到内核缓冲区,再由其他进程从内核缓冲区读取,以此实现进程间的数据交换、同步或通知。

2. 进程间通信的7种方式

2.1 管道/匿名管道(pipe)

匿名管道是Linux最基础的IPC方式,本质是内核维护的单向字节流缓冲区,仅适用于有亲缘关系的进程(父子/兄弟进程)通信。

管道的核心特性:

  • 半双工通信:数据仅能沿一个方向流动,双向通信需创建两个独立管道;
  • 内存级文件:管道不属于文件系统,仅存在于内核内存中,随进程退出自动释放;
  • FIFO规则:数据按“先进先出”顺序读写,读取后数据从缓冲区删除,且无法随机定位(如lseek操作);
  • 阻塞特性:读端无数据时阻塞,写端缓冲区满时阻塞;若写端关闭,读端读取到EOF后返回;若读端关闭,写端会触发SIGPIPE信号并退出。

管道的局限:

  • 仅支持亲缘进程通信,无跨进程访问能力;
  • 无消息边界,需自定义数据格式(如固定长度、分隔符);
  • 缓冲区大小固定(通常几KB),无法传输大量数据。
2.2 有名管道(FIFO)

有名管道(FIFO)是匿名管道的扩展,核心区别是通过文件系统中的特殊文件标识,因此支持任意无亲缘关系的进程通信。

有名管道的核心特性:

  • 文件标识:FIFO文件存储在文件系统中(可通过ls -l查看,类型为p),但数据仍存于内核内存;
  • 跨进程访问:任意进程只要有权限访问FIFO文件路径,即可通过该管道通信;
  • 兼容FIFO规则:与匿名管道一致,数据按先进先出读写,不支持随机定位;
  • 阻塞/非阻塞模式:
    • 默认阻塞:打开FIFO时,读端等待写端打开,写端等待读端打开;
    • 非阻塞模式:通过O_NONBLOCK标志打开,无对应进程时直接返回错误,不阻塞;
    • 读写模式(O_RDWR):同一进程可同时读写,不会阻塞。

有名管道与匿名管道的核心区别:

特性匿名管道有名管道
标识形式 无名称,仅文件描述符 有文件路径,可见于文件系统
通信范围 亲缘进程 任意进程
生命周期 随进程退出释放 需手动unlink删除文件
打开方式 pipe()创建 mkfifo()创建+open()打开
2.3 信号(Signal)

信号是Linux最轻量化的IPC机制,本质是异步通知,用于告知进程发生特定事件(而非传输数据),是软件层面对硬件中断的模拟。

信号的核心特性:

  • 异步性:信号可随时发送给进程,无需等待进程执行状态;若进程阻塞该信号,内核会暂存至解除阻塞后传递;
  • 有限数据量:仅传递“信号类型”(编号),无附加数据(部分信号可携带少量数据,如SIGQUEUE);
  • 预定义行为:每个信号有默认处理方式(终止、忽略、暂停等),也可通过signal()/sigaction()自定义处理函数;
  • 不可靠性:早期System V信号(如SIGINT)不保证有序送达,POSIX实时信号(SIGRTMIN~SIGRTMAX)支持排队,可靠性更高。

Linux常用信号及场景:

信号名编号触发场景默认行为
SIGINT 2 按下Ctrl+C 终止进程
SIGKILL 9 kill -9 进程PID 强制终止(不可捕获)
SIGTERM 15 kill 进程PID(默认) 终止进程(可捕获)
SIGPIPE 13 向无读端的管道写数据 终止进程
SIGCHLD 17 子进程退出/暂停 忽略
SIGALRM 14 alarm()定时器到期 终止进程

信号的生命周期:

  • 产生:硬件(如Ctrl+C)或软件(kill()、alarm())触发;
  • 传递:内核将信号标记到目标进程的信号集,若未阻塞则立即传递;
  • 处理:进程暂停当前执行,执行信号处理函数(自定义/默认),执行完成后恢复原上下文。
  • 2.4 消息队列(Message Queue)

    消息队列是内核中的结构化消息链表,解决了管道无格式、信号数据量少的问题,支持按“消息类型”读写数据。

    消息队列的核心特性:

    • 结构化存储:每个消息包含“类型标识”和“数据内容”,进程可按类型读取指定消息(无需FIFO);
    • 独立于进程:消息队列存于内核,进程退出后消息仍保留,需手动删除或内核重启时释放;
    • 异步通信:发送方写入消息后无需等待接收方,接收方可在任意时间读取;
    • 容量限制:单条消息大小、队列总容量受内核参数限制(如msgmax、msgmnb)。

    Linux主流消息队列类型:

    • System V消息队列:通过msgget()/msgsnd()/msgrcv()实现,标识符为key值,使用最广泛;
    • POSIX消息队列:通过mq_open()/mq_send()/mq_receive()实现,基于文件路径标识,支持超时读取。

    消息队列的优势:

    • 克服管道“无格式字节流”的缺点,可按业务定义消息结构;
    • 支持多发送方/多接收方,且可按优先级读取消息(按类型筛选);
    • 无需进程同步等待,降低耦合度。
    2.5 共享内存(Shared Memory)

    共享内存是效率最高的IPC方式,本质是内核开辟一块物理内存,让多个进程将其映射到自身虚拟地址空间,进程可直接读写该内存,无需数据拷贝。

    共享内存的核心特性:

    • 零拷贝:数据直接在进程虚拟地址空间与共享内存间交互,无内核缓冲区中转(管道/消息队列需2次拷贝:用户→内核→用户);
    • 映射机制:通过mmap()(POSIX)或shmat()(System V)将共享内存映射到进程地址空间,解除映射用munmap()/shmdt();
    • 无内置同步:多个进程同时读写会导致数据竞争,需配合信号量、互斥锁等同步机制;
    • 生命周期:POSIX共享内存需shm_unlink()删除,System V共享内存需shmctl(IPC_RMID)删除,否则内核重启前一直存在。

    共享内存的实现方式:

    • POSIX共享内存:推荐使用,通过shm_open()创建内存对象,ftruncate()设置大小,接口更简洁;
    • System V共享内存:通过shmget()创建,基于key值标识,需手动管理标识符。
    2.6 信号量(Semaphore)

    信号量是内核中的计数器,核心作用是实现进程间的同步与互斥,控制多个进程对共享资源的访问。

    信号量的核心特性:

    • 原子操作:信号量的增减操作(P/V原语)是原子的,避免多进程竞争;
      • P操作(wait):信号量值减1,若结果≤0则进程阻塞;
      • V操作(post):信号量值加1,唤醒阻塞的进程;
    • 类型分类:
      • 二值信号量:值为0/1,用于互斥(如控制共享内存的独占访问);
      • 计数信号量:值为非负整数,用于控制资源数量(如限制同时访问的进程数);
    • 实现方式:
      • System V信号量:基于key值,通过semget()/semop()/semctl()实现;
      • POSIX信号量:有名信号量(文件路径标识)、基于内存的信号量(共享内存中),接口更易用。

    信号量与互斥量的区别:

    特性信号量互斥量
    取值范围 非负整数 仅0/1(锁状态)
    操作主体 不同进程/线程可操作 加锁/解锁需同一线程
    核心用途 同步+互斥(多资源) 仅互斥(单资源)
    阻塞唤醒 可唤醒任意等待进程 仅唤醒一个等待线程
    2.7 套接字(Socket)

    套接字是跨网络/跨主机的IPC机制,不仅支持同一主机的进程通信,还能实现不同主机间的进程通信,是网络编程的核心基础。

    套接字的核心特性:

    • 通信范围:本地进程(AF_UNIX域)或网络进程(AF_INET/AF_INET6域);
    • 标识属性:由“域+端口号+协议类型”唯一确定;
      • 域:AF_UNIX(本地通信,基于文件系统)、AF_INET(IPv4网络);
      • 端口号:0-65535,1024以下为系统保留端口;
      • 协议类型:
        • SOCK_STREAM(流套接字):基于TCP,可靠、有序、面向连接;
        • SOCK_DGRAM(数据报套接字):基于UDP,无连接、不可靠、速度快;
        • SOCK_RAW(原始套接字):直接访问网络层(IP),用于自定义协议/网络抓包;
    • 通信模型:
      • 客户端/服务器(C/S):服务器绑定端口监听,客户端主动连接;
      • 无连接模型(UDP):无需建立连接,直接发送数据报。

    套接字通信的基本流程(TCP):

  • 服务器端:
    • socket():创建套接字描述符;
    • bind():绑定IP和端口;
    • listen():监听连接请求;
    • accept():接受客户端连接,创建新套接字用于通信;
    • read()/write():与客户端收发数据;
    • close():关闭套接字。
  • 客户端:
    • socket():创建套接字描述符;
    • connect():连接服务器IP和端口;
    • read()/write():与服务器收发数据;
    • close():关闭套接字。
  • 总结

  • 性能优先级:共享内存(最快,零拷贝)> 消息队列 > 管道/FIFO > 套接字 > 信号(仅通知);
  • 适用场景:
    • 亲缘进程简单通信:匿名管道;
    • 无亲缘进程本地通信:有名管道/FIFO;
    • 跨网络通信:套接字;
    • 高并发数据传输:共享内存+信号量(同步);
    • 异步通知/简单控制:信号;
    • 结构化数据异步通信:消息队列;
  • 注意:共享内存、消息队列等需手动释放资源,否则会造成内核资源泄漏;多进程并发访问共享资源时,必须配合信号量/互斥锁保证同步。
  • 赞(0)
    未经允许不得转载:171主机测评 » 【Linux】一篇文章搞懂进程间通信方式
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址