3

I do not understand the following code:

pid_t pid;
int counter = 0;
void handler1(int sig) {
  counter++;
  printf("counter = %d\n", counter);
  fflush(stdout);
  kill(pid, SIGUSR1);
}
void handler2(int sig) {
  counter += 3;
  printf("counter = %d\n", counter);
  exit(0);
}
int main() {
  signal(SIGUSR1, handler1);
  if ((pid = fork()) == 0) {
    signal(SIGUSR1, handler2);
    kill(getppid(), SIGUSR1);
    while (1) {
    };
  } else {
    pid_t p;
    int status;
    if ((p = wait(&status)) > 0) {
      counter += 2;
      printf("counter = %d\n", counter);
    }
  }
}

How can the child process actually kill the parent process while the parent process waits until its child process terminates?

tux3
  • 7,171
  • 6
  • 39
  • 51
Makara
  • 127
  • 3
  • 11
  • Use case: Parent forks off to child. Child starts up by reading config etc. and only after successful startup sends SIGUSR1 to parent. Until then, the parent gets notified if the Child dies while starting (as it is in ``wait()``) and after the handling of SIGUSR1 the Parent terminates with the assumption that the Child will be well. – Jonas Schäfer Jun 13 '15 at 15:56
  • Note [How to avoid using `printf()` in a signal handler](http://stackoverflow.com/questions/16891019/). – Jonathan Leffler Jun 13 '15 at 16:38

1 Answers1

4

Note first that the kill() syscall is somewhat misnamed, as it is a general-purpose function for sending a signal to a process. Killing the target process is only one of several possible results.

More generally, however, signal handling is is an interrupt mechanism. A great many library functions, including wait() can be interrupted by receipt of a signal. Control passes to the registered handler for that signal, if any, or else the default action is performed. Afterward, the function that was interrupted returns, indicating an error via its return code and / or by setting errno.

Edited to add: Additionally, your own code may be interrupted by receipt of a signal. Provided that the effect is not to terminate the process, execution will resume at the point where it left off if the signal handler exits normally, or at some other point if the handler exits by calling longjmp().

Also, signal delivery is an asynchronous mechanism handled by the kernel. A process can be blocked or otherwise occupied and still receive signals; indeed, that's a big part of the point of them. Each process has a queue of pending signals awaiting handling; for most processes, this queue is empty almost all the time, but unless you explicitly block signals, it is never safe for a process to assume that it will not receive any (and certain signals cannot be blocked anyway).

In your particular code, the main process starts by setting function1() to be its handler for signal USR1. It then forks, and the child process sets function function2() as its handler for signal USR1 (not thereby affecting the parent), sends signal USR1 to the parent, and goes into an infinite loop.

Meanwhile, the parent initiates a wait() for its child process. This will be interrupted by receipt of the signal. The wait will end after the signal is received and the registered handler runs, with wait() returning -1 and setting errno to EINTR. One of the actions the handler performs is to send a SIGUSR1 to the child.

Upon receiving the signal, the child's normal flow of execution is interrupted to run its handler, function2(). This updates the child's copy of variable counter, prints its value, and exit()s.

End result:

The main process prints

counter = 1

then exits. The child process prints

counter = 3

then exits.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • So you mean even the function kill cannot kill the parent successfully. It still send out the signal. – Makara Jun 13 '15 at 15:21
  • @Makara, no, I don't mean that at all. If the child sent a `SIGKILL` or a `SIGTERM` then it could cause its parent to terminate; that's just not what it does. – John Bollinger Jun 13 '15 at 15:25
  • thank for pointing that out but I am a little bit more confuse about the result why in my computer they print the result out like counter = 1\n counter = 3\n counter = 3\n – Makara Jun 13 '15 at 15:32
  • @Jonh, dont the kill function inside the handler1 send the signal back to child process that trigger the signal function. Within that handler, program call function exit to terminate the child process, so I think that why it is not in the infinite loop. What do you think? – Makara Jun 13 '15 at 15:39
  • @Makara, yes, the parent process's signal handler sends a signal to the child. I first erroneously claimed that it would remain pending while the child spun in its infinite loop (not blocking the parent, however), but in fact the child will be interrupted, run its handler, then resume the loop. Answer updated. – John Bollinger Jun 13 '15 at 15:47
  • @Jonh, I try to run the code in my computer. It turns out different things. It terminate completely. I think the exit function here might terminate the child process completely, doesn't it. Sorry to ask more but I am a bit confuse here. – Makara Jun 13 '15 at 15:50
  • @Makara, Ah, yes. I missed the `exit()` at the end of `function2()`. That certainly will cause the child process to terminate. – John Bollinger Jun 13 '15 at 15:52
  • @Jonh, exit function is terminate the entire process right, I mean the process that call exit function. It is not just informing the system that the function is complete right? – Makara Jun 13 '15 at 15:55
  • @Makara, yes, the `exit()` function causes the process in which it is called to terminate. It has no (direct) effect on other processes, however. In particular, the child is a separate process from the parent, so when the child exits, it does not affect the parent. (Technically, when the child terminates for any reason, its parent is sent a `SIGCHLD`. This happens when the program exits by returning from `main()`, too, and anyway the default disposition for that signal is to ignore it). – John Bollinger Jun 13 '15 at 15:58
  • Note that POSIX defines the [`sigsetjmp()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigsetjmp.html) and [`siglongjmp()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/siglongjmp.html) functions and arguably they should be used in preference to [`setjmp()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/setjmp.html) and [`longjmp()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/longjmp.html) on systems that support them (POSIX systems) when the long jumping will be done from signal handlers. – Jonathan Leffler Jun 13 '15 at 17:22