5

Currently I am having problems with my users complaining about the termination of my application. Under some (seemingly arbitrary) conditions and desktop environments the app is not terminated and settings are not saved on reboot. I asked in the relevant irc channels and most of the time I get told to handle signals correctly. I know of SIGINT for Ctrl-C in terminal and SIGTERM for the "normal" termination. But I got told that SIGHUP is important too. So my question is:

What signals do I have to handle to build a well behaved application?

1
  • 1
    You can setup your application to catch every signal, log it and then analyze why your application is terminated. The kernel usually provides more information with using sigaction instead of signal and analyzing siginfo_t structure with SA_SIGINFO flag set.
    – user140866
    May 19, 2016 at 11:51

2 Answers 2

2

https://en.wikipedia.org/wiki/Unix_signal

has a list of standard signals and their default actions. Any privileged-enough process can always send you any signal, but processses (or you, via processes) shouldn't do this.

You should only kill with and expect your processes to be killed with TERM and a bunch of signals that can be generated by your environment (the shell, the terminal driver), unless you know the target handles the special signal you want to send.

You can sort the basic signals by their actions.

The coredumping signals

ABRT TRAP SYS BUS FPE ILL SEGV   XFSZ XCPU QUIT

are either generated by you (TRAP, ABRT, SYS) using some specialized functions, or by you getting into hard error states (BUS, FPE,ILL, SEGV). QUIT is generated by a user at a terminal wanting a core dump (C+\).

You might want to keep these at their default dispositions.

Out of the terminating signals:

HUP INT PIPE TERM ALRM POLL PROF USR1 USR2

you should expect

HUP INT PIPE TERM

from your environment in various circumstances:

HUP -- when the terminal hungs up or you become a stopped orphaned process
INT -- when the user at the terminal interrupts you with C-c
PIPE -- when a PIPE or socket you write into closes, e.g. by
        exiting before you finished writing to it 
       (`yourprogram | (exit)` will give you a `PIPE` 
        if yourprogram attempts to write to its STDOUT) 
TERM -- when a process ends you a normal termination request

You should receive the rest of the terminating signals only if you set it up yourself.

KILL and STOP you can do nothing about.

You may wish to intercept the terminal generated stopping signals:

TTIN -- you read from the terminal and you're a backgrounded process
TTOU -- you write the terminal when you're a backgrounded process
        and the terminal is set to put you to sleep when you do
TSTP -- you're being put to sleep with `C-Z`

but at worst, these will just stop your process, without corrupting your state.

Unless you know you need to handle CHLD, CONT, or URG, you don't.

TL;DR:

Basically, I think HUP, INT, PIPE, and TERM should be handled (or ignored) if you usually want to do some pre-exit cleanup. The rest can be left alone, unless your program uses those signals, or unless you absolutely need some cleanup in all circumstances.

In the latter case, you can blankly block all unhandled signals, but beware that signal blocking masks get inherited across forks and execve calls and ignoring or blocking signals like ILL, if they arose from the run of your process and weren't sent to you by kill or sigqueue will give you undefined behavior.

If you want to get more into it, explore the manpages and the standards. Signals are quite a big topic on Unixes and handling them can get very tricky.

1

If you're going that way, you'll probably end up ignoring a good number of signals, simply because one of them is terminating your program. As suggested by siblynx in a comment, the best course of action would be to analyse these signals and understand where they're coming from. After all, there might be a good reason for your program getting killed.

Having said that, you can have a look at signals(7) to get a list of signals which may cause process termination :

SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGKILL, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, SIGUSR1 and SIGUSR2.

If your application spends some time attached to a terminal, handling SIGINT is a good idea (Ctrl+C is quite famous after all). SIGTERM is also a good idea because it remains the basic termination signal, and is used by the kernel (for instance, upon system shutdown). SIGQUIT is probably the "harsher" version of the two previous ones, since it causes a core dump. When you receive that one, your program should probably be expected to crash right away. Upon receiving any of the others, your program would be expected to "clean up its mess and leave" (save settings, terminate properly).

SIGILL, SIGFPE, SIGSEGV and SIGPIPE are signals related to program misbehaviour. Segmentation fault is probably the most recurrent situation, but in the end, all these cases can be treated as bugs, and I don't believe that catching the signal is the best way to recover from these situations.

  • Most of the time, compilers won't let SIGILL happen that easily (more here) so I think you may put that one aside.
  • Make sure you never divide by zero, and you'll avoid most SIGFPEs. Other than that, just make sure your program is good at maths and doesn't go crazy with the numbers.
  • SIGSEGV may be caused by several things (illegal memory access, illegal derefencing), and for that one I'd suggest using gdb to watch over your program as it executes, and trace back the nasty instruction using gdb's stack.
  • Finally, SIGPIPE is mostly related to data streams (reading files, connections, ...). If any of your program I/O streams is likely to be terminated as it executes, you should make sure your program checks its descriptors before using them, and handles all I/O events occuring on it (when using select or poll for instance). gdb might be useful here as well.

SIGABRT, SIGALRM, SIGUSR1 and SIGUSR2 are basically user signals. If you're receiving them, chances are you actually made that happen.

This leaves us with SIGHUP and SIGKILL, which may not be caught.

  • SIGHUP will occur for instance if you start the process from a terminal, and close that terminal afterwards without detaching the process (disown, or using nohup). If your program is started from terminal and runs as a deamon, don't forget to have it detach itself from the shell, for instance by double-forking. This particular signal is less relevant if your program can start from a GUI, or at boot time, where the controlling terminal is less likely to be "closed".

  • SIGKILL as you know, is the almighty of signals. It's the kernel's way of saying "sh*t up!". If you're receiving that, then you should focus your attention on whatever's emitting the signal, rather than on how to handle it in your program.

In the end, analysing the signal and using debugging tools is probably the simplest approach to these problems. If a signal is triggered by the kernel when your program misbehaves, gdb should help you locate the problem. In that case, forget about the signal, and focus on the bug that it's telling you about.

If the signal comes from "outside", finding the emitter is probably the best course of action (if you really can't find it, tracing the kill(2) system call might be a good last resort). Then you can decide whether you want to ignore it (SIG_IGN), or take it into account. Silencing the emitting program can also be a decent alternative...

3
  • Cool answer, just curious why you censored "shut up"
    – schaiba
    Aug 30, 2016 at 11:55
  • 1
    @schaiba Because I'm an overly nice and polite person... Obviously... Kind of... Most of the time... Okay, maybe just sometimes... Aaaaah! You got me, dang it! Aug 30, 2016 at 12:00
  • I wouldn't want to hear "shut up" from you, seeing that you'd probably follow that up by drawing up a murder weapon. :D Anyway, SIGPIPE isn't necessarily a sign of program misbehavior. Piping into head and similar scenarios rely on SIGPIPE to silently kill the producer when enough data has been obtained from it. Aug 30, 2016 at 12:56

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .