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;

}

相关推荐
Experience-摆渡13 分钟前
自建 IP 轮换方案实战:健康探测 + 熔断剔除 + 粘性会话
网络·网络协议·tcp/ip
春风解人意21 分钟前
从零开始学习嵌入式P28----线程
c语言·学习·算法
m0_7345717623 分钟前
深入理解C++ 类型转换<三>const_cast
开发语言·c++
luj_176824 分钟前
合法避税与投资评估实战指南
c语言·开发语言·网络·经验分享·算法
库玛西30 分钟前
Linux网络编程:HTTP/HTTPS协议核心技术全解析
linux·运维·服务器·网络·c++·http·https
sxd20011 小时前
Vmware和multicast
linux·windows·vmware·无线桥接·multicast
暴力求解1 小时前
Linux网络---NAT代理服务、内网穿透
linux·网络·智能路由器
二十雨辰1 小时前
[Java]-JVM面试题
java·开发语言·jvm
cfm_29141 小时前
ConcurrentHashMap 线程安全机制与 JDK 1.8 演进
java·开发语言·安全