Question

Can anyone give me the steps or even the code for the following situation:

A process which contains multiple thread, and of these threads is responsible of catching a user defined signal SIGUSR1. Only this thread should be capable of receiving this signal, and upon the reception of this signal I do some stuff.

In my situation the signal is being sent by a Kernel Module to my Process ID. Then it is the responsibility of my process to deliver it to the correct listening thread, which has also established the Signal Handler i.e. the signal handler is not in the main thread.

I already did some code which runs for a single-thread process, but I have a problem in running it in multiple thread environment.

I am running my code on Linux Ubuntu 12.04.3 with Kernel Version 3.8.0-29. And for the creation of the process I am mixing between Boost Threads and POSIX threads API.

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/mman.h>
#include <string.h>

/* Value of the last signal caught */
volatile sig_atomic_t sig_value;

static void sig_handler(const int sig_number, siginfo_t *sig_info, void *context)
{
if (sig_number == SIGSEGV)
{
    error_sys("Error at address 0x%lx", (long)sig_info->si_addr);
    exit(-1);
}
sig_value = sig_number;
}


int init_signal_catcher()
{
struct sigaction sig_action; /* Structure describing the action to be taken when asignal arrives.  */

sigset_t oldmask;  /* Signal mask before signal disposition change.      */
sigset_t newmask;  /* Signal mask after signal disposition change.       */
sigset_t zeromask; /* Signal mask to unblock all signal while suspended. */

/* Define signal mask and install signal handlers */
memset(&sig_action, 0, sizeof(struct sigaction));

sig_action.sa_flags = SA_SIGINFO;
sig_action.sa_sigaction = sig_handler;

/* Examine and change a signal action. */
sigaction(SIGHUP, &sig_action, NULL);
sigaction(SIGINT, &sig_action, NULL);
sigaction(SIGTERM, &sig_action, NULL);
sigaction(SIGSEGV, &sig_action, NULL);
sigaction(SIGUSR1, &sig_action, NULL);

    /* Block SIGHUP, SIGINT, SIGTERM, SIGSEGV and SIGUSR1 signals. */
sigemptyset(&newmask);
sigaddset(&newmask, SIGHUP);
sigaddset(&newmask, SIGINT);
sigaddset(&newmask, SIGTERM);
sigaddset(&newmask, SIGSEGV);
sigaddset(&newmask, SIGUSR1);

/* Examine and change blocked signals. */
pthread_sigmask(SIG_BLOCK, &newmask, &oldmask);

/* Initialize the empty signal set. */
sigemptyset(&zeromask);
sig_value = 0;

while ((sig_value != SIGINT) && (sig_value != SIGTERM))
{
    sig_value = 0;

    /*
     * Go to sleep (unblocking all signals) until a signal is catched.
     * On return from sleep, the signals SIGHUP, SIGINT, SIGTERM and
         * SIGUSR1 are again blocked.
     */
        printf("Suspending on %lu mask.", zeromask);

        // Wait for a signal.
    sigsuspend(&zeromask);

    switch(sig_value)
    {
                printf("Caught Signal %d", sig_value);
        case SIGUSR1:
                    printf("Caught SIGUSR1");
                    break;
    }
}

return 0;
}
Was it helpful?

Solution

The signals need to be blocked in every thread. The safest way to do this is to block them in the first thread before any others are created. Then a single, specially chosen thread can call sigsuspend() and only that thread will execute the signal handlers.

void *signal_handling_thread(void *whatever) {
  sig_value := 0
  while (sig_value not in (SIGTERM, SIGINT)) {
    sigsuspend(empty_mask)
    ...
  }
  ...
}

int main(int argc, char **argv) {
  block_relevant_signals();               // SIG_BLOCK HUP, TERM, USR1, etc.
  catch_relevant_signals();               // SA_SIGINFO ...

  spawn_signal_handling_thread();         // spawned with relevant signals blocked

  for (int i = 0; i < NUM_WORKERS; i++) {
    spawn_worker_thread();                // spawned with relevant signals blocked
  }
  ...
}

It's time to refactor your code to break apart concerns — do global process attribute manipulation in one place, signal-specific reaction in another, etc.

OTHER TIPS

In your signal handler, you are calling exit(-1). exit(-1) is not asynchronous signal-handler safe. Use _exit(-1) instead.

The difference between the two functions is that exit() calls all of the registered atexit() routines (including C++ static destructors). Before exit() does that shutdown step, it uses pthread_mutex_lock() to ensure a thread-safe shutdown. If the lock happens to be held by another thread, your program will deadlock.

_exit() skips those atexit routines and terminates the process.

I'm not familiar with error_sys(), but it looks like it ends up using printf()/fprintf(). Those routines also tend to be protected by mutexes.

Here is an example to organize which thread gets which signal using pthread_sigmask: http://man7.org/linux/man-pages/man3/pthread_sigmask.3.html

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top