在Linux系统中,信号(Signal)是进程间通信的关键机制,通常用于通知进程发生特定事件。例如,用户按下Ctrl+C
会发送SIGINT
信号来终止进程,或程序访问非法内存时触发SIGSEGV
信号。信号处理是进程响应这些事件的核心机制。
Linux支持多种信号(可通过kill -l
查看)。常见信号及其默认行为如下:
终止进程:
SIGTERM
(优雅终止)
SIGKILL
(强制终止,不可捕获)
SIGINT
(Ctrl+C)
核心转储:
SIGSEGV
(段错误)
SIGFPE
(算术错误)
挂起/继续:
SIGHUP
(终端断开)
SIGSTOP
(暂停进程,不可捕获)
其他:
SIGALRM
(定时器)
SIGCHLD
(子进程状态变化)
进程可通过注册信号处理函数自定义信号行为,常用的API如下:
1. signal()
函数 ️
#include
void (*signal(int signum, void (*handler)(int)))(int);
功能:为信号signum
绑定处理函数handler
。
示例:
void handler(int sig) {
printf("Received SIGINT\n");
exit(0);
}
signal(SIGINT, handler); // 捕获Ctrl+C
缺点:行为因系统而异,缺乏灵活性(如无法控制信号屏蔽)。
2. sigaction()
函数(推荐)
#include
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
参数:
act
:指定新行为,通过struct sigaction
配置:
sa_handler
:处理函数(或SIG_IGN
/SIG_DFL
)。
sa_mask
:在处理信号时阻塞其他信号。
sa_flags
:控制行为(如SA_RESTART
自动重启被中断的系统调用)。
oldact
:保存旧的处理配置。
示例:
struct sigaction act;
act.sa_handler = handler;
sigemptyset(&act.sa_mask); // 不阻塞其他信号
act.sa_flags = 0;
sigaction(SIGINT, &act, NULL);
可重入性:避免使用非异步安全函数(如printf
、malloc
),可能引发竞态条件。
使用volatile变量或原子操作传递状态到主程序。
执行时间短:避免长时间操作,尽快返回。
避免修改全局状态:如需修改,需通过信号屏蔽等手段保证原子性。
通过信号掩码控制进程对信号的响应:
sigprocmask()
:设置/修改进程的信号掩码。
sigpending()
:检查未决(被阻塞)的信号。
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigprocmask(SIG_BLOCK, &set, NULL); // 阻塞SIGINT
范围:SIGRTMIN
到SIGRTMAX
(如32-64)。
特点:支持排队(避免丢失)、可携带额外数据(通过sigqueue()
发送)。
使用:
union sigval value;
value.sival_int = 123;
sigqueue(pid, SIGRTMIN, value); // 发送附带数据的信号
命令:kill -信号 PID
(如kill -9 1234
)。
系统调用:
kill()
:向指定进程发送信号。
raise()
:向自身发送信号。
alarm()
:设置定时器(触发SIGALRM
)。
信号处理函数是进程级:所有线程共享同一处理函数。
信号掩码是线程级:各线程可独立设置阻塞的信号。
建议:
主线程统一处理信号(通过sigwait()
同步等待)。
避免在多线程中使用异步信号处理。
优雅退出:捕获SIGTERM
释放资源。
定时任务:通过SIGALRM
触发定时操作。
错误调试:处理SIGSEGV
记录错误信息。
sigaction
处理SIGINT
#include
#include
#include
volatile sig_atomic_t flag = 0;
void handler(int sig) {
flag = 1; // 安全设置标志位
}
int main() {
struct sigaction sa;
sa.sa_handler = handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGINT, &sa, NULL);
while (!flag) {
sleep(1);
printf("Running...\n");
}
printf("Exiting gracefully\n");
return 0;
}
Linux信号处理是控制进程行为的重要机制。通过合理使用signal()
或sigaction()
注册处理函数,结合信号阻塞、实时信号等特性,可以实现灵活的事件响应。需要特别注意处理函数的可重入性,避免竞态条件,确保程序健壮性。