How to Signal Handler to Survive After "Exec"

Is it possible to signal handler to survive after exec ?

No. From the man pages:

execve() does not return on success, and the text, data, bss, and stack of the calling process are overwritten by that of the program loaded. The program invoked inherits the calling process's PID, and any open file descriptors that are not set to close on exec. Signals pending on the calling process are cleared. Any signals set to be caught by the calling process are reset to their default behaviour. The SIGCHLD signal (when set to SIG_IGN) may or may not be reset to SIG_DFL.

In fact, if the signal handler were still active after the code had been replaced with some very different code, you could expect all sorts of mayhem when the signal occurred. The signal handler is, after all, just an address to call when something happens (discounting SIG_IGN and SIG_DFL for now). Who knows what piece of code would be at that address when you replace the entire text segment?

Can't catch signals after exec

Signal masks are inherited across exec, and SIGINT is blocked during the invocation of your signal handler, which calls execve. Thus your re-exec'd image is started with SIGINT blocked.

If you strace the process, you'll see that your signal call becomes something like:

3143  rt_sigaction(SIGINT, {0xabcd, [INT], SA_RESTORER|SA_RESTART, 0xabcd}, {SIG_DFL, [], 0}, 8) = 0
^^^^^
|
+--- SIGINT is blocked during handler!

sigaction will give you finer control over the signal handler, and is recommended over signal.

What happens after the execution of a handler function on the SIGCHLD signal in C?

As @ChrisDodd said, the problem was that a system call (fgets), was interrupted by the death of the background process and caused undefined behaviour.

Therefore, I solved my problem by adding the following flag to my handler:

sa.sa_flags = SA_RESTART;

As the documentation says it will:

Provide behavior compatible with BSD signal semantics by making certain system calls restartable across signals.

Thus no more issue with fgets.

What happens when a signal is received while already in a signal handler?

In your concrete example (the same signal being received), the signal is delivered after the signal handler has finished (so bullet point #2 is correct). Note, however, that you may "lose" signals.

The reason for that is that while a signal is being inside its handler, it is blocked. Blocked signals are set to pending, but not queued. The term "pending" means that the operating system remembers that there is a signal waiting to be delivered at the next opportunity, and "not queued" means that it does this by setting a flag somewhere, but not by keeping an exact record of how many signals have arrived.

Thus, you may receive 2 or 3 (or 10) more SIGCHLD while in your handler, but only see one (so in some cases, bullet point #1 can be correct, too).

Note that several flags that you can pass to sigaction can affect the default behaviour, such as SA_NODEFER (prevents blocking signal) and SA_NOCLDWAIT (may not generate signal at all on some systems).

Now of course, if you receive a different type of signal, there's no guarantee that it won't interrupt your handler. For that reason, one preferrably doesn't use non signal safe functions.

different ways to ignore a signal?


if I want to ignore SIGINT signal, then I just need to simply code as: signal(SIGINT, SIG_IGN);, is my understanding corrct?

Yes, but the docs for signal recommend using sigaction. signal's semantics vary by system, while sigaction's are more consistent.

does the compiler automacially inject this handler into the compiled code?

No. The compiler doesn't create an empty handler for SIG_IGN. It literally tells the OS to ignore the signal for the process. It's not a function pointer but a value signal treats specially.

since my own sigint_handler does nothing, it is pretty much like ignoring the SIGINT, so can I say this approach is fundamentally the same as signal(SIGINT, SIG_IGN);?

While both effectively ignore the signal, there are differences.

  • When using a signal handler, a blocking syscall might return prematurely with error EINTR to allow the signal handler to run. This won't happen with SIG_IGN.
  • SIG_IGN will survive exec, but a handler doesn't.
  • On some systems, the signals's disposition is reset to SIG_DFL when the signal handler is called, so your code would only ignore the first instance of the signal on such systems.
  • There are other differences, which may vary by platform.


Related Topics



Leave a reply



Submit