Linux系统编程|进程间通信IPC全套详解(1)(管道、信号)

一、管道通信

管道本质是内核开辟的一块缓冲区,遵循FIFO先进先出规则,分为无名管道pipe和有名管道FIFO(mkfifo)。

1.1 无名管道(匿名管道 pipe)

无名管道只能用于有亲缘关系进程之间通信(父子、兄弟进程)。

原理:父进程调用`pipe()`创建管道得到两个文件描述符,fork创建子进程后,子进程复制父进程文件描述符,父子进程就可以操作同一组管道fd完成数据传输。

函数原型

#include <unistd.h>

int pipe(int pipefd2);

  • pipefd0:管道读端,使用read读取数据

  • pipefd1:管道写端,使用write写入数据

  • 返回值:成功返回0;失败返回1

管道核心特性

  1. 半双工单向通信;数据只能单向流动;

  2. 缓冲区默认大小64K;

  3. 写阻塞:管道写满,write会阻塞;

  4. 读阻塞:管道为空,read会阻塞;

  5. 读返回0:管道写端全部关闭,read返回0,代表读到末尾;

  6. 管道破裂SIGPIPE:管道读端全部关闭,继续写管道,会产生SIGPIPE信号,进程默认直接退出。

简单示例逻辑:父进程创建pipe,fork子进程;父关闭读端往pipefd1写数据;子关闭写端从pipefd0读数据。

1.2 有名管道 FIFO(mkfifo)

无名管道局限于亲缘进程,有名管道解决这个问题,可以用于本机任意两个进程通信。

它会在文件系统生成一个特殊管道文件,不同进程通过open打开这个文件,就可以完成通信。

函数原型

#include <sys/types.h>

#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

  • pathname:管道文件路径

  • mode:文件权限,例如0664

  • 返回值:成功0,失败1

创建完成后,就像普通文件一样使用open/read/write/close操作。

注意:打开FIFO会发生阻塞:一个进程以读打开,会阻塞等待另一个进程以写打开;反之亦然。

无名管道 vs 有名管道对比

|------------|-------------------|-------------------|
| 特性 | 无名管道 pipe | 有名管道 FIFO |
| 适用进程 | 仅父子 / 兄弟亲缘进程 | 本机任意进程 |
| 文件系统 | 无实体文件,内核缓冲区 | 生成 FIFO 特殊文件 |
| 创建方式 | pipe() | mkfifo() |
| 生命周期 | 进程全部退出自动销毁 | 文件存在,可手动 rm 删除 |

二、信号 Signal ------ 异步软中断通知

信号是Linux提供的异步通知机制,信号本身不携带大量业务数据,仅仅用来通知进程发生某个事件,相当于软件层面的中断。

常见信号列表

|--------------|-----------------|------------------------|
| 信号编号 | 宏名称 | 作用说明 |
| 2 | SIGINT | Ctrl+C,中断进程 |
| 3 | SIGQUIT | Ctrl+\,进程退出 |
| 9 | SIGKILL | 强制杀死进程,不能捕获、不能忽略 |
| 11 | SIGSEGV | 段错误,访问非法内存触发 |
| 13 | SIGPIPE | 管道破裂,写已经没有读端的管道 |
| 14 | SIGALRM | alarm 定时超时信号 |
| 17 | SIGCHLD | 子进程退出时,发送给父进程,用来回收僵尸进程 |
| 19 | SIGSTOP | 强制暂停进程,不可捕获忽略 |
| 10/12 | SIGUSR1、SIGUSR2 | 用户自定义预留信号,业务自己使用 |

信号三种处理方式

进程收到信号,可以选择下面三种处理:

  1. SIG_IGN 忽略:收到信号直接无视,不做处理

  2. SIG_DFL 默认处理:操作系统默认行为(终止、暂停、打印core转储等)

  3. 捕获信号:注册自定义处理函数,收到信号执行自己写的回调函数

signal注册信号函数

#include <signal.h>

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

  • signum:要处理的信号编号

  • handler:处理方式,SIG_IGN / SIG_DFL / 自定义回调函数地址

  • 返回值:成功返回旧处理函数;失败返回SIG_ERR

注意:signal函数接口简单,但部分行为在不同版本Linux存在差异;正式项目推荐使用sigaction。

简单示例:捕获Ctrl+C信号,不直接杀死程序

#include <stdio.h>

#include <signal.h>

#include <unistd.h>

void sig_handler(int sig)

{

printf("收到信号 %d,程序不会直接退出\n", sig);

}

int main(void)

{

signal(SIGINT, sig_handler);

while(1)

{

sleep(1);

printf("程序运行中...\n");

}

return 0;

}

相关推荐
Ivanqhz8 小时前
矩阵引擎的数据流模式与 BM1684X 架构
java·服务器·网络·深度学习·神经网络
formulahendry9 小时前
微信和 VS Code 强强联合!WeChat AHP 来了
visual studio code·vs code
安易算力9 小时前
昇腾生态开发深度实践:CANN算子库架构解析与MindSpore模型优化
网络·容器·架构·kubernetes·vllm
雷✘9 小时前
C 程序从源文件到运行:预处理、编译、汇编、链接重定位与执行环境
c语言
传奇开心果编程9 小时前
【Rust入门练中学】 第1课:从零开始
开发语言·学习·rust
m0_715674439 小时前
智能化+基于行标+场景化 政务数据库审计与风险监测全维度解决方案
网络·数据库·安全·网络安全·政务
Logic1019 小时前
C语言/数据结构位运算题解:异或XOR找出数据表中的“独特编号“——只出现一次的数字
c语言·数据结构·数组·位运算·时间复杂度·算法题·异或性质
高亦真10 小时前
今天是学习嵌入式的第39天
linux·学习·算法
AR-26710-10 小时前
Linux Day8——FHS文件系统层次
linux
Brilliantwxx10 小时前
【STM32】 SPI阻塞式通信与HAL源码(串口读ID实战)
开发语言·stm32·单片机·嵌入式硬件·ecmascript