一、信号机制概述与架构全景
信号(Signal)是 Linux/Unix 系统中一种异步通知机制,用于通知进程发生了某种事件。它是最古老的进程间通信方式之一,也是内核调度的核心组成部分。信号机制的设计体现了Unix哲学中"一切皆文件"与"事件驱动"两大核心理念。
1.1 信号的生命周期
信号的完整生命周期包含五个阶段:
- 生成(Generation):内核或另一个进程通过 kill()/tgkill()/sigqueue() 发送信号,内核调用 send_signal() 将信号加入目标进程的 pending 队列
- 挂起(Pending):信号保持在 pending 集合中,等待目标进程的处理时机。如果同一信号在阻塞期间多次产生,标准信号仅记录一次,实时信号则排队保留
- 递送(Delivery):当进程从内核态返回用户态时(系统调用返回或中断返回),内核检查 pending 信号并执行处理函数
- 处理(Handling):进程执行注册的信号处理函数(signal handler),或执行默认动作(终止、忽略、core dump、停止、继续)
- 完成(Completion):信号处理函数返回后,若为一次性处理(SA_RESETHAND),则恢复默认处理;若为 SA_RESTART,被中断的系统调用自动重启
1.2 信号在内核中的数据结构
信号相关的核心数据结构贯穿进程描述符(task_struct):
struct task_struct {
struct signal_struct *signal; // 共享信号状态(多线程共享)
sigpending pending; // 私有挂起信号队列
struct sigaction sighand[SIGRTMAX]; // 信号处理动作表
sigset_t blocked; // 信号掩码(阻塞集合)
...
};
struct signal_struct {
atomic_t sigcnt; // 共享该 signal_struct 的线程数
atomic_t live; // 存活线程计数
wait_queue_head_t wait_chldexit; // 等待子进程退出队列
struct sigpending shared_pending; // 共享挂起信号(线程组级别)
struct hlist_head multiprocess; // 多进程链表
int group_exit_code; // 组退出码
struct tty_struct *tty; // 关联终端
...
} ____cacheline_aligned_refcounted;
struct sigpending {
struct list_head list; // sigqueue 链表
sigset_t signal; // 挂起信号的 bitmask
};
标准信号(SIGKILL 之前)与实时信号(SIGRTMIN~SIGRTMAX,通常32~63)的关键区别在于:标准信号不排队——如果同一个信号在阻塞期间多次到达,只会被pending一次;实时信号则支持排队,每次发送都会创建一个新的 sigqueue 节点。
二、信号的生成与投递机制
2.1 内核信号发送路径
信号发送的核心路径如下:
用户空间 内核空间
signal()/raise() ————> do_send_sig_info()
kill()/tgkill() ————> do_send_sig_info()
sigqueue() ————> do_send_sig_info()
——————————————————
|
V
send_signal(sig, info, t)
|
V
__group_send_sig_info(sig, info, t)
|
|--- 检查 SIGKILL/SIGSTOP 的特殊处理
|
|--- 对快速路径:检查目标进程的信号处理是否可合并
|
V
complete_signal() / signal_wake_up()
|
|--- 将目标进程设为 TASK_INTERRUPTIBLE
|--- 唤醒目标进程(TIF_SIGPENDING flag)
2.2 send_signal() 详细实现流程
static int send_signal(int sig, struct kernel_siginfo *info,
struct task_struct *t, enum pid_type type)
{
struct sigpending *pending;
struct sigqueue *q;
// 1. 信号有效性检查
if (!valid_signal(sig))
return -EINVAL;
// 2. 选择挂起队列:线程私有 vs 线程组共享
// type=PIDTYPE_PID → pending = &t->pending(私有队列)
// type=PIDTYPE_TGID → pending = &t->signal->shared_pending(共享队列)
pending = (type != PIDTYPE_PID) ?
&t->signal->shared_pending : &t->pending;
// 3. 如果是标准信号且已在 pending 中,直接返回(不排队)
if (legacy_queue(pending, sig))
return 0;
// 4. 分配 sigqueue 节点(GFP_ATOMIC | __GFP_NOTRACK)
q = __sigqueue_alloc(sig, t, GFP_ATOMIC, NULL, override_rlimit);
if (q) {
// 5a. 成功分配,加入链表
list_add_tail(&q->list, &pending->list);
// 5b. 拷贝信号信息
switch (siginfo_zero_test(info)) {
case 1: copy_siginfo(&q->info, info); break; // 用户态来源
case SIGKILL: case SIGSTOP: /* 直接设置 info pointer */ break;
}
} else if (sig >= SIGRTMIN && (info->si_code != SI_USER)) {
// 5c. 实时信号且来自内核/非资源超限 → 调用 __send_signal 处理
ret = __send_signal(sig, info, t, type, override_rlimit);
// 6. 设置对应比特位
sigaddset(&pending->signal, sig);
} else {
ret = -EAGAIN; // 内存不足,丢弃
}
return ret;
}
2.3 complete_signal() 与唤醒机制
static void complete_signal(int sig, struct task_struct *p)
{
struct signal_struct *signal = p->signal;
// 1. 如果目标正在退出,跳过
if (unlikely(signal->flags & SIGNAL_GROUP_EXIT))
return;
// 2. 选择唤醒线程:优先唤醒未阻塞该信号的线程
// 从 signal->thread_head 链表中遍历线程组
if (wants_signal(sig, p))
return; // 继续唤醒逻辑
// 3. 在所有线程中找一个未阻塞该信号的线程唤醒
signal_wake_up_state(p, __fatal_signal_pending(p) ?
TASK_WAKE_KILL : __TASK_STOPPED | TASK_INTERRUPTIBLE);
// 4. SIGKILL/SIGSTOP 特殊处理:唤醒所有线程
if (sig == SIGKILL)
signal_wake_up(p, 1);
}
三、信号处理函数与执行上下文
3.1 sigaction 结构体详解
struct sigaction {
union {
__sighandler_t sa_handler; // 简单处理:SIG_DFL/SIG_IGN/函数指针
void (*sa_sigaction)(int, siginfo_t *, void *); // SA_SIGINFO 模式
} __sigaction_handler;
sigset_t sa_mask; // 处理函数执行期间额外阻塞的信号
int sa_flags; // 控制标志位
void (*sa_restorer)(void); // 恢复函数(已废弃,保留历史兼容)
};
3.2 sa_flags 关键标志位
| 标志位 | 功能说明 | 使用场景 |
|---|---|---|
| SA_RESTART | 被信号中断的系统调用自动重启(如 read/write/select) | 替代手动 EINTR 重试循环,简化编程模型 |
| SA_RESETHAND | 处理函数执行完毕后,恢复为 SIG_DFL(一次性处理) | 调试场景,防止处理函数递归执行 |
| SA_NODEFER | 处理期间不自动阻塞同种信号(允许递归进入) | 实现定时器链式触发、长期运行的异步任务 |
| SA_SIGINFO | 使用 sa_sigaction 三参数版本,附带 siginfo_t 上下文 | 需要获取发送者 PID、信号来源 UID 的场景 |
| SA_NOCLDWAIT | 子进程退出时不产生 SIGCHLD,不变成僵尸进程 | 服务器守护进程忽略子进程终止 |
| SA_ONSTACK | 在替代信号栈(sigaltstack)上执行信号处理函数 | 主栈溢出、线程信号栈隔离、多线程安全 |
| SA_NOCLDSTOP | 子进程停止/继续时不产生 SIGCHLD | 作业控制场景,减少不必要的中断 |
3.3 信号栈(Signal Stack)
当信号处理函数执行时,内核需要为信号处理提供一个执行上下文。默认情况下,信号处理在用户线程栈上执行,这在以下场景会出问题:
- 线程栈已满(信号处理→立即栈溢出→SIGSEGV→递归)
- 多线程信号栈与线程栈冲突
- 特殊架构(如 x86 的红区 red zone)与信号处理冲突
解决方案是通过 sigaltstack() 注册一个替代信号栈:
#include <signal.h>
stack_t ss;
static char alt_stack[SIGSTKSZ * 4]; // 16KB 替代信号栈
ss.ss_sp = alt_stack;
ss.ss_size = sizeof(alt_stack);
ss.ss_flags = 0;
sigaltstack(&ss, NULL);
// 注册处理函数时指定 SA_ONSTACK
struct sigaction sa;
sa.sa_sigaction = signal_handler;
sa.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
sigemptyset(&sa.sa_mask);
sigaction(SIGSEGV, &sa, NULL);
四、信号掩码与阻塞机制
4.1 sigset_t 信号集操作
信号集通过 sigset_t 位掩码表示,提供以下原子操作函数:
#include <signal.h>
sigset_t set;
sigemptyset(&set); // 清空信号集
sigfillset(&set); // 包含所有信号
sigaddset(&set, SIGINT); // 添加 SIGINT
sigdelset(&set, SIGTERM); // 移除 SIGTERM
int ret = sigismember(&set, SIGUSR1); // 检查是否包含某信号
// 原子的按位操作(注意不是系统调用,而是内联函数)
sigset_t src, dst;
sigandset(&dst, &a, &b); // 交集
sigrandset(&dst, &a, &b); // 重命名后的交集
sigorset(&dst, &a, &b); // 并集
sigandset(&dst, &a); // dst &= a
4.2 sigprocmask() 与 pthread_sigmask()
多线程环境下信号掩码操作需要特别注意:
// 单线程/进程内主线程:使用 sigprocmask
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
// how: SIG_BLOCK(添加), SIG_UNBLOCK(移除), SIG_SETMASK(替换)
// 多线程程序:每个线程有独立的阻塞掩码,必须用 pthread_sigmask
int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
// 正确的多线程信号处理实践:
// 1. 主线程在创建子线程前,阻塞所有需要在特定线程处理的信号
// 2. 各个线程通过 pthread_sigmask 单独解除阻塞
// 3. 信号处理函数保持最小化——仅设置标志位或写管道
// 示例:创建时隔离信号
sigset_t all_signals;
sigfillset(&all_signals);
pthread_sigmask(SIG_BLOCK, &all_signals, NULL); // 主线程阻塞所有
// 后来创建的工作线程各自解开需要的信号
sigset_t worker_set;
sigfillset(&worker_set);
sigdelset(&worker_set, SIGQUIT);
sigdelset(&worker_set, SIGUSR1);
pthread_sigmask(SIG_SETMASK, &worker_set, NULL); // 仅解除需要的
五、实时信号与队列管理
5.1 实时信号队列的排队与丢弃策略
实时信号(SIGRTMIN 到 SIGRTMAX,通常 32~63)的核心优势是排队:每次发送 sigqueue() 都会创建独立的 sigqueue 内核节点。队列上限受以下约束:
// 查看/修改实时信号队列上限
ulimit -a | grep "pending signals" // 默认: 通常 65536
// /proc/sys/kernel/rtsig-max // 全局上限
// /proc/sys/kernel/rtsig-nr // 当前使用计数
// RLIMIT_SIGpending // 每进程限制(setrlimit)
// 队列满时的行为:
// 1. 如果 sigqueue() 指定的结构有效,返回 EAGAIN
// 2. 如果发送者无权限(kill权限检查失败),返回 EPERM
// 3. stdlib abort(): 某些实现会在队列满时调用 _exit(SIGRTMAX+1)
5.2 siginfo_t 上下文传递
SA_SIGINFO 模式允许在信号处理函数中获取丰富的上下文信息:
// siginfo_t 核心字段(字段名因信号类型不同而不同)
void handler(int signo, siginfo_t *info, void *ucontext) {
// 通用字段
int si_signo; // 信号编号
int si_code; // 信号来源代码
pid_t si_pid; // 发送者 PID(kill/sigqueue 用户态)
uid_t si_uid; // 发送者 UID
// 特定信号类型字段
// SI_USER: 由 kill()/raise() 发送
// SI_KERNEL: 由内核直接发送(如 SIGSEGV)
// SI_QUEUE: 由 sigqueue() 发送
// SI_TIMER: 由 POSIX 定时器到期
// SI_ASYNCIO: 由 AIO 完成
// SI_MESGQ: 由 POSIX 消息队列
if (info->si_code == SI_QUEUE) {
// sigqueue 发送时附加的数据
int value = info->si_value.sival_int;
void *ptr = info->si_value.sival_ptr;
// 可用于命令通道、事件通知等
}
if (info->si_code == SI_KERNEL && info->si_signo == SIGSEGV) {
// 内核发送的段错误
void *addr = info->si_addr; // 导致故障的虚拟地址
// 可用于 mmap 保护页面的处理、JIT 编译器的异常触发
}
}
六、signalfd:文件描述符化信号监听
6.1 signalfd 的设计哲学
signalfd 是 Linux 特有的扩展,它将信号封装为文件描述符,使得信号可以通过 read()、epoll、select() 等 I/O 多路复用机制监听,完美融合了
- 事件驱动架构:将信号转换为可读事件,避免异步处理函数带来的竞态条件
- 与 epoll 集成:与网络、定时器、自定义 fd 统一管理,简化事件循环
- 线程隔离:多个线程可独立创建 signalfd,互不干扰
- 避免信号安全(async-signal-safe)限制:处理不再局限于 async-signal-safe 函数
6.2 signalfd API 使用指南
#include <sys/signalfd.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int create_signalfd(sigset_t *mask) {
// 1. 必须先阻塞这些信号(防止被默认处理)
if (sigprocmask(SIG_BLOCK, mask, NULL) == -1) {
perror("sigprocmask");
return -1;
}
// 2. 创建 signalfd(-1 = 新建,fd = 已存在的 fd 进行修改)
int sfd = signalfd(-1, mask, SFD_NONBLOCK | SFD_CLOEXEC);
if (sfd == -1) {
perror("signalfd");
return -1;
}
return sfd;
}
// 读取信号信息
void read_signal(int sfd) {
struct signalfd_siginfo sfd_info;
ssize_t s;
while ((s = read(sfd, &sfd_info, sizeof(sfd_info))) > 0) {
if (s != sizeof(sfd_info)) {
fprintf(stderr, "Read size mismatch\n");
continue;
}
printf("Received signal %d from PID %d (UID %d)\n",
sfd_info.ssi_signo,
sfd_info.ssi_pid,
sfd_info.ssi_uid);
// 处理信号
switch (sfd_info.ssi_signo) {
case SIGINT:
case SIGTERM:
// 优雅退出
goto cleanup;
case SIGUSR1:
// 重新加载配置
reload_config();
break;
case SIGRTMIN:
// 实时信号,获取附加数据
printf("Data: %d\n", sfd_info.ssi_int);
break;
}
}
}
6.3 signalfd + epoll 完整事件循环
#include <sys/epoll.h>
#include <sys/signalfd.hpp>
#include <signal.h>
#include <unistd.h>
...
#define MAX_EVENTS 64
int main() {
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGUSR1);
sigprocmask(SIG_BLOCK, &mask, NULL); // 阻塞这些信号的默认行为
int sfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
// 注册 signalfd 到 epoll
struct epoll_event ev = { .events = EPOLLIN, .data.fd = sfd };
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sfd, &ev);
// 如果需要继续添加其他 fd(socket、timerfd、eventfd)...
// epoll_ctl(epoll_fd, EPOLL_CTL_ADD, timer_fd, &timer_ev);
struct epoll_event events[MAX_EVENTS];
int running = 1;
while (running) {
int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
for (int i = 0; i < nfds; i++) {
if (events[i].data.fd == sfd) {
// 信号处理
struct signalfd_siginfo info;
read(sfd, &info, sizeof(info));
if (info.ssi_signo == SIGINT || info.ssi_signo == SIGTERM)
running = 0;
}
// 可按 events[i].data.fd 分发到其他处理器
}
}
close(sfd);
close(epoll_fd);
return 0;
}
七、生产级信号处理最佳实践
7.1 死亡信号的安全交接(SIGCHLD 处理)
/**
* 最佳实践:在 SIGCHLD 处理中循环 waitpid() 以收割所有僵尸进程
* 原因:标准信号不排队,多个子进程同时退出可能只触发一次处理
*/
void sigchld_handler(int signo) {
(void)signo;
int saved_errno = errno; // 保存 errno(处理函数可能修改它)
siginfo_t info;
int status;
// 循环收割,直到没有更多僵尸子进程
while (1) {
info.si_pid = 0;
// WEXITED | WNOHANG:不阻塞,立即返回
if (waitid(P_ALL, 0, &info, WEXITED | WNOHANG | WNOWAIT) < 0)
break;
if (info.si_pid == 0)
break; // 没有更多僵尸
// 记录退出状态(注意:此时进程仍未完全消亡)
// 实际收割:
waitpid(info.si_pid, &status, WNOHANG);
}
errno = saved_errno; // 恢复 errno
}
7.2 自管道技巧(Self-Pipe Trick)
在 signalfd 之前的标准做法,至今仍适用于不支持 signalfd 的场景(如某些 BSD 系统):
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
static int pipefd[2]; // 自管道:写端在信号处理器,读端在主循环
void signal_handler(int sig) {
int saved_errno = errno;
// 将信号编号写入管道(async-signal-safe 操作)
// 忽略 write() 可能的 EINTR/ENOMEM - 主循环通过 epoll 感知
unsigned char sig_byte = (unsigned char)sig;
write(pipefd[1], &sig_byte, 1);
errno = saved_errno;
}
void setup_self_pipe() {
pipe2(pipefd, O_NONBLOCK | O_CLOEXEC);
struct sigaction sa;
sa.sa_handler = signal_handler;
sa.sa_flags = SA_RESTART; // 避免系统调用被中断
sigemptyset(&sa.mask);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGHUP, &sa, NULL); // 配置重载
sigaction(SIGUSR1, &sa, NULL); // 日志轮转
}
// 主循环中读取管道
void event_loop() {
char sig_buf[16];
while (read(pipefd[0], sig_buf, sizeof(sig_buf)) > 0) {
for (int i = 0; i < sizeof(sig_buf); i++) {
switch (sig_buf[i]) {
case SIGINT: case SIGTERM: goto shutdown;
case SIGHUP: reload_config(); break;
case SIGUSR1: rotate_log(); break;
}
}
}
shutdown:;
}
7.3 多线程程序的信号分发策略
多线程环境下的信号处理设计模式:
模式一:主线程集中处理
┌─────────────────────────────────────────────────────────┐
│ 主线程:epoll_wait + signalfd + signalfd_siginfo解析 │
│ 工作线程:全部阻塞所有信号,专注业务逻辑 │
│ 优点:信号状态一致性,避免竞态 │
│ 缺点:主线程瓶颈 │
├─────────────────────────────────────────────────────────┤
│ 适用场景:Web服务器(Nginx/Apache模式) │
│ 实现:master进程监控signal + 分发至worker │
└─────────────────────────────────────────────────────────┘
模式二:信号分发线程
┌─────────────────────────────────────────────────────────┐
│ 信号监控线程:block all signals → signalfd → epoll │
│ 线程池:各线程各司其职(网络、定时、计算) │
│ 信号类型决定转发目标:管理信号→管理线程,任务信号→工作线程 │
│ 通信方式:eventfd + 队列 │
├─────────────────────────────────────────────────────────┤
│ 适用场景:游戏服务器、交易系统 │
└─────────────────────────────────────────────────────────┘
模式三:每线程独立 signalfd
┌─────────────────────────────────────────────────────────┐
│ 各线程独立 sigset_t mask → 各自 signalfd │
│ 信号路由:通过 kill(pid/tgid) 或 tgkill() 定向发送 │
│ 实时信号排队保证不丢失 │
│ 需要注意:SIGSTOP/SIGKILL/SIGSTOP 为进程级信号 │
├─────────────────────────────────────────────────────────┤
│ 适用场景:线程池、协程运行时 │
└─────────────────────────────────────────────────────────┘
八、高级专题
8.1 rt_sigqueueinfo() 实时信号队列监控
通过 /proc/sys/kernel/rtsig-max 和 /proc/<pid>/status 可监控信号队列状态:
# 查看进程信号状态
cat /proc/<pid>/status | grep -E 'Sig|Shd'
# SigPnd: 进程级挂起信号的 bitmask
# SigBlk: 当前阻塞掩码
# SigCgt: 已捕获(非忽略)信号的 bitmask
# ShdPnd: 线程级挂起信号的 bitmask(2.6.32+)
# 查看实时信号队列使用量
cat /proc/sys/kernel/rtsig-nr # 当前队列使用
cat /proc/sys/kernel/rtsig-max # 全局队列最大数
8.2 信号传递的权限检查流程
内核在信号投递前进行严格的安全检查:
// __send_signal_permission_check() 核心逻辑
static bool kill_ok_by_cred(struct task_struct *t, int sig,
const struct kernel_siginfo *info) {
const struct cred *cred = current_cred();
const struct cred *tcred = __task_cred(t);
// 1. root 用户(CAP_KILL)可以发送给任何进程
if (cred->euid == 0 || cred->uid == tcred->suid)
return true;
// 2. 发送者 UID 必须等于目标 UID 或保存的 suid
if (cred->uid == tcred->uid || cred->uid == tcred->euid ||
cred->suid == tcred->uid || cred->suid == tcred->euid)
return true;
// 3. SI_USER: SIGCONT 例外(同一会话可发)
if (info->si_code == SI_USER && sig == SIGCONT &&
same_session(t))
return true;
return false;
}
8.3 与 TIOCNOTTY 和会话控制的信号交互
控制终端相关的信号:
- SIGHUP:终端挂断时发送给会话首进程。守护进程经典用法:SIGHUP 触发配置文件重载
- SIGTTIN/SIGTTOU:后台进程读取/写入控制终端时发送。可通过
stty -tostop禁用 - SIGTSTP:Ctrl+Z 触发的终端停止信号。与普通停止的区别:可被捕获和忽略
九、总结与速查表
9.1 关键函数对照
| 函数 | 用途 | 替代/演进 |
|---|---|---|
| signal() | 旧版信号处理(语义因系统而异) | 永远使用 sigaction() |
| sigaction() | POSIX 标准信号处理 | 生产环境首选 |
| sigprocmask() | 修改进程阻塞掩码 | 多线程用 pthread_sigmask() |
| sigqueue() | 发送排队实时信号 | 需要传递附带数据时 |
| sigtimedwait() | 同步等待信号 | 替代 pause()+sigsuspend() |
| signalfd() | 文件描述符化信号(Linux特有) | 与 epoll 集成的最佳实践 |
| sigaltstack() | 注册替代信号栈 | 栈溢出保护、多线程 |
| pause()/sigsuspend() | 挂起等待信号 | 推荐用 signalfd + epoll |
9.2 信号动作速查
| 默认动作 | 信号 | 典型用途 |
|---|---|---|
| 终止+Core | SIGSEGV, SIGABRT, SIGBUS, SIGFPE, SIGILL | 程序错误/硬件异常 |
| 终止 | SIGTERM, SIGINT, SIGQUIT, SIGHUP | 标准退出、终端中断、配置重载 |
| 忽略 | SIGCHLD, SIGURG, SIGWINCH | 子进程退出、窗口大小变化 |
| 停止 | SIGTSTP, SIGTTIN, SIGTTOU, SIGSTOP | 作业控制(Ctrl+Z)、后台读写终端 |
| 继续 | SIGCONT | 恢复已停止进程 |
| 不可捕获 | SIGKILL, SIGSTOP | 强制终止/停止的最后手段 |
9.3 常见陷阱与注意事项
- 不可重入函数:信号处理函数中只能调用 async-signal-safe 函数(如 write()、signal()、_exit()),绝对不能调用 malloc()、printf()、pthread_mutex_lock()
- errno 保存:进入处理函数前必须保存 errno,退出时恢复。否则被中断的系统调用返回的错误码可能被覆盖
- 标准信号不排队:如果同一标准信号在阻塞期间多次到达,只被 pending 一次。必须使用实时信号保证可靠性
- 多线程的 SIGKILL/SIGSTOP:这两个信号作用于整个进程(所有线程),无法被阻塞、捕获或忽略
- TLS 修改约束:信号处理函数修改 TLS(线程局部存储)是安全的,因为信号处理在当前线程栈上执行
- SA_RESAVE 不存在:某些资料提到 SA_RETHAND,正确拼写是 SA_RESETHAND(一键恢复)
十、参考资料
- Linux 内核源码:
kernel/signal.c、include/linux/signal.h - The Linux Programming Interface — Michael Kerrisk, Chapters 20-22
- UNIX Network Programming, Vol.1 — W. Stevens, Chapter 16-19
- POSIX.1-2017 Signal Specifications
本文基于 Linux 6.x 内核源码分析,结合生产环境最佳实践,深入剖析信号机制的完整技术栈。

发表评论 取消回复