Mailing List Archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [tlug] UNIX signals

>>>>> "Jim" == Jim  <> writes:

    >> ... man pages ... Except that they all say that HUP is useful,
    >> I don't find the documentation very meaningful to me when
    >> trying to understand the difference between signals. Should
    >> that be filed as a bug?

    Jim> No. Explaining signals is too much for a man page.

The man page should explain what non-default behavior will occur when
a signal is received.  Any man page for a daemon that understands
SIGHUP that doesn't include text such as "Sending a HUP signal to sshd
causes it to reload its configuration file.  This will not affect
existing connections." is broken.

The default behaviors are listed in signal(7), and are simply
functions provided by the operating system or a library.  signal(7)
also gives brief descriptions of the purpose of each signal.  See also
signal(2) and the various functions listed under SEE ALSO in that man

    Jim> You need a big long explanation to _understand_ signals. 

It's really not so hard.  Here's the Cliff's Notes version:

Signals are closely related to interrupts.  In each case the program
defines a function, typically _not_ called in the _normal_ execution
of the program, called a "handler".  It then registers the handler
with the operating system, eg, in a table which is part of the process
metadata the OS uses to manage the process.

If the operating system is told to deliver a signal (eg, by a hardware
interrupt or by the kill(1) utility), it looks up the function in the
table, and calls it, thus transferring control to the program
receiving the signal.  Although the main program usually does not call
the signal handler, the signal handler normally _does_ update data
that the program can see.  In some cases the signal handler may call
routines in the main program, but this is usually very unwise.  The
signal handler then returns to the operating system (usually, although
it _could_ longjmp(3)---but that's almost inevitably gonna leave a
smoking hole in somebody's foot).  The "message" from the handler is
then picked up by the main program in its regularly scheduled time

What _is_ hard is using them correctly, because most nontrivial uses
involve fork() and concurrency.  You need to understand those a little
bit to understand why signals, which look like such a roundabout way
of doing things, are actually necessary.  But what signals _do_ is
quite simple.

Then there's a horde of details about under what circumstances a
signal is going to be received.  That's what determines what code
needs to go into the handler.

    >> Sending the HUP signal to the parent causes it to kill off its
    >> children like in TERM but the parent doesn't exit. It re-reads
    >> its configuration files, and re-opens any log files. Then it
    >> spawns a new set of children and continues serving hits.

    Jim> Each program can handle HUP signals however it wants. What
    Jim> you quoted is how _Apache_ handles signals. Other programs
    Jim> likely handle signals differently.

For example, sshd does not kill its children.  sshd doesn't want to
forcibly kill its children, that would disconnect the sessions.  And
since each process manages a single session, there's no point in
telling them to die when they're done, because they're going to do
that, anyway.  Apache can kill its children because HTTP doesn't
define sessions (keepalive is an optimization, not a promise), so a
child can be told to die after finishing the current request.

However, re-reading configuration files and re-opening log files are
very common tasks executed upon receiving a HUP.  (Why re-open log
files?  Because if the log file is open, an attempt to save space by
rotating and compressing or deleting it will fail at best and possibly
corrupt it, because POSIX semantics say that you don't recover a
file's storage until there are no open fd's using it.  This is quite
tricky to do right, though.)

How does a signal work in a program?  Very simple.

/* server.c */

/* tell the compiler that this variable could change even if we
   never change it---else the "while (!caught_sigterm)" might
   get optimized by a Sufficiently Smart Compiler to "while (1)" */
volatile int caught_sigterm = 0;

int main (int argc, char *argv[])
  initialize (argc, argv);

  /* do forking here */

  if (i_am_a_child ())
      /* conceptually, put sigterm_handler's address at
         process_signal_table[SIGTERM]; don't call it here!  */
      signal (SIGTERM, &sigterm_handler);

      /* main loop */
      while (!caught_sigterm)
          /* we don't touch caught_sigterm here
             how can it get set?  betcha already guessed! */
          handle_one_request ();

      finalize ();
      exit (caught_sigterm ? 0 : -1);
      while (1)
          allocate_requests_to_children ();

/* handle_sigterm.c */

extern volatile int caught_sigterm; /* a shared (global) variable
                                       set by the signal handler
                                       read by the main program   */

/* this handler is called by the OS, not by the program */
void sigterm_handler (int ignored)
  caught_sigterm = 1;

HTH ;-)

School of Systems and Information Engineering
University of Tsukuba                    Tennodai 1-1-1 Tsukuba 305-8573 JAPAN
               Ask not how you can "do" free software business;
              ask what your business can "do for" free software.

Home | Main Index | Thread Index

Home Page Mailing List Linux and Japan TLUG Members Links