All about Linux signals

Handling SIGCHLD


If you create new processes in your program and don't really want to wait until they exit, and possibly their exist status doesn't matter, just want to cleanup zombie processes you can create a SIGCHLD handler that does just that and forget about process you've created. This handler can look like this one:

Fragment of example: SIGCHLD handler

  1. static void sigchld_hdl (int sig)
  2. {
  3. /* Wait for all dead processes.
  4. * We use a non-blocking call to be sure this signal handler will not
  5. * block if a child was cleaned up in another part of the program. */
  6. while (waitpid(-1, NULL, WNOHANG) > 0) {
  7. }
  8. }

This way whenever a child exits it will be cleaned-up but information which process was that, why it exited and its exit status is forgotten. You could make the handler more intelligent but remember to not use any function that is not listed as signal-safe.

You must remember that if you make child processes SIGCHLD must have a handler. The behavior of ignoring this signal is undefined, so at least a handler that doesn't do anything is required.

Handling SIGBUS


The SIGBUS signal is sent to the process when you access mapped memory (with mmap(2)) that doesn't correspond to a file. A common example is that the file you've mapped was later truncated (possible by another program) and you read past it's current end. Accessing files this way doesn't require any system function that could return an error, you just read from memory like if it was on the heap or stack. This is a really bad situation when you don't want your program to terminate after a file read error. Unfortunately handling SIGBUS isn't simple or clean, but it's possible. If you want to continue running your program you have to use longjmp(3). It's something like goto but worse! We have to jump to some other place in the program that the mmap()ed memory is not accessed if we receive SIGBUS. If you place an empty handler for this signal, in case of read error the program will be interrupted, signal handler executed and the control returns to the same place that caused the error. So we need to jump into another place from the signal handler. This sounds low-level, but it's possible using standard POSIX functions.

See the example: SIGBUS handling

You must keep in mind the list of signal-safe functions: In this example we never actually return from the signal handler. The stack is cleaned up, but program is restarted in completely different place, so if you've had, for example, a mutex locked during the operation like:

  1. pthread_mutex_lock (&m);
  2. for (l = 0; l < 1000; l++)
  3. if (mem[l] == 'd') // BANG! SIGBUS here!
  4. j++;
  5. pthread_mutex_unlock (&m);

After longjmp(3) the mutex is still held although in every other situation the mutex is released.

So handling SIGBUS is possible but very tricky and can introduce bugs that are very hard to debug. The program's code also becomes ugly.

Handling SIGSEGV


Handling the SIGSEGV (segmentation fault) signal is also possible. In most cases returning from the signal handler makes no sense since the program will be restarted from the instruction that caused segmentation fault. So if you have no solution on how to fix the state of the program to let it continue running properly at the same moment it crashed, you must end the program. One example of when you may restart the program is when you have memory obtained using mmap(2) that is read-only, you may check if the signal handler that the cause of segmentation fault was writing to this memory (using data from siginfo_t) and use mprotect(2) to change the protection of this memory. How practical is it? I don't know.

Exhausting stack space is one of the causes of segmentation fault. In this case running a signal handler is not possible because it requires space on the stack. To allow handling SIGSEGV in such condition the sigaltstack(2) function exists that sets alternative stack to be used by signal handlers.

Handling SIGABRT


When handling this signal you should keep in mind how the abort(3) function works: it rises the signal twice, but the second time the SIGABRT handler is restored to the default state, so the program terminates even if you have a handler defined. So you actually have a chance to do something in case of abort(3) before the program termination. It's possible to not terminate the program by not exiting from the signal handler and using longjmp(3) instead as described earlier.

Hi, typo jerk here

Hi, typo jerk here again: page3: Moreover, it lack's features -> lacks why the signal was send -> sent page5: signals like SIGPIPE, SIGUSR1, SIGUSR1 -> SIGUSR2 signal i exits -> it This program read from it's -> reads, its Additionally when SIGUSR1 -> Additionally, when [missing comma] I hope you really, truly don't consider this as some kind of personal attack.

Thanks. I don't consider it

Thanks. I don't consider it as an attack :) Some of them are just caused by the fact that English is not my native language.

I was looking for linux

I was looking for linux programming tutorial and i found this blog . keep good work.

Great write-up

Great write-up; thanks for the information.

Signals - That's not everything

I wouls suggest the reading of "Advanced Programming in the Unix Environment". It does not address signals with threads, but it is the most extensive explanation of signal handling. It treats also long jumps to remove races in signal handling.