home *** CD-ROM | disk | FTP | other *** search
/ SuperHack / SuperHack CD.bin / CODING / MISC / JFSIGNAL.ZIP / JFSIGNAL.TXT
Encoding:
Text File  |  1996-03-16  |  19.1 KB  |  568 lines

  1.  
  2.                         UNIX SIGNALS AND PROCESS GROUPS
  3.                                        
  4.    
  5.    
  6.    Jim Frost
  7.    Software Tool & Die
  8.    Copyright (c) 1994 Jim Frost
  9.    All Rights Reserved
  10.    Last changed August 17, 1994
  11.    
  12.      _________________________________________________________________
  13.    
  14. Contents
  15.  
  16.      * Abstract
  17.      * UNIX Signal Handling Environments
  18.           + The Traditional System-V Signal Environment
  19.           + The BSD 4.X Signal Environment
  20.           + The System-V.3 Reliable Signal Environment
  21.           + The POSIX Signal Environment
  22.      * Process Groups and Tty Management
  23.           + The Berkeley Approach
  24.           + The POSIX Approach
  25.           + Differences between POSIX and BSD Process Group Management
  26.             
  27.    
  28.      _________________________________________________________________
  29.    
  30. Abstract 
  31.  
  32.    
  33.    
  34.    This document describes the four common UNIX signalling environments,
  35.    including their interfaces, and contrasts each. In addition, it
  36.    describes the concepts and interfaces behind both BSD and POSIX
  37.    process groups.
  38.    
  39. UNIX Signal Handling Environments 
  40.  
  41.    
  42.    
  43.    There are four common signal-handling environments today. They are
  44.    BSD, System-V unreliable, System-V reliable, and POSIX. This section
  45.    discusses each and common variations where appropriate.
  46.    
  47.   THE TRADITIONAL SYSTEM-V SIGNAL ENVIRONMENT 
  48.   
  49.    
  50.    
  51.    Traditional System-V inherited its signal-handling environment from V7
  52.    research UNIX. This environment has three major limitations:
  53.    
  54.      * Recursive signal handling is always allowed.
  55.      * Signal handlers are reset to SIG_DFL prior to being called.
  56.      * System calls interrupt when a signal is delivered.
  57.        
  58.    
  59.    
  60.    These limitations cause "unreliable" signal behavior: Since signals
  61.    can be delivered recursively and the signal handler must manually
  62.    reset the signal handler from SIG_DFL, there is a window during which
  63.    the default signal handler can be called if another signal of the same
  64.    type arrives. In many cases the default action is to ignore the
  65.    signal, causing the signal to be lost. In the worst case the default
  66.    action is to terminate the process.
  67.    
  68.    Additionally any signal can interrupt a system call. The window for
  69.    interrupting a system call is fairly small for most system calls (and
  70.    impossible for some) but large for some common "slow" system calls
  71.    such as read() or write(). Few applications attempt to restart system
  72.    calls that have been interrupted by a signal. This results in even
  73.    more unreliability.
  74.    
  75.    The interface to the traditional signal handling environment is:
  76.    
  77.    
  78.           
  79.    int (*signal)(int signal_number, int (*function)(int));
  80.           
  81.           
  82.           Change the signal handler for the indicated signal.
  83.           Signal_number indicates which signal, function is the new
  84.           handler.
  85.           
  86.    int pause(void);
  87.           
  88.           
  89.           Wait for a signal to arrive.
  90.           
  91.    
  92.    
  93.    Two standard signal handlers exist:
  94.    
  95.    
  96.           
  97.    SIG_DFL
  98.           Take the default action for this signal.
  99.           
  100.    SIG_IGN
  101.           Ignore this signal.
  102.           
  103.   THE BSD 4.X SIGNAL ENVIRONMENT 
  104.   
  105.    
  106.    
  107.    Because of the problems inherent in V7 signal handling, the BSD
  108.    developers modified the signal-handling semantics somewhat:
  109.    
  110.      * Signals are blocked for the duration of a signal handler (i.e.
  111.        recursive signals are not normally allowed).
  112.      * A "signal mask" can be set to block most signals during critical
  113.        regions.
  114.      * Signal handlers normally remain installed during and after signal
  115.        delivery.
  116.      * A separate signal handler stack can be used if desired.
  117.      * Most system calls are restarted following delivery of a signal.
  118.        
  119.    
  120.    
  121.    This worked around the "unreliable" semantics of traditional System-V
  122.    signal handling and added critical-section protection at the same
  123.    time. Both are essential for reliable applications.
  124.    
  125.    The BSD interface includes the traditional System-V interface (using
  126.    the new reliable semantics) but adds the following:
  127.    
  128.    
  129.           
  130.    int sigvec(int signal_number, struct sigvec *new_sigvec, struct sigvec
  131.           *old_sigved);
  132.           
  133.           
  134.           Change or query signal handlers. Signal_number indicates which
  135.           signal. New_sigvec defines the handler environment or may be
  136.           null if you don't want to change the handler. If non-null,
  137.           old_sigvec is filled with the current handler information.
  138.           
  139.    int sigstack(char *stack);
  140.           
  141.           
  142.           Change the stack which will be used during signal delivery.
  143.           
  144.    int siginterrupt(int signal_number, int interrupt);
  145.           
  146.           
  147.           Alter the SV_INTERRUPT property of a signal handler. If
  148.           interrupt is zero, system calls will be restarted after
  149.           signal delivery. If it is non-zero they will return EINTR.
  150.           
  151.    int sigsetmask(int new_mask);
  152.           
  153.           
  154.           Change the set of masked/held signals. New_mask is a bit
  155.           pattern describing the new set of signals. The old signal mask
  156.           is returned.
  157.           
  158.    int sigblock(int signal_mask);
  159.           
  160.           
  161.           Add a new set of signals to the current signal mask.
  162.           
  163.    int sigpause(int signal_mask);
  164.           
  165.           
  166.           Wait for a signal using the given signal mask.
  167.           
  168.    
  169.    
  170.    The sigvec structure defines the signal-handling semantics:
  171.    
  172.  
  173.         struct sigvec {
  174.           int (*sv_handler)(void); /* signal handler */
  175.           int sv_mask; /* signals to mask during signal delivery */
  176.           int sv_flags; /* signal delivery options */
  177.         };
  178.  
  179.    
  180.    
  181.    The sv_flags field describes the options you could use to alter signal
  182.    handling. Standard options are:
  183.    
  184.    
  185.           
  186.    SV_ONSTACK
  187.           Use a special stack rather than the normal hardware stack. The
  188.           stack to use must be specified using sigstack().
  189.           
  190.    SV_RESETHAND
  191.           Use V7-style "reset to default handler" signal handling
  192.           semantics.
  193.           
  194.    SV_INTERRUPT
  195.           Allow system calls to be interrupted by signal delivery.
  196.           
  197.    
  198.    
  199.    Not all of these options are supported by "BSD-compatible" systems and
  200.    SV_RESETHAND was not supported until BSD 4.3.
  201.    
  202.    The same set of standard signal handlers is used as in V7 and
  203.    traditional System-V.
  204.    
  205.   THE SYSTEM-V.3 RELIABLE SIGNAL ENVIRONMENT 
  206.   
  207.    
  208.    
  209.    Because of the problems caused by unreliable signals AT&T added a new
  210.    interface to give reliable signal-handling semantics to System-V at
  211.    release 3 (SVR3). Unfortunately this interface differs from -- and is
  212.    less flexible than -- the BSD interface. It is unclear why AT&T picked
  213.    a conflicting interface given that the BSD interface easily predated
  214.    the newer System-V.
  215.    
  216.    The System-V approach gives a signal environment very similar to that
  217.    of BSD, except that a call to the sigset() function clears the signal
  218.    mask, causing it to be impossible to establish a critical section
  219.    during which a signal handler can be changed.
  220.    
  221.    The interface is:
  222.    
  223.    
  224.           
  225.    int (*sigset)(int signal_number, int (*function)(int));
  226.           
  227.           
  228.           Change the signal handler for the indicated signal.
  229.           
  230.    int sighold(int signal_number);
  231.           
  232.           
  233.           Hold/mask a signal.
  234.           
  235.    int sigrelse(int signal_number);
  236.           
  237.           
  238.           Release/unmask a held signal.
  239.           
  240.    int sigignore(int signal_number);
  241.           
  242.           
  243.           Ignore a signal.
  244.           
  245.    int sigpause(int signal_number);
  246.           
  247.           
  248.           Pause for a signal, unmasking the given signal.
  249.           
  250.    
  251.    
  252.    Note that sigpause() conflicts with the BSD function of the same name,
  253.    and that sigset() has an additional standard signal handler, SIG_HOLD,
  254.    which is used to block a signal temporarily.
  255.    
  256.    The most important limitation of the this signal interface is the
  257.    inability to atomically unblock and wait for more than one signal at a
  258.    time.
  259.    
  260.   THE POSIX SIGNAL ENVIRONMENT 
  261.   
  262.    
  263.    
  264.    To make certain that no one could write an easily portable
  265.    application, the POSIX committee added yet another signal handling
  266.    environment which is supposed to be a superset of BSD and both
  267.    System-V environments.
  268.    
  269.    Depending on the particular vendor, the POSIX approach can usually be
  270.    used to emulate all of the environments discussed here, and appears to
  271.    be derived from the BSD sigvec() interface. Few vendors actually
  272.    implement all of the options, however, and the POSIX committee did not
  273.    standardize the common options. Thus few implementations of the POSIX
  274.    signal handling environment are identical.
  275.    
  276.    The biggest advantage in using the POSIX interface is the ability to
  277.    deal with more signals than can fit in an integer -- more than the 32
  278.    used in BSD.
  279.    
  280.    The POSIX interface is:
  281.    
  282.    
  283.           
  284.    int sigaction(int signal_number, struct sigaction *new_handler, struct
  285.           sigaction *old_handler);
  286.           
  287.           
  288.           Set or query the signal handling environment of a given signal.
  289.           
  290.           
  291.    int sigprocmask(int how, sigset_t *new_set, sigset_t *old_set);
  292.           
  293.           
  294.           Set or query the signal mask. If new_set is null, no change is
  295.           made. If old_set is null, nothing is returned.
  296.           
  297.    int sigemptyset(sigset_t *set);
  298.           
  299.           
  300.           Clear a signal mask.
  301.           
  302.    int sigfillset(sigset_t *set);
  303.           
  304.           
  305.           Fill (add all possible signals to) a signal mask.
  306.           
  307.    int sigaddset(sigset_t *set, int signal_number);
  308.           
  309.           
  310.           Add a signal to a signal mask.
  311.           
  312.    int sigdelset(sigset_t *set, int signal_number);
  313.           
  314.           
  315.           Remove a signal from a signal mask.
  316.           
  317.    int sigismember(sigset_t *set, int signal_number);
  318.           
  319.           
  320.           See if a signal is a member of a signal mask.
  321.           
  322.    int sigpending(sigset_t *set);
  323.           
  324.           
  325.           Return the set of signals that have been delivered but which
  326.           were blocked.
  327.           
  328.    
  329.    
  330.    The sigaction structure describes the signal handling environment:
  331.    
  332.  
  333.      struct sigaction {
  334.        void (*sa_handler)(int);
  335.        sigset_t sa_mask; /* new signal mask */
  336.        int sa_flags;     /* options */
  337.      };
  338.  
  339.    
  340.    
  341.    The same default signal handlers are used in POSIX as in V7.
  342.    
  343.    Common options for sigaction() include:
  344.    
  345.    
  346.           
  347.    SA_OLDSTYLE
  348.           
  349.    SA_RESETHAND
  350.           V7 unreliable signal semantics.
  351.           
  352.    SA_INTERRUPT
  353.           Allow system calls to be interrupted by signal delivery.
  354.           
  355.    SA_RESTART
  356.           Allow system calls to be restarted after signal delivery.
  357.           
  358.    SA_ONSTACK
  359.           Use a special stack during signal handling.
  360.           
  361.    SA_NOCLDSTOP
  362.           Disable SIGCLD/SIGCHLD for stopped (versus terminated)
  363.           processes.
  364.           
  365.    
  366.    
  367.    Only SA_NOCLDSTOP is required by the POSIX specification. Other
  368.    options are often missing or have different names.
  369.    
  370. Process Groups and Tty Management 
  371.  
  372.    
  373.    
  374.    One of the areas least-understood by most UNIX programmers is
  375.    process-group management, a topic that is inseparable from
  376.    signal-handling.
  377.    
  378.    To understand why process-groups exist, think back to the world before
  379.    windowing systems.
  380.    
  381.    Your average developer wants to run several programs simultaneously --
  382.    usually at least an editor and a compilation, although often a
  383.    debugger as well. Obviously you cannot have two processes reading from
  384.    the same tty at the same time -- they'll each get some of the
  385.    characters you type, a useless situation. Likewise output should be
  386.    managed so that your editor's output doesn't get the output of a
  387.    background compile intermixed, destroying the screen.
  388.    
  389.    This has been a problem with many operating systems. One solution,
  390.    used by Tenex and TOPS-20, was to use process stacks. You could
  391.    interrupt a process to run another process, and when the new process
  392.    was finished the old would restart.
  393.    
  394.    While this was useful it didn't allow you to switch back and forth
  395.    between processes (like a debugger and editor) without exiting one of
  396.    them. Clearly there must be a better way.
  397.    
  398.   THE BERKELEY APPROACH 
  399.   
  400.    
  401.    
  402.    The Berkeley UNIX folks came up with a different idea, called process
  403.    groups. Whenever the shell starts a new command each process in the
  404.    command (there can be more than one, eg "ls | more") is placed in its
  405.    own process group, which is identified by a number. The tty has a
  406.    concept of "foreground process group", the group of processes which is
  407.    allowed to do input and output to the tty. The shell sets the
  408.    foreground process group when starting a new set of processes; by
  409.    convention the new process group number is the same as the process ID
  410.    of one of the members of the group. A set of processes has a tty
  411.    device to which it belongs, called its "controlling tty". This tty
  412.    device is what is returned when /dev/tty is opened.
  413.    
  414.    Because you want to be able to interrupt the foreground processes, the
  415.    tty watches for particular keypresses (^Z is the most common one) and
  416.    sends an interrupt signal to the foreground process group when it sees
  417.    one. All processes in the process group see the signal, and all stop
  418.    -- returning control to the shell.
  419.    
  420.    At this point the shell can place any of the active process groups
  421.    back in the foreground and restart the processes, or start a new
  422.    process group.
  423.    
  424.    To handle the case where a background process tries to read or write
  425.    from the tty, the tty driver will send a SIGTTIN or SIGTTOU signal to
  426.    any background process which attempts to perform such an operation.
  427.    Under normal circumstances, therefore, only the foreground process(es)
  428.    can use the tty.
  429.    
  430.    The set of commands to handle process groups is small and
  431.    straightforward. Under BSD, the commands are:
  432.    
  433.    
  434.           
  435.    int setpgrp(int process_id, int group_number);
  436.           
  437.           
  438.           Move a process into a process group. If you are creating a new
  439.           process group the group_number should be the same as
  440.           process_id. If process_id is zero, the current process is
  441.           moved.
  442.           
  443.    int getpgrp(int process_id);
  444.           
  445.           
  446.           Find the process group of the indicated process. If process_id
  447.           is zero, the current process is inspected.
  448.           
  449.    int killpgrp(int signal_number, int group_number);
  450.           
  451.           
  452.           Send a signal to all members of the indicated process group.
  453.           
  454.    int ioctl(int tty, TIOCSETPGRP, int foreground_group);
  455.           
  456.           
  457.           Change the foreground process group of a tty.
  458.           
  459.    int ioctl(int tty, TIOCGETPGRP, int *foreground_group);
  460.           
  461.           
  462.           Find the foreground process group of a tty.
  463.           
  464.    int ioctl(int tty, TIOCNOTTY, 0);
  465.           
  466.           
  467.           Disassociate this process from its controlling tty. The next
  468.           tty device that is opened will become the new controlling tty.
  469.           
  470.   THE POSIX APPROACH 
  471.   
  472.    
  473.    
  474.    The BSD process-group API is rarely used today, although most of the
  475.    concepts survive. The POSIX specification has provided new interfaces
  476.    for handling process groups, and even overloaded some existing ones.
  477.    It also limits several of the calls in ways which BSD did not.
  478.    
  479.    The POSIX process-group API is:
  480.    
  481.    
  482.           
  483.    int setpgid(int process_id, int process_group);
  484.           
  485.           
  486.           Move a process into a new process group. Process_id is the
  487.           process to move, process_group is the new process group.
  488.           
  489.    int getpgid(int process_id);
  490.           
  491.           
  492.           Find the process group of a process. Process_id is the process
  493.           to inspect.
  494.           
  495.    int getpgrp(void);
  496.           
  497.           
  498.           Find the process group of the current process. This is
  499.           identical to getpgrp(getpid()).
  500.           
  501.    int tcsetpgrp(int tty, int foreground_group);
  502.           
  503.           
  504.           Change the foreground process group of a tty. Tty is the file
  505.           descriptor of the tty to change, foreground_group is the new
  506.           foreground process group.
  507.           
  508.    int tcgetpgrp(int tty, int *foreground_group);
  509.           
  510.           
  511.           Find the foreground process group of a tty. Tty is the file
  512.           descriptor of the tty to inspect, foreground_group is returned
  513.           filled with the foreground process group of the tty.
  514.           
  515.    int kill(int -process_group, int signal_number);
  516.           
  517.           
  518.           Send a signal to a process group. Note that process_group must
  519.           be passed as a negative value, otherwise the signal goes to the
  520.           indicated process.
  521.           
  522.   DIFFERENCES BETWEEN POSIX AND BSD PROCESS GROUP MANAGEMENT 
  523.   
  524.    
  525.    
  526.    The setpgrp() function is called setpgid() under POSIX and is
  527.    essentially identical. You must be careful under POSIX not to use the
  528.    setpgrp() function -- usually it exists, but performs the operation of
  529.    setsid().
  530.    
  531.    The getpgrp() function was renamed getpgid(), and getpgid() can only
  532.    inspect the current process' process group.
  533.    
  534.    The killpgrp() function doesn't exist at all. Instead, a negative
  535.    value passed to the kill() function is taken to mean the process
  536.    group. Thus you'd perform killpgrp(process_group) by calling
  537.    kill(-process_group).
  538.    
  539.    The ioctl() commands for querying and changing the foreground process
  540.    group are replaced with first-class functions:
  541.      * int tcsetpgrp(int tty, int process_group);
  542.      * int tcgetpgrp(int tty, int *process_group);
  543.        
  544.    
  545.    
  546.    While the original BSD ioctl() functions would allow any tty to take
  547.    on any process group (or even nonexistant process groups) as its
  548.    foreground tty, POSIX allows only process groups which have the tty as
  549.    their controlling tty. This limitation disallows some ambiguous (and
  550.    potentially security-undermining) cases present in BSD.
  551.    
  552.    The TIOCNOTTY ioctl used in BSD is replaced with the setsid()
  553.    function, which is essentially identical to:
  554.    
  555.  
  556.         if (getpgrp() != getpid()) {
  557.           ioctl(tty, TIOCNOTTY, 0);
  558.           setpgrp(getpid(), getpid());
  559.         }
  560.  
  561.    
  562.    
  563.    It releases the current tty and puts the calling process into its own
  564.    process group. Notice that nothing is done if the calling process is
  565.    already in its own process group -- this is another new limitation,
  566.    and eliminates some ambiguous cases that existed in BSD (along with
  567.    some of BSD's flexibility).
  568.