Selma Selma - 3 months ago 19
Linux Question

how to alternate continously Signal handler

I want to write a program in c for linux that catchs the first SIGUSR1 signal, ignores the second one and continue in this behaviour (catch-ignore) for the successive SIGUSR1 signals.

I wonder how to keep alternating between the two handlers, because once i set the handler to SIG_IGN, the signal will be ignored and I won't be able to detect it and act accordingly.

This is the code i have tried:

int tst;
void sigusr1_handler(){
if(tst==0){
signal(SIGUSR1,SIG_IGN);
tst=1;
}
else tst= 0;
}


int main(){
signal(SIGUSR1, sigusr1_handler);
tst=1;
while(1){}
return 0;
}

Answer

You do not.

What you can do, is have your signal handler decide whether to do something or not -- for example, whether to call an another function or not. This is not completely reliable, however, because standard POSIX signals like SIGUSR1 are not queued. If two or more signals are sent at (nearly) the same time, only one is actually delivered. POSIX realtime signals (SIGRTMIN+0 to SIGRTMAX-0 -- from the programmer's point of view, only the number and semantics differ) are queued, but even they can be dropped in some situations.

In all cases, remember that a signal handler function can only use async-signal safe functions. If you need to be able to use all functions, you should instead block the signals in all threads, and have a dedicated thread receive the signals using e.g. sigwaitinfo(). In this case, you don't have signal handler functions, but instead a dedicated thread that receives the signals, and is thus free to use any functions it wants.

If we reword the question into "How do I alternate the handling of a delivered signal in a single-threaded program?", the answer is simple: you use a volatile sig_atomic_t counter.

For alternating between two choices, do_something_odd() first:

#include <signal.h>

void my_signal_handler(int signum)
{
    static volatile sig_atomic_t  count = 0;

    switch (++count) {

    case 1:
        do_something_odd();
        break;

    default:
        count = 0;
        do_something_even();
        break;
    }
}

For alternating between three cases, you add more case statements as necessary:

#include <signal.h>

void my_signal_handler(int signum)
{
    static volatile sig_atomic_t  count = 0;

    switch (++count) {

    case 2:
        do_something_2_of_3();
        break;

    case 1:
        do_something_1_of_3();
        break;

    default:
        count = 0;
        do_something_3_of_3()           
        break;
    }
}

The above assumes you install the signal handler using sigaction() without SA_SIGINFO in .sa_flags.

The POSIX standard says you can do up to 128 cases (as sig_atomic_t is guaranteed to be able to represent values 0 to 127, inclusive) this way.

You can do larger sets using an unsigned int or unsigned long, but then you must NOT have SA_NODEFER in the .sa_flags, and if the same handler is used for multiple signals, the .sa_mask must have all the other signals handled by the same handler set. This ensures that the signal handler is not interrupted by another signal that is delivered to the same handler, and allows us to use non-atomic counter variable.

In a multithreaded program one must rely on compiler-provided atomic operations, either legacy __sync built-ins, or __atomic built-ins. They are not GCC-specific; at least Intel Compiler Collection and clang provide them also. For alternating between two options, our = __atomic_xor_fetch(&counter, 1, __ATOMIC_SEQ_CST); or our = __sync_xor_and_fetch(&counter, 1); can be used to obtain and flip a counter between 0 and 1 atomically. For a non-power-of-two number of options, a compare-and-swap loop is typically used.