如何创建信号处理线程并在收到信号后退出进程?

时间:2018-01-22 06:06:14

标签: c linux signals

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

以下是有关以下代码的简要说明。

  • 收到信号后,在信号处理程序内设置 volatile sig_atomic_t sig_set_flag = 1;
  • 在signal_handler_thread中,检查循环中的sig_set_flag值。
  • 如果(sig_set_flag == 1)发送通知,例如"我正在下降"来自 signal_handler_thread 并从主题中调用退出(0);

进程中的任何线程都可以接收信号。所以我正在设置全局变量。

我有两个问题。

1)这个实现没问题?或者我必须阻止主线程的信号并仅由产生的线程处理?

2)如何阻止信号进入主进程并在线程中处理它?<​​/ p>

#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 个答案:

答案 0 :(得分:0)

1)您的实施似乎是正确的。 signal()sigaction()为整个过程注册一个处理函数,因此在主线程或生成的线程中调用它们并不重要。

2)要阻止主线程中的信号,并在线程中处理它,您必须使用sigwait()sigwaitinfo()来设计处理程序线程,而不是处理程序线程。因此,线程将等待信号,程序执行不会被中断。

在这种情况下,您必须在所有线程中阻止进程范围的信号,包括主线程。如果未被阻止,则信号将具有程序的默认行为。

您必须使用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(),从我做过的测试中,子进程将继承被阻塞的信号。