我编写了以下代码来在单独的线程中处理信号,以强制清理一些资源并退出整个过程。
这里是关于下面代码的简短注释。
- 收到信号后,在信号处理程序中设置volatile sig_atomic_t sig_set_flag = 1;。
- 在 signal_handler_thread 中,循环检查 sig_set_flag 值。
- if(sig_set_flag==1) 从 signal_handler_thread 发送诸如“我正在下降”之类的通知并调用 exit(0); 从线程。
进程中的任何线程都可以接收信号。所以我正在设置全局变量。
我有 2 个问题。
1) 这个实现没问题?或者我必须阻塞主线程的信号并仅由生成的线程处理?
2) 如何阻塞主进程的信号并在线程中处理?
#include <stdio.h>
#include <signal.h>
#include <pthread.h>
#include <stdatomic.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
/*
* Set this variable if any signal is received
*/
volatile sig_atomic_t sig_set_flag = 0;
pthread_mutex_t cleanup_mutex;
/*
* Resource cleanup function.
*/
int cleaup_resources() {
pthread_mutex_lock(&cleanup_mutex);
/*
* Send notification to all the clients.
* Delete all the temp files
*/
printf("Notified to clients.Exiting process\n");
pthread_mutex_unlock(&cleanup_mutex);
return 0;
}
/*
* Signal handler thread
*/
void sig_term_handler(int sig_num) {
sig_set_flag = sig_num;
}
/*
* Signal handler thread routine
*/
void *signal_handler_thread(void * args) {
while(1) {
if(sig_set_flag != 0) {
printf("%s : Signal flag is set for sig_no %d\n",__func__,sig_set_flag);
cleaup_resources();
break;
}
usleep(5);
}
exit(0);
}
int main()
{
int loop_count,status;
pthread_t tid;
pid_t pid;
struct sigaction sig;
sig.sa_handler = &sig_term_handler;
sig.sa_flags = 0;
sigaction(SIGTERM, &sig, NULL);
/*
* Spawn a thread to monitor signals.
* If signal received, Exit the process.
*/
pthread_create(&tid, NULL, signal_handler_thread, NULL);
while(1) {
printf("Some time consuming task in progress... PID = %d\n",getpid());
pid = fork();
if(pid == 0) {
sleep(100);
return 0;
} else {
waitpid(pid, &status, 0);
loop_count++;
if( loop_count>=10)
break;
}
}
cleaup_resources();
exit(0);
}
注意:我知道信号会中断一些系统调用并且 EINTR 会被设置。不幸的是,某些系统调用(即 waitpid())不会被中断。所以我生成了一个线程来处理这种情况。
最佳答案
1) 您的实现似乎是正确的。 signal()
和 sigaction()
为整个进程注册了一个处理函数,因此无论您是在主线程还是在派生线程中调用它们都没有关系。
2) 要在主线程中阻塞信号,并在线程中处理它,您必须设计一个处理线程,而不是处理函数,使用 sigwait()
或 信号等待信息()
。因此线程将等待信号,程序执行不会被中断。
在这种情况下,您必须在所有线程(包括主线程)中阻塞进程范围的信号。如果未被阻止,信号将具有程序的默认行为。
您必须使用pthread_sigmask()
来阻止一个或多个信号。阻止 SIGTERM 的代码示例:
sigset_t set;
sigemptyset(&set);
sigaddset(&set,SIGTERM);
pthread_sigmask(SIG_BLOCK,&set,NULL);
创建线程时,它会继承创建线程的阻塞信号。
我修改了您的代码以向您展示如何使用 sigwaitinfo()
和 pthread_sigmask()
:
#include <stdio.h>
#include <signal.h>
#include <pthread.h>
#include <stdatomic.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
pthread_mutex_t cleanup_mutex;
/*
* Resource cleanup function.
*/
int cleaup_resources() {
pthread_mutex_lock(&cleanup_mutex);
/*
* Send notification to all the clients.
* Delete all the temp files
*/
printf("Notified to clients.Exiting process\n");
pthread_mutex_unlock(&cleanup_mutex);
return 0;
}
/*
* Signal handler thread routine
*/
void *signal_handler_thread(void * args) {
sigset_t set;
sigemptyset(&set);
sigaddset(&set,SIGINT);
siginfo_t info;
while(1) {
sigwaitinfo(&set,&info);
if(info.si_signo == SIGINT){
printf("\nSIGINT received\n");
cleaup_resources();
exit(0);
}
}
}
int main()
{
int loop_count,status;
pthread_t tid;
pid_t pid;
sigset_t set;
sigemptyset(&set);
sigaddset(&set,SIGINT);
pthread_sigmask(SIG_BLOCK,&set,NULL);
// The new thread will inherit the blocked
// signals from the thread that create it:
pthread_create(&tid, NULL, signal_handler_thread, NULL);
while(1) {
printf("Some time consuming task in progress... PID = %d\n",getpid());
pid = fork();
if(pid == 0) {
sleep(100);
return 0;
} else {
waitpid(pid, &status, 0);
loop_count++;
if( loop_count>=10)
break;
}
}
cleaup_resources();
exit(0);
}
另外,注意fork()
,从我做过的测试来看,子进程会继承被阻塞的信号。
关于c - 如何创建一个线程来处理信号并在收到信号后退出进程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48375541/