面试题答案
一键面试Linux中信号在多线程程序中的行为特性
- 信号分配:
- 在多线程程序中,信号通常发送到进程,而不是特定线程。如果没有特殊设置,任何线程都可能接收到信号。但是,可以通过设置线程掩码(使用
pthread_sigmask
函数)来控制哪个线程可以接收信号。 - 某些信号(如
SIGKILL
和SIGSTOP
)是不能被捕获或忽略的,并且会立即终止进程,与线程设置无关。
- 在多线程程序中,信号通常发送到进程,而不是特定线程。如果没有特殊设置,任何线程都可能接收到信号。但是,可以通过设置线程掩码(使用
- 确保信号处理的正确性和线程安全:
- 信号处理函数应该是可重入的。这意味着信号处理函数在被调用时,不应该依赖于可能正在被其他线程修改的全局或静态数据结构,避免数据竞争。
- 使用线程特定数据(TSD,Thread - Specific Data)来确保每个线程有自己独立的数据副本,从而避免在信号处理时与其他线程的数据冲突。
- 可以通过阻塞信号(使用
pthread_sigmask
),在特定线程中安全地处理信号,防止信号在不合适的时候中断线程的关键操作。
POSIX线程信号函数
pthread_sigmask
:- 功能:用于设置线程的信号掩码,决定线程是否阻塞某些信号。
- 原型:
int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
- 参数:
how
:指定如何修改信号掩码,有SIG_BLOCK
(阻塞信号集中的信号)、SIG_UNBLOCK
(解除阻塞信号集中的信号)、SIG_SETMASK
(将信号掩码设置为指定的信号集)。set
:指向要操作的信号集。oldset
:指向一个信号集,用于保存原来的信号掩码(可以为NULL
)。
- 返回值:成功返回0,失败返回错误码。
pthread_kill
:- 功能:向指定线程发送信号。
- 原型:
int pthread_kill(pthread_t thread, int sig);
- 参数:
thread
:目标线程的ID。sig
:要发送的信号。
- 返回值:成功返回0,失败返回错误码。
sigwait
:- 功能:等待信号集里的信号到达,线程会阻塞直到有信号到达。
- 原型:
int sigwait(const sigset_t *set, int *sig);
- 参数:
set
:指向要等待的信号集。sig
:用于返回接收到的信号值。
- 返回值:成功返回0,失败返回错误码。
多线程中安全处理信号的示例代码
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// 信号处理函数
void *signal_handler(void *arg) {
sigset_t *set = (sigset_t *)arg;
int sig;
while (1) {
// 等待信号
if (sigwait(set, &sig) != 0) {
perror("sigwait");
pthread_exit(NULL);
}
switch (sig) {
case SIGINT:
printf("Received SIGINT in signal handler thread\n");
break;
case SIGTERM:
printf("Received SIGTERM in signal handler thread\n");
pthread_exit(NULL);
default:
printf("Received unknown signal %d in signal handler thread\n", sig);
}
}
return NULL;
}
int main() {
pthread_t signal_thread;
sigset_t set;
// 初始化信号集
sigemptyset(&set);
// 添加要处理的信号
sigaddset(&set, SIGINT);
sigaddset(&set, SIGTERM);
// 阻塞主线程中的信号
if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) {
perror("pthread_sigmask");
return 1;
}
// 创建信号处理线程
if (pthread_create(&signal_thread, NULL, signal_handler, (void *)&set) != 0) {
perror("pthread_create");
return 1;
}
// 主线程继续执行其他任务
while (1) {
printf("Main thread is running...\n");
sleep(1);
}
// 等待信号处理线程结束
if (pthread_join(signal_thread, NULL) != 0) {
perror("pthread_join");
return 1;
}
return 0;
}
在这个示例中:
- 主线程阻塞了
SIGINT
和SIGTERM
信号。 - 创建了一个专门的线程来处理这两个信号,使用
sigwait
等待信号到达并进行处理。这样可以确保信号处理的线程安全性,避免信号在主线程执行关键操作时打断主线程。