在Linux中,线程和信号之间存在复杂但重要的交互关系。理解这种关系对于开发多线程应用程序至关重要。
同步信号:由线程自身触发(如SIGSEGV, SIGFPE)
异步信号:由外部进程或内核发送(如SIGINT, SIGTERM)
定向信号:使用pthread_kill()或tgkill()发送给特定线程
现象:在多线程程序中,异步信号可能被任意线程接收,导致不确定行为
解决方案:
// 在主线程设置信号处理程序
signal(SIGINT, handler);
// 在所有其他线程阻塞该信号
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGINT);
pthread_sigmask(SIG_BLOCK, &set, NULL);
现象:信号处理程序中只能调用异步信号安全函数
解决方案: - 在信号处理程序中设置标志位 - 在主线程中检查并处理标志位 - 使用自管道技术(self-pipe trick)
// 示例:自管道技术
int pipefd[2];
pipe(pipefd);
void handler(int sig) {
write(pipefd[1], "x", 1);
}
// 在主线程中监控pipefd[0]
现象:线程取消请求(PTHREAD_CANCEL)可能被信号中断
解决方案:
// 设置线程取消点为推迟取消
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
// 执行关键代码段
// 恢复取消状态
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_testcancel(); // 显式检查取消点
专用信号处理线程:
使用signalfd:
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTERM);
int sfd = signalfd(-1, &mask, 0);
// 可以像普通文件描述符一样监控sfd
避免混合信号与线程取消:
谨慎使用pthread_kill:
strace -f
跟踪多线程程序的信号/proc/[pid]/task/[tid]/status
中的信号信息handle
命令控制信号处理理解Linux中线程和信号的交互关系可以帮助开发者编写更健壮的多线程应用程序,避免常见的并发问题和竞态条件。