简体   繁体   中英

How to create a thread for signal handling and exit the process upon receiving the signal?

I written the below code to handle signals in separate thread to forcefully cleanup some resources and exit the complete process.

Here is the brief note about the below code.

  • When the signal is received, set volatile sig_atomic_t sig_set_flag = 1; inside signal handler.
  • In signal_handler_thread, checking sig_set_flag value in a loop.
  • if(sig_set_flag==1) send notifications like "i am going down" from signal_handler_thread and call exit(0); from the thread.

Signals can be received by any thread in a process. So i am setting the global variable.

I have 2 questions.

1) This implementation is fine? or i have to block the signals for the main thread and handle only by the spawned thread ?

2) How to block a signal to the main process and handle it in a thread?

#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);

}

Note:I know signals will interrupt the some system calls and EINTR will be set. Unfortunately some system calls (ie) waitpid() will not be interrupted. So i spawned a thread to handle this scenario.

1) Your implementation seems to be correct. signal() and sigaction() register a handler function for the whole process, so it doesn't matter you call them in the main thread or in the spawned thread.

2) To block a signal in the main thread, and handle it in a thread, you have to design, not a handler function, but a handler thread, using sigwait() or sigwaitinfo() . So the thread will wait for the signals and the program execution won't be interrupted.

In this case, you have to block process-wide signals in all the threads, including the main thread. If it is not blocked, the signal will have the default behavior on the program.

You have to use pthread_sigmask() to block one or more signals. An example of code to block SIGTERM:

sigset_t set;
sigemptyset(&set);
sigaddset(&set,SIGTERM);
pthread_sigmask(SIG_BLOCK,&set,NULL);

When a thread is created, it inherits of the blocked signals of the creator thread.

I modified your code to show you how to use sigwaitinfo() and 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);

}

Also, be careful of the fork() , from the tests I have done, the child process will inherit of the blocked signals.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM