一、管道通信
管道本质是内核开辟的一块缓冲区,遵循FIFO先进先出规则,分为无名管道pipe和有名管道FIFO(mkfifo)。
1.1 无名管道(匿名管道 pipe)
无名管道只能用于有亲缘关系进程之间通信(父子、兄弟进程)。
原理:父进程调用`pipe()`创建管道得到两个文件描述符,fork创建子进程后,子进程复制父进程文件描述符,父子进程就可以操作同一组管道fd完成数据传输。
函数原型
#include <unistd.h>
int pipe(int pipefd2);
-
pipefd0:管道读端,使用read读取数据
-
pipefd1:管道写端,使用write写入数据
-
返回值:成功返回0;失败返回1
管道核心特性
-
半双工单向通信;数据只能单向流动;
-
缓冲区默认大小64K;
-
写阻塞:管道写满,write会阻塞;
-
读阻塞:管道为空,read会阻塞;
-
读返回0:管道写端全部关闭,read返回0,代表读到末尾;
-
管道破裂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 | 用户自定义预留信号,业务自己使用 |
信号三种处理方式
进程收到信号,可以选择下面三种处理:
-
SIG_IGN 忽略:收到信号直接无视,不做处理
-
SIG_DFL 默认处理:操作系统默认行为(终止、暂停、打印core转储等)
-
捕获信号:注册自定义处理函数,收到信号执行自己写的回调函数
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;
}