10

I have an application which is communicating with workers via signals (particullary SIGUSR1/SIGUSR2/SIGSTOP).

Can I trust that whatever happens every signal will be delivered and processed by handler?

What happens if signals are sent quicklier than is't possible for application to handle them (eg. due to high host load at the moment)?

9

Aside from the "too many signals" problem, signals can be explicitly ignored. From man 2 signal:

If the signal signum is delivered to the process, then one of the
following happens:    
  *  If the disposition is set to SIG_IGN, then the signal is ignored.

Signals can also be blocked. From man 7 signal;

A signal may be blocked, which means that it will not be delivered
until it is later unblocked.  Between the time when it is generated
and when it is delivered a signal is said to be pending.

Both blocked and ignored sets of signals are inherited by child processes, so it may happen that the parent process of your application ignored or blocked one of these signals.

What happens when multiple signals are delivered before the process has finished handling previous ones? That depends on the OS. The signal(2) manpage linked above discusses it:

  • System V would reset the signal disposition to the default. Worse, rapid delivery of multiple signals would result in recursive (?) calls.
  • BSD would automatically block the signal until the handler is done.
  • On Linux, this depends on the compilation flags set for GNU libc, but I'd expect the BSD behaviour.
1
  • 4
    Linux's man page for signal(2) suggests emphatically that you avoid this confusion by using sigaction(2) instead. Jan 28 '15 at 6:07
8

You can't trust that every signal sent will be delivered. For example, the linux kernel "coalesces" SIGCHLD if a process takes a long time in handling SIGCHLD from an exited child process.

To answer another part of your question, signals get "queued" inside the kernel if a number of different signals arrive in too short of an interval.

You should use sigaction() to set up the signal handler with the sa_sigaction member of siginfo_t, setting the sa_mask member of the siginfo_t argument carefully. I think this means masking off all of the "asynch" signals at least. According to the man page for Linux sigaction(), you'll also mask off the signal being handled. I think you should set the sa_flags member to SA_SIGINFO, but I can't remember why I have this superstition. I believe this will get your process a signal handler that stays set with no race conditions, and one that doesn't get interrupted by most other signals.

Write your signal handler function very, very carefully. Basically just set a global variable to indicate that a signal got caught, and have the rest of the process deal with the desired action for that signal. Signals will be masked for the least amount of time that way.

Also, you'll want to test your signal handling code very thoroughly. Put it in a small test process and send as many SIGUSR1 and SIGUSR2 signals as possible, maybe from 2 or 3 special purpose signal-sending programs. Mix in some other signals as well, after you're confidant that your code can handle SIGUSR1 and SIGUSR2 swiftly and correctly. Prepare yourself for difficult debugging.

If you're using linux and only linux, you might think about using signalfd() to create a file descriptor that you can select() or poll to receive those signals. Using signalfd() might make debugging easier.

3
  • 3
    It's not just SIGCLD that gets coalesced: all signals are potentially coalesced if they're delivered before they can be processed. Jan 28 '15 at 0:48
  • Is there any measure of how long is "too long" for SIGCHLD signals? I'm experiencing this behaviour in my program right now, and my signal handler doesn't take more than ~100ms I would reckon.
    – xrisk
    Sep 16 '19 at 18:22
  • @Rishav - to my knowledge there's no way to find out what "too long" is. I would expect that overall system load is important. That is, what other processes and the kernel are doing would affect "how long" between signals for them to get coalesced. Not a helpful answer, I think. Sep 17 '19 at 19:36
6

A signal is guaranteed to be delivered, in the sense that if a process successfully calls kill, then the target will receive the signal. This is asynchronous: the sender has no way to know when the signal is received or processed. However, this does not guarantee that the signal will be delivered. The target could die before it can process the signal. If the target is ignoring the signal at the time it is delivered, the signal will have no effect. If the target receives multiple instances of the same signal number before it can process them, the signals may (and usually are) merged: if you send the same signal twice to a process, you can't know whether the process will receive the signal once or twice. Signals are mostly designed to kill a process or as a way to make a process pay attention, they aren't designed for communication as such.

If you need reliable delivery then you need a different communication mechanism. There are two main communication mechanisms between processes: a pipe allows unidirectional communication; a socket allows bidirectional communication and multiple connections to the same server. If you need the target to process as many notifications as you send it, send bytes over a pipe.

1
  • 4
    Did you mean to write "A signal is guaranteed to be delivered" since you went on to describe some of the ways in which the signal would not be delivered (i.e. process died before it was received, or signals were coalesced)?
    – Johnny
    Jan 28 '15 at 2:32
2

The kernel is free to coalesce standard signals if more than one is delivered whilst blocked. Real-time signals on the other hand are not similarly handicapped.

From signal(7) manual page:

Real-time signals are distinguished by the following:

  1. Multiple instances of real-time signals can be queued. By contrast, if multiple instances of a standard signal are delivered while that signal is currently blocked, then only one instance is queued.

Try using a signal with a number in the range SIGRTMIN to SIGRTMAX.

1
  • There is a limit for real-time signals, but it's quite high. A signal will be dropped if the number of outstanding pending signals sent by the user exceeds RLIMIT_SIGPENDING. ulimit -i shows this value as 63432 on Ubuntu 18.04.
    – bain
    Nov 18 '19 at 16:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.