文章目录
-
- 一、详解 IPC 通信特点,进程与线程的核心对比
-
- 1.1 进程间通信(IPC)的核心概念与特点
- 1.2 进程与线程的核心对比
- 二、Linux 信号 API 与典型使用场景
-
- 2.1 信号的基础认知
-
- 2.1.1 信号的本质与默认动作
- 2.1.2 信号的分类
- 2.2 核心信号 API 详解
-
- 2.2.1 信号处理函数注册
-
- (1)signal ():简易注册函数
- (2)sigaction ():健壮注册函数(推荐)
- 2.2.2 信号发送函数
-
- (1)kill ():向指定进程发送信号
- (2)raise ():向当前进程发送信号
- (3)abort ():强制终止当前进程
- (4)alarm ():设置定时器(触发 SIGALRM)
- 2.2.3 信号集操作函数
- 2.2.4 信号屏蔽与未决信号函数
-
- (1)sigprocmask ():设置进程信号屏蔽字
- (2)sigpending ():获取未决信号集
- 2.2.5 信号等待函数
-
- (1)pause ():挂起进程等待信号
- (2)sigsuspend ():原子操作等待信号
- 2.3 信号的典型使用场景
- 三、信号实战示例与深度解析
-
- 3.1 示例 1:基础信号捕获(signal ())
-
- 代码解析:
- 运行步骤:
- 输出结果:
- 3.2 示例 2:健壮信号处理(sigaction ())
-
- 代码解析:
- 运行步骤:
- 输出结果:
- 3.3 示例 3:父子进程间信号通信
-
- 代码解析:
- 运行步骤:
- 输出结果:
- 3.4 示例 4:信号屏蔽与未决信号
-
- 代码解析:
- 运行步骤:
- 输出结果(5 秒内按 Ctrl+C):
- 3.5 示例 5:超时控制(alarm () + pause ())
-
- 代码解析:
- 测试结果:
- 四、总结
信号(Signal)是 Linux 系统中最基础、最核心的进程间通信(IPC)机制之一,本质是内核向进程发送的软中断,用于实现异步事件通知。本文将从 IPC 通信特点与进程 / 线程对比入手,深入讲解信号的核心 API、使用场景,并通过多个可运行的实战示例,帮助你彻底掌握 Linux 信号的使用方式。
一、详解 IPC 通信特点,进程与线程的核心对比
要理解信号的设计初衷和使用场景,首先需要明确进程间通信的本质,以及进程与线程在通信维度的核心差异。
1.1 进程间通信(IPC)的核心概念与特点
进程间通信(Inter-Process Communication,IPC)是指两个或多个独立进程之间交换数据、传递信息、同步行为的机制。Linux 系统中,IPC 机制包括信号、管道、消息队列、共享内存、套接字等,而信号是其中最轻量、最异步的一种。
IPC 的核心特点可总结为以下 6 点:
进程独立性:每个进程拥有独立的虚拟地址空间,用户态下无法直接访问其他进程的内存,因此所有 IPC 机制都需要内核介入(内核作为中间层完成数据转发或事件通知)。
通信异步性:信号是典型的异步 IPC 机制 —— 进程无需主动轮询事件,信号到达时内核会中断进程的正常执行流程,触发预设的处理逻辑;而管道、共享内存等则偏向同步通信(进程需主动读写)。
数据传输能力
:不同 IPC 机制的传输能力差异显著:
- 信号:仅传递 “事件标识”(整数),无法携带自定义数据(实时信号可携带少量数据,但非核心能力);
- 管道 / 套接字:可传输流式数据,但有内核拷贝开销;
- 共享内存:可传输大量数据,无内核拷贝,但需手动处理同步互斥。
同步与互斥:IPC 需解决多进程竞态问题(如同时读写共享资源),但信号本身不提供同步互斥能力,需配合锁、信号量等机制使用。
开销与效率:信号的内核开销极低(仅传递一个整数),但功能简单;共享内存效率最高,但开发复杂度高;管道 / 套接字居中。
可靠性
:信号分为
不可靠信号
(1-31 号,非实时信号)和
可靠信号
(34-64 号,实时信号):
- 不可靠信号:不排队,若同一信号多次触发且未处理,可能丢失;
- 可靠信号:排队机制,确保所有信号都能被递达,不会丢失。
1.2 进程与线程的核心对比
进程和线程是操作系统中两个核心的执行单元,二者的设计目标不同,导致通信方式和特性差异巨大,具体对比如下表:
表格
| 地址空间 | 完全独立,必须通过 IPC 机制通信 | 共享进程地址空间,可直接访问全局变量 |
| 通信方式 | 信号、管道、共享内存、套接字等 | 全局变量、互斥锁、条件变量、信号量等 |
| 通信开销 | 高(需内核介入,有上下文切换) | 低(用户态即可完成,无内核拷贝) |
| 数据安全性 | 天然隔离,数据泄露风险低 | 共享数据易出现竞态,需同步机制保护 |
| 异步通知 | 信号是主要实现方式 | 可通过条件变量、信号量模拟异步通知 |
| 错误影响范围 | 单个进程崩溃不影响其他进程 | 线程崩溃可能导致整个进程退出 |
| 信号作用范围 | 信号默认发给进程,所有线程共享处理函数 | 可通过pthread_kill发送给指定线程 |
关键补充:
- 进程是 “资源分配的基本单位”,线程是 “CPU 调度的基本单位”;
- 线程间通信无需内核介入(直接访问共享内存),但需处理同步问题;
- 信号是少数能跨进程、跨线程的通用通知机制,也是进程异常处理的核心手段(如段错误触发 SIGSEGV 信号)。
二、Linux 信号 API 与典型使用场景
信号的生命周期分为四个阶段:产生(生成)→ 注册(未决)→ 递送(触发)→ 处理。Linux 提供了一套完整的 API 来操作信号的全生命周期,下面逐一讲解核心 API 及使用场景。
2.1 信号的基础认知
2.1.1 信号的本质与默认动作
信号是内核向进程发送的 “异步事件通知”,常见的信号来源包括:
- 用户操作:如Ctrl+C触发 SIGINT(2 号)、Ctrl+\\触发 SIGQUIT(3 号);
- 硬件异常:如段错误触发 SIGSEGV(11 号)、浮点错误触发 SIGFPE(8 号);
- 进程主动发送:如kill命令、kill()函数发送信号;
- 内核事件:如子进程退出触发 SIGCHLD(17 号)、定时器到期触发 SIGALRM(14 号)。
每个信号都有预设的默认动作,核心类型包括:
- Term:终止进程(如 SIGINT、SIGTERM(15 号));
- Core:终止进程并生成 core dump 文件(如 SIGSEGV、SIGABRT(6 号));
- Ign:忽略信号(如 SIGCHLD 默认忽略);
- Stop:暂停进程(如 SIGSTOP(19 号),不可捕获 / 忽略);
- Cont:恢复暂停的进程(如 SIGCONT(18 号))。
2.1.2 信号的分类
| 不可靠信号 | 1-31 | 不排队、可能丢失、无参数 | SIGINT、SIGTERM |
| 可靠信号 | 34-64 | 排队、不丢失、可带参数 | SIGRTMIN+0~30 |
2.2 核心信号 API 详解
2.2.1 信号处理函数注册
信号的核心操作是 “自定义处理逻辑”,Linux 提供了两个核心函数:signal()(简单但不健壮)和sigaction()(推荐、POSIX 标准)。
(1)signal ():简易注册函数
函数原型:
#include <signal.h>
// 定义信号处理函数类型
typedef void (*sighandler_t)(int);
// 注册信号处理函数
sighandler_t signal(int signum, sighandler_t handler);
参数说明:
-
signum:要处理的信号编号(如SIGINT=2);
- handler
:处理逻辑,可选值:
- SIG_IGN:忽略该信号;
- SIG_DFL:恢复默认动作;
- 自定义函数指针:参数为信号编号,无返回值。
返回值:成功返回旧的处理函数指针,失败返回SIG_ERR。
缺点:
- 跨平台实现不一致(不同系统处理逻辑有差异);
- 信号处理函数执行后,部分系统会恢复为默认动作;
- 无法设置信号屏蔽集、无法获取信号详细信息。
(2)sigaction ():健壮注册函数(推荐)
函数原型:
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
核心结构体struct sigaction:
struct sigaction {
// 普通处理函数(同signal())
void (*sa_handler)(int);
// 高级处理函数(带信号详细信息)
void (*sa_sigaction)(int, siginfo_t *, void *);
// 处理信号时屏蔽的信号集
sigset_t sa_mask;
// 标志位(控制信号处理行为)
int sa_flags;
// 已废弃,无需关注
void (*sa_restorer)(void);
};
参数说明:
-
act:新的信号处理配置(NULL 表示不修改);
-
oldact:输出旧的配置(NULL 表示不获取);
- sa_flags
:核心标志位,常用值:
- SA_RESTART:系统调用被信号中断后自动重启;
- SA_SIGINFO:启用sa_sigaction(高级处理函数);
- SA_NODEFER:处理信号时不屏蔽自身(默认会屏蔽)。
返回值:成功返回 0,失败返回 – 1。
优点:跨平台兼容、可配置屏蔽集、可获取信号详细信息(如发送方 PID)。
2.2.2 信号发送函数
用于主动向进程 / 进程组发送信号,核心函数包括kill()、raise()、abort()、alarm()。
(1)kill ():向指定进程发送信号
函数原型:
#include <signal.h>
int kill(pid_t pid, int sig);
参数说明:
- pid
:目标进程 / 进程组 ID:
- pid > 0:发送给 PID 为pid的进程;
- pid = 0:发送给当前进程组的所有进程;
- pid = -1:发送给所有有权限的进程;
- pid < -1:发送给进程组 ID 为-pid的所有进程;
-
sig:要发送的信号(sig=0时仅检测进程是否存在,不发送信号)。
返回值:成功返回 0,失败返回 – 1。
(2)raise ():向当前进程发送信号
等价于kill(getpid(), sig),原型:
#include <signal.h>
int raise(int sig);
(3)abort ():强制终止当前进程
向当前进程发送SIGABRT(6 号)信号,强制终止并生成 core dump 文件(无法忽略),原型:
#include <stdlib.h>
void abort(void);
(4)alarm ():设置定时器(触发 SIGALRM)
设置秒级定时器,到期后内核向当前进程发送SIGALRM(14 号)信号,原型:
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
参数:seconds=0表示取消定时器;
返回值:之前未到期的定时器剩余秒数。
2.2.3 信号集操作函数
信号集(sigset_t)是表示多个信号的集合,用于屏蔽信号、查询未决信号,核心操作函数:
#include <signal.h>
// 清空信号集
int sigemptyset(sigset_t *set);
// 填充所有信号到集合
int sigfillset(sigset_t *set);
// 添加指定信号到集合
int sigaddset(sigset_t *set, int signum);
// 从集合删除指定信号
int sigdelset(sigset_t *set, int signum);
// 判断信号是否在集合中(1=是,0=否)
int sigismember(const sigset_t *set, int signum);
2.2.4 信号屏蔽与未决信号函数
(1)sigprocmask ():设置进程信号屏蔽字
屏蔽指定信号(使其进入 “未决” 状态,暂时不处理),原型:
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
参数how:
- SIG_BLOCK:将set中的信号添加到屏蔽集;
- SIG_UNBLOCK:从屏蔽集删除set中的信号;
- SIG_SETMASK:用set替换当前屏蔽集。
(2)sigpending ():获取未决信号集
查询当前被屏蔽、尚未处理的信号,原型:
#include <signal.h>
int sigpending(sigset_t *set);
2.2.5 信号等待函数
(1)pause ():挂起进程等待信号
暂停进程执行,直到收到一个信号并处理,原型:
#include <unistd.h>
int pause(void);
返回值:始终返回 – 1,errno=EINTR(被信号中断)。
(2)sigsuspend ():原子操作等待信号
解决pause()的竞态问题(先屏蔽信号再挂起),原型:
#include <signal.h>
int sigsuspend(const sigset_t *mask);
2.3 信号的典型使用场景
三、信号实战示例与深度解析
下面通过 5 个递进式示例,从基础到综合,讲解信号的实际使用方式,所有代码均可直接编译运行。
3.1 示例 1:基础信号捕获(signal ())
功能:捕获Ctrl+C触发的 SIGINT 信号,修改默认 “终止进程” 动作,改为自定义清理后退出。
完整代码(sigint_demo.c):
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
// 自定义SIGINT信号处理函数
void sigint_handler(int signum) {
// signum为触发的信号编号(SIGINT=2)
printf("\\n[处理函数] 捕获到信号:%d(SIGINT)\\n", signum);
// 模拟资源清理:关闭文件、释放内存等
printf("[处理函数] 执行清理操作:关闭日志文件、释放网络连接…\\n");
// 主动退出进程
exit(EXIT_SUCCESS);
}
int main() {
// 注册SIGINT信号的处理函数
if (signal(SIGINT, sigint_handler) == SIG_ERR) {
perror("signal注册失败"); // 打印错误原因
exit(EXIT_FAILURE);
}
// 打印进程PID,方便调试
printf("程序启动,PID:%d\\n", getpid());
printf("按下Ctrl+C触发SIGINT信号…\\n");
// 无限循环等待信号(避免进程退出)
while (1) {
printf("程序正常运行中…\\n");
sleep(1); // 每秒打印一次,避免CPU占用过高
}
return 0;
}
代码解析:
运行步骤:
# 编译
gcc sigint_demo.c -o sigint_demo
# 运行
./sigint_demo
输出结果:
程序启动,PID:12345
按下Ctrl+C触发SIGINT信号…
程序正常运行中…
程序正常运行中…
^C(按下Ctrl+C)
[处理函数] 捕获到信号:2(SIGINT)
[处理函数] 执行清理操作:关闭日志文件、释放网络连接…
3.2 示例 2:健壮信号处理(sigaction ())
功能:使用sigaction()注册 SIGUSR1 信号,配置屏蔽集、获取信号详细信息(发送方 PID)。
完整代码(sigaction_demo.c):
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
// 高级信号处理函数(带详细信息)
void sigusr1_handler(int signum, siginfo_t *info, void *context) {
printf("\\n=== 捕获到SIGUSR1信号 ===\\n");
printf("信号编号:%d\\n", signum); // 信号编号(SIGUSR1=10)
printf("发送进程PID:%d\\n", info->si_pid); // 发送方进程PID
printf("信号携带值:%d\\n", info->si_value.sival_int); // 信号携带的自定义值
printf("=========================\\n");
}
int main() {
struct sigaction act;
// 清空结构体(避免随机内存值导致异常)
memset(&act, 0, sizeof(act));
// 配置高级处理函数
act.sa_sigaction = sigusr1_handler;
// 标志位:启用sa_sigaction + 系统调用重启
act.sa_flags = SA_SIGINFO | SA_RESTART;
// 初始化屏蔽集:处理SIGUSR1时屏蔽SIGINT(Ctrl+C)
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask, SIGINT);
// 注册SIGUSR1信号
if (sigaction(SIGUSR1, &act, NULL) == -1) {
perror("sigaction注册失败");
exit(EXIT_FAILURE);
}
printf("程序启动,PID:%d\\n", getpid());
printf("测试方式:新开终端执行 kill -USR1 %d\\n", getpid());
// 循环等待信号
while (1) {
printf("程序运行中…\\n");
sleep(2);
}
return 0;
}
代码解析:
运行步骤:
# 编译
gcc sigaction_demo.c -o sigaction_demo
# 运行
./sigaction_demo
# 新开终端发送信号
kill -USR1 12346(替换为实际PID)
输出结果:
程序启动,PID:12346
测试方式:新开终端执行 kill -USR1 12346
程序运行中…
程序运行中…
=== 捕获到SIGUSR1信号 ===
信号编号:10
发送进程PID:12347(发送方终端的bash PID)
信号携带值:0
=========================
程序运行中…
3.3 示例 3:父子进程间信号通信
功能:父进程创建子进程,子进程向父进程发送 SIGUSR1,父进程回复 SIGUSR2,子进程收到后退出。
完整代码(parent_child_sig.c):
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
// 全局变量:存储父子进程PID
pid_t child_pid = 0;
pid_t parent_pid = 0;
// 父进程处理SIGUSR1的函数
void parent_sigusr1_handler(int signum) {
printf("[父进程 %d] 收到子进程 %d 的SIGUSR1信号\\n", parent_pid, child_pid);
// 向子进程发送SIGUSR2信号
if (kill(child_pid, SIGUSR2) == -1) {
perror("kill发送SIGUSR2失败");
exit(EXIT_FAILURE);
}
printf("[父进程 %d] 已向子进程 %d 发送SIGUSR2信号\\n", parent_pid, child_pid);
}
// 子进程处理SIGUSR2的函数
void child_sigusr2_handler(int signum) {
printf("[子进程 %d] 收到父进程 %d 的SIGUSR2信号,准备退出\\n", getpid(), parent_pid);
exit(EXIT_SUCCESS);
}
int main() {
parent_pid = getpid();
printf("[父进程 %d] 启动,准备创建子进程\\n", parent_pid);
// 创建子进程
child_pid = fork();
if (child_pid == -1) {
perror("fork失败");
exit(EXIT_FAILURE);
}
if (child_pid == 0) {
// 子进程逻辑
printf("[子进程 %d] 启动,父进程PID:%d\\n", getpid(), parent_pid);
// 注册SIGUSR2处理函数
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = child_sigusr2_handler;
act.sa_flags = SA_RESTART;
if (sigaction(SIGUSR2, &act, NULL) == -1) {
perror("子进程sigaction注册失败");
exit(EXIT_FAILURE);
}
// 向父进程发送SIGUSR1信号
printf("[子进程 %d] 向父进程 %d 发送SIGUSR1信号\\n", getpid(), parent_pid);
if (kill(parent_pid, SIGUSR1) == -1) {
perror("kill发送SIGUSR1失败");
exit(EXIT_FAILURE);
}
// 等待父进程回复
while (1) {
printf("[子进程 %d] 等待父进程回复…\\n", getpid());
sleep(1);
}
} else {
// 父进程逻辑
// 注册SIGUSR1处理函数
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = parent_sigusr1_handler;
act.sa_flags = SA_RESTART;
if (sigaction(SIGUSR1, &act, NULL) == -1) {
perror("父进程sigaction注册失败");
exit(EXIT_FAILURE);
}
// 等待子进程退出(避免僵尸进程)
printf("[父进程 %d] 等待子进程退出…\\n", parent_pid);
waitpid(child_pid, NULL, 0);
printf("[父进程 %d] 子进程已退出,父进程结束\\n", parent_pid);
}
return 0;
}
代码解析:
运行步骤:
gcc parent_child_sig.c -o parent_child_sig
./parent_child_sig
输出结果:
[父进程 12348] 启动,准备创建子进程
[父进程 12348] 等待子进程退出…
[子进程 12349] 启动,父进程PID:12348
[子进程 12349] 向父进程 12348 发送SIGUSR1信号
[父进程 12348] 收到子进程 12349 的SIGUSR1信号
[父进程 12348] 已向子进程 12349 发送SIGUSR2信号
[子进程 12349] 收到父进程 12348 的SIGUSR2信号,准备退出
[父进程 12348] 子进程已退出,父进程结束
3.4 示例 4:信号屏蔽与未决信号
功能:屏蔽 SIGINT 信号,触发后查看未决信号,解除屏蔽后处理。
完整代码(sig_mask_pending.c):
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
// SIGINT处理函数
void sigint_handler(int signum) {
printf("\\n[处理函数] 捕获到SIGINT信号(%d)\\n", signum);
}
// 打印未决信号集
void print_pending_signals() {
sigset_t pending_set;
// 获取未决信号集
if (sigpending(&pending_set) == -1) {
perror("sigpending失败");
exit(EXIT_FAILURE);
}
printf("当前未决信号:");
// 遍历1-31号信号,检查是否未决
for (int i = 1; i <= 31; i++) {
if (sigismember(&pending_set, i)) {
printf("%d ", i);
}
}
printf("\\n");
}
int main() {
sigset_t mask_set;
// 注册SIGINT处理函数
if (signal(SIGINT, sigint_handler) == SIG_ERR) {
perror("signal注册失败");
exit(EXIT_FAILURE);
}
// 初始化屏蔽集,添加SIGINT
sigemptyset(&mask_set);
sigaddset(&mask_set, SIGINT);
// 屏蔽SIGINT信号
printf("[主进程] 屏蔽SIGINT信号…\\n");
if (sigprocmask(SIG_BLOCK, &mask_set, NULL) == -1) {
perror("sigprocmask屏蔽失败");
exit(EXIT_FAILURE);
}
printf("[主进程] PID:%d,5秒内按下Ctrl+C触发SIGINT…\\n", getpid());
sleep(5); // 5秒内按Ctrl+C,信号进入未决状态
// 查看未决信号
print_pending_signals();
// 解除SIGINT屏蔽
printf("[主进程] 解除SIGINT屏蔽,信号将被处理…\\n");
if (sigprocmask(SIG_UNBLOCK, &mask_set, NULL) == -1) {
perror("sigprocmask解除失败");
exit(EXIT_FAILURE);
}
// 再次查看未决信号(已处理)
sleep(1);
print_pending_signals();
printf("[主进程] 程序正常退出\\n");
return 0;
}
代码解析:
运行步骤:
gcc sig_mask_pending.c -o sig_mask_pending
./sig_mask_pending
输出结果(5 秒内按 Ctrl+C):
[主进程] 屏蔽SIGINT信号…
[主进程] PID:12350,5秒内按下Ctrl+C触发SIGINT…
^C(按下Ctrl+C,无立即反应)
当前未决信号:2
[主进程] 解除SIGINT屏蔽,信号将被处理…
[处理函数] 捕获到SIGINT信号(2)
当前未决信号:
[主进程] 程序正常退出
3.5 示例 5:超时控制(alarm () + pause ())
功能:实现函数执行超时控制,5 秒内未完成则触发 SIGALRM 终止程序。
完整代码(sig_alarm_demo.c):
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
// 超时处理函数
void alarm_handler(int signum) {
printf("\\n[超时] 函数执行超过5秒,强制终止!\\n");
exit(EXIT_FAILURE);
}
// 模拟耗时函数
void time_consuming_func(int seconds) {
printf("[函数] 开始执行,预计耗时%d秒…\\n", seconds);
sleep(seconds); // 模拟耗时操作
printf("[函数] 执行完成!\\n");
}
int main() {
// 注册SIGALRM处理函数
if (signal(SIGALRM, alarm_handler) == SIG_ERR) {
perror("signal注册失败");
exit(EXIT_FAILURE);
}
// 设置5秒定时器
printf("[主进程] 设置5秒超时定时器…\\n");
alarm(5);
// 调用耗时函数(测试:3秒=正常,6秒=超时)
time_consuming_func(6); // 改为3秒测试正常情况
// 取消定时器(若函数提前完成)
alarm(0);
printf("[主进程] 程序正常结束\\n");
return 0;
}
代码解析:
测试结果:
-
测试 1(超时)
time_consuming_func(6)
[主进程] 设置5秒超时定时器…
[函数] 开始执行,预计耗时6秒…[超时] 函数执行超过5秒,强制终止!
-
测试 2(正常)
time_consuming_func(3)
[主进程] 设置5秒超时定时器…
[函数] 开始执行,预计耗时3秒…
[函数] 执行完成!
[主进程] 程序正常结束
四、总结
- 注册函数:优先使用sigaction()(健壮、可配置),避免signal()(兼容性差);
- 发送函数:kill()(跨进程)、alarm()(定时器)是最常用的信号发送方式;
- 控制函数:sigprocmask()(屏蔽信号)、sigpending()(查询未决信号)用于精细控制信号生命周期;
信号是 Linux 进程通信的基础,掌握信号的使用不仅能解决进程间异步通知问题,也是理解 Linux 内核进程调度、异常处理机制的关键。在实际开发中,需注意信号的可靠性(优先使用实时信号)、屏蔽集的配置(避免关键信号被中断),以及与其他 IPC 机制(如共享内存、锁)的配合使用。






