Skip to content
Advertisement

Signals – SIGUSR1 class exercise

I got this as a class exercise. I had to analyze the output without running it and I figured that

the output should be num=4.

But actually the output of this code after running it is:

num=8

I’ll be glad to know what am I missing?

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <termios.h>

int num = 0;
int y = 1;
void signal_hand(int sig) {
    signal(SIGUSR1, signal_hand);
    num = num + 1;
    y = y * f();
}

int f() {
    if (num == 2) {
        return y;
    }
    pid_t pid = 0;
    pid = getpid();
    kill(pid, SIGUSR1);
    return 2 * y;
}

int main(void) {
    int x = 0;
    signal(SIGUSR1, signal_hand);
    x = f();
    printf("num = %dn", x);
    return 1;

}

Advertisement

Answer

This is an interesting question.

So what’s happening is that a signal handler is created which calls into f(), that kills the program that’s running. But the signal handler traps the same signal it issues.

Each time the program is killed, it calls the signal handler, and tries to kill the program again. It’s recursive suicide. This program really wants to die.

This would be a never ending loop, except that it counts the number of times that it’s trapped its signal and when it’s trapped 2 signals, and the count is num=2, it returns from f() without killing itself a 3rd time.

So to break down how it gets to 8…

It calls f() 3 times in total. The first time, it’s called, it kills itself. It calls itself 2 more times through the signal handler.

This is difficult to explain easily.

  1. The outer layer of the f() call (which is the function that started the kill recursion to begin with), this is the last f() to finish, and it takes the result of y=4 from the 3) [below] and multiplies it by 2, to get 8. Which it returns.

This called into the following, which run sequentially:

  1. The inner layer of the f() call (1 signal trap in), returns 2, which later gets multiplied by the y=1 in the handler to get y=2. This function completes before the signal handler receives the 2nd signal (below) and is the first f() to finish.
  2. After 2) has finished. Another inner layer of the f() call (2 signal traps in) returns 2, the value it started with. It later gets multiplied by y which is 2 in the signal handler, to get 4. Num=2 at this point. This is the second f() to finish.

Now those “last” two steps, i don’t think are meant to run sequentially, i think that if there was more processing in f(), what we’d see is that the signal would be trapped in trap 2 while trap 1 was still running. But i think what we are seeing here is a timing issue, the trap 1 f() completes right away before the signal has time to be issued, go through the operating system and return to the application to be handled by the signal handler.

It’s an evil question. I think the point of this question is that when you trap a signal in C, the program isn’t dead, it’s still very much alive. And it can be killed multiple times too, the signal handling has complete control over what the program does and how it’s shut down.

For an example of why this is useful, imagine a linux database that is habitually resident in RAM via SHM or something. If you want to shut down the PC or something the operating system sends a kill signal to programs. Most programs ignore this, and just die, but a database may not be able to just die because it could lose data that isn’t yet written to disk, so in that situation it may want to trap the kill signal, then flush the data to disk, then exit.

*Note I said kill and die in this, but i’ve just noticed it’s a user signal, but the mechanics are similar with kill signals too.

User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement