CS 3733 Operating Systems Notes: Signals



A signal is a software notification to a process of an event.

Terminology:

  • A signal is generated when the event that causes the signal occurs.
  • A signal is delivered when the process takes action based on the signal.
  • The lifetime of a signal is the interval between its generation and delivery.
  • A signal that has been generated but not yet delivered is pending.
  • A process catches a signal if it executes a signal handler when the signal is delivered.
  • Alternatively, a process can ignore as signal when it is delivered, that is to take no action.
  • The function sigaction is used to specify what is to happen to a signal when it is delivered.
  • The signal mask determines the action to be taken when the signal is generated. It contains a list of signals to be blocked.
  • A blocked signal is not delivered to a process until it is unblocked.
  • The function sigprocmask is used to modify the signal mask.
  • Each signal has a default action which is usually to terminate the process.


    SymbolMeaning
    SIGABRTabnormal termination as initiated by abort
    SIGALRM timeout signal as initiated by alarm
    SIGFPE error in arithmetic operation as in division by zero
    SIGHUP hang up (death) on controlling terminal (process)
    SIGILL invalid hardware instruction
    SIGINT interactive attention signal (CTRL-C)
    SIGKILL terminate (cannot be caught or ignored)
    SIGPIPE write on a pipe with no readers
    SIGQUIT interactive termination: dump core (CTRL-|)
    SIGSEGV invalid memory reference
    SIGTERM termination
    SIGUSR1 user-defined signal 1
    SIGUSR2 user-defined signal 2

    Table 5.1, Page 170: POSIX.1 required signals.

    Note that SIGKILL is special. It cannot be caught or ignored.


    SymbolMeaning
    SIGCHLD indicates child process terminated or stopped
    SIGCONT continue if stopped (done when generated)
    SIGSTOP stop signal (cannot be caught or ignored)
    SIGTSTP interactive stop signal
    SIGTTIN background process attempts to read from controlling terminal
    SIGTTOU background process attempts to write to controlling terminal

    Table 5.2, Page 171: The job control signals control foreground and background processes.


    You can send a signal to a process from the command line using kill
    kill -l will list the signals the system understands
    kill [-signal] pid will send a signal to a process.
    The optional argument may be a name or a number.
    The default is SIGTERM.
    To unconditionally kill a process, use:
    kill -9 pid which is
    kill -KILL pid.

    From a program you can use the kill system call:

    #include <sys/types.h>
    #include <signal.h>
    int kill(pid_t pid, int sig);
    
    Example 5.3, page 172, shows how to send a SIGUSR1 signal to process 3423.
    Example 5.4, page 173, shows how a child process can kill its parent.
    Example 5.5, page 173, shows the use of raise to send a signal to yourself.
    Example 5.7, page 174, shows a way to generate a SIGALRM signal after 10 seconds.


    Signal Mask and Signal Sets How do you deal with a set of signals?
    Originally, an int would hold a collection of bits, one per signal.
    Now, signal sets of type sigset_t are used.

    Here are the routines for handling sets of signals.
    You should compare these to the way select handles sets of file descriptors.

      #include <signal.h>
    
      int sigemptyset(sigset_t *set);
      int sigfillset(sigset_t *set);
      int sigaddset(sigset_t *set, int signo);
      int sigdelset(sigset_t *set, int signo);
      int sigismember(const sigset_t *set, int signo);
    
    sigemptyset initializes the set to contain no signals
    sigfillset puts all signals in the set
    sigaddset adds one signal to the set
    sigdelset removes one signal from the set
    sigismember tests to see if a signal is in the set

    The process signal mask is a set of signals that are blocked.
    A blocked signal remains pending after it is generated until the signal is unblocked.
    The process signal mask is modified with the sigprocmask system call.

       #include <signal.h>
       int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
    

    The how can be:

    Either set or oset may be NULL.

    Example 5.8, page 175, initializes a set to contain 2 signals.
    Example 5.9, page 176, blocks SIGINT
    Example 5.10, pages 176-177, blocks and unblocks SIGINT in a loop
    Example 5.11, pages 177-178, blocks signals while a pipe is created
    Example 5.12, pages 178-179, blocks signals while child is created

    The process signal mask is inherited by the child process.

    Catching and Ignoring Signals: sigaction

      #include <signal.h>
    
      int sigaction(int signo, const struct sigaction *act,
                                 struct sigaction *oact);
    
      struct sigaction {
         void (*sa_handler)(); /* SIG_DFL, SIG_IGN, or
                                  pointer to function */
         sigset_t sa_mask;     /* additional signals to be blocked
                                  during execution of handler */
         int sa_flags;         /* special flags and options */
      };
    
    Either act or oact may be NULL.

    void (*sa_handler)() means a pointer to a function that has not return value.

    Example 5.13, page 180: Set up a handler for SIGINT
    Example 5.14, pages 180-181: Ignore SIGINT if using the default action
    Example 5.15, page 181: Catch SIGINT
    Example 5.16, pages 181-182: Set SIGINT to default action
    Example 5.17, page 182: return 1 if signal is ignored

    Waiting for signals
    Old way: pause

    #include <unistd.h>
    int pause(void);
    
    Example 5.18, page 183: if a signal is received between the test and the pause, this may block forever.
    Example 5.19, page 183: Does not work since pause is executed while the signal is blocked.

    What you need to do: atomically unblock the signal and suspend the process.

    #include <signal.h>
    int sigsuspend(const sigset_t *sigmask);
    
    Sets the signal mask to the one pointed to by sigmask and suspends the process until a signal is received.
    When it returns the signal mask is restored to what it was before it was called.
    This function always returns -1.

    Example 5.20, page 184 shows a correct way to wait for a signal.

    A biff example

    Program 5.1, pages 186-188) uses signals to turn on or off the notification of mail.

    System Calls and Signals
    Two problems:
    system call interruption and
    non-reentrant system calls.

    System Call Interruption:

    Non-reentrant system calls:

    _exit() fstat() read() sysconf()
    access() getegid() rename() tcdrain()
    alarm() geteuid() rmdir() tcflow()
    cfgetispeed() getgid() setgid() tcflush()
    cfgetospeed() getgroups() setpgid() tcgetattr()
    cfsetispeed() getpgrp() setsid() tcgetpgrp()
    cfsetospeed() getpid() setuid() tcsendbreak()
    chdir() getppid() sigaction() tcsetattr()
    chmod() getuid() sigaddset() tcsetpgrp()
    chown() kill() sigdelset() time()
    close() link() sigemptyset() times()
    creat() lseek() sigfillset() umask()
    dup2() mkdir() sigismember() uname()
    dup() mkfifo() sigpending() unlink()
    execle() open() sigprocmask() utime()
    execve() pathconf() sigsuspend() wait()
    fcntl() pause() sleep() waitpid()
    fork() pipe() stat() write()
    Functions that POSIX.1 specify as async-signal-safe.

    Realtime Signals
    In 1993 a new signal interface was added to the POSIX standard.

    The sigaction structure has been extended to include an additional field:
      #include <signal.h>
    
      struct sigaction {
         void (*sa_handler)(); /* SIG_DFL, SIG_IGN, or
                                  pointer to function */
         void (*sa_sigaction)(int, siginfo_t *, void *);
         sigset_t sa_mask;     /* additional signals to be blocked
                                  during execution of handler */
         int sa_flags;         /* special flags and options */
      };
    
    This defines a new data type: siginfo_t which is a structure containing at least the following:
        int si_signo;            /* signal number */
        int si_code;             /* cause of the signal */
        union sigval si_value;   /* signal value */
    
    where the union sigval contains at least:
        int sival_int;
        void *sival_ptr;
    
    When the sa_flags field has the SA_SIGINFO flag set, then sa_sigaction is used for the signal handler rather than sa_handler.

    The system call to send one of these signals is called sigqueue rather than kill:

      #include <signal.h>
     
      int sigqueue(pid_t pid, int signo, const union sigval value);
    

    On some systems, only certain signals can be queued, those between SIGRTMIN and SIGRTMAX.

    Program 5.3, page 195 shows a program to send a queued signal to a process. Note that there is no command line function similar to kill.

    Program 5.4, page 196, gives an example program that can receive SIGUSR1 signals.