c - 如何创建一个线程来处理信号并在收到信号后退出进程?

标签 c linux signals

我编写了以下代码来在单独的线程中处理信号,以强制清理一些资源并退出整个过程。

这里是关于下面代码的简短注释。

  • 收到信号后,在信号处理程序中设置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/

相关文章:

更改我在 C 函数中传递的指针

c - 将 int[][] 形式的变量传递给 int 类型的方法**

c - 在主 MEX 网关函数中准备输出数据

Pythonnormpath 不使用混合路径分隔符进行规范化

child 使用pause()收到信号后不继续运行

php - 用 C 语言编写的基本 Web 服务器,支持 PHP

linux - 安装依赖 SciKit/NumPy/SciPy

linux - 使用 Nagios 磁盘进行监控 - NRPE - Linux

c - accept() 中断信号和 epoll_wait()

debugging - 如何获取 Postgres 中某些信号(例如 SIGINT)的处理程序名称/地址?