Docstoc

Signals

Document Sample
Signals Powered By Docstoc
					                            15-395

                       Signals
                   January 17, 2007
        Topics
                 Process Hierarchy
                 Shells
                 Signals
                 Nonlocal jumps




class16.ppt
ECF Exists at All Levels of a System
Exceptions
         Hardware and operating system kernel
          software                               Previous Lecture
Concurrent processes
         Hardware timer and kernel software

Signals
         Kernel software                        This Lecture

Non-local jumps
         Application code




–2–                                                     15-213, F’04
The World of Multitasking
System Runs Many Processes Concurrently
         Process: executing program
            State consists of memory image + register values + program
             counter
         Continually switches from one process to another
            Suspend process when it needs I/O resource or timer event
             occurs
            Resume process when I/O available or given scheduling priority
         Appears to user(s) as if all processes executing
          simultaneously
            Even though most systems can only execute one process at a
             time
            Except possibly with lower performance than if running alone




–3–                                                                15-213, F’04
Programmer’s Model of Multitasking
Basic Functions
         fork() spawns new process
            Called once, returns twice
         exit() terminates own process
            Called once, never returns
            Puts it into “zombie” status
         wait() and waitpid() wait for and reap terminated
          children
         execl() and execve() run a new program in an existing
          process
            Called once, (normally) never returns

Programming Challenge
         Understanding the nonstandard semantics of the functions
         Avoiding improper use of system resources
            E.g. “Fork bombs” can disable a system.

–4–                                                         15-213, F’04
Unix Process Hierarchy

                                [0]



                            init [1]


       Daemon               Login shell
      e.g. httpd


           Child                Child                Child




                   Grandchild           Grandchild

–5–                                                          15-213, F’04
The ps command
      Unix> ps aux -w --forest                         (output edited to fit slide)

      USER      PID   TTY   STAT   COMMAND
      root        1   ?     S      init [3]
      root        2   ?     SW     [keventd]
      root        3   ?     SWN    [ksoftirqd_CPU0]
      root        4   ?     SW     [kswapd]
      root        5   ?     SW     [bdflush]
      root        6   ?     SW     [kupdated]
      root        9   ?     SW<    [mdrecoveryd]
      root       12   ?     SW     [scsi_eh_0]
      root      397   ?     S      /sbin/pump -i eth0
      root      484   ?     S<     /usr/local/sbin/afsd -nosettime
      root      533   ?     S      syslogd -m 0
      root      538   ?     S      klogd -2
      rpc       563   ?     S      portmap
      rpcuser   578   ?     S      rpc.statd
      daemon    696   ?     S      /usr/sbin/atd
      root      713   ?     S      /usr/local/etc/nanny -init /etc/nanny.conf
      mmdf      721   ?     S       \_ /usr/local/etc/deliver -b -csmtpcmu
      root      732   ?     S       \_ /usr/local/sbin/named -f
      root      738   ?     S       \_ /usr/local/sbin/sshd -D
      root      739   ?     S<L     \_ /usr/local/etc/ntpd -n
      root      752   ?     S<L     |   \_ /usr/local/etc/ntpd -n
      root      753   ?     S<L     |       \_ /usr/local/etc/ntpd -n
      root      744   ?     S       \_ /usr/local/sbin/zhm -n zephyr-1.srv.cm
      root      774   ?     S      gpm -t ps/2 -m /dev/mouse
      root      786   ?     S      crond


–6–                                                                      15-213, F’04
The ps Command (cont.)
USER     PID   TTY       STAT COMMAND
root     889   tty1      S    /bin/login -- agn
agn      900   tty1      S     \_ xinit -- :0
root     921    ?        SL        \_ /etc/X11/X -auth /usr1/agn/.Xauthority :0
agn      948   tty1      S         \_ /bin/sh /afs/cs.cmu.edu/user/agn/.xinitrc
agn      958   tty1      S             \_ xterm -geometry 80x45+1+1 -C -j -ls -n
agn      966   pts/0     S             |   \_ -tcsh
agn     1184    pts/0    S             |       \_ /usr/local/bin/wish8.0 -f /usr
agn     1212   pts/0     S             |        |  \_ /usr/local/bin/wish8.0 -f
agn     3346   pts/0     S             |        |  \_ aspell -a -S
agn     1191   pts/0     S             |       \_ /bin/sh /usr/local/libexec/moz
agn     1204   8 pts/0   S             |           \_ /usr/local/libexec/mozilla
agn     1207   8 pts/0   S             |               \_ /usr/local/libexec/moz
agn     1208   8 pts/0   S             |               |   \_ /usr/local/libexec
agn     1209   8 pts/0   S             |               |   \_ /usr/local/libexec
agn    17814   8 pts/0   S             |               |   \_ /usr/local/libexec
agn     2469    pts/0    S             |               \_ usr/local/lib/Acrobat
agn     2483    pts/0    S             |               \_ java_vm
agn     2484    pts/0    S             |                   \_ java_vm
agn     2485    pts/0    S             |                       \_ java_vm
agn     3042    pts/0    S             |                       \_ java_vm
agn      959   tty1      S             \_ /bin/sh /usr/local/libexec/kde/bin/sta
agn     1020   tty1      S                 \_ kwrapper ksmserver




–7–                                                                  15-213, F’04
Shell Programs
A shell is an application program that runs programs on
  behalf of the user.
        sh – Original Unix Bourne Shell
        csh – BSD Unix C Shell, tcsh – Enhanced C Shell
        bash –Bourne-Again Shell

    int main()
    {
        char cmdline[MAXLINE];

          while (1) {
             /* read */
             printf("> ");
                                               Execution is a sequence of
             Fgets(cmdline, MAXLINE, stdin);      read/evaluate steps
             if (feof(stdin))
                 exit(0);

              /* evaluate */
              eval(cmdline);
          }
– 8 –}                                                         15-213, F’04
Simple Shell eval Function
void eval(char *cmdline)
{
    char *argv[MAXARGS]; /* argv for execve() */
    int bg;              /* should the job run in bg or fg? */
    pid_t pid;           /* process id */

      bg = parseline(cmdline, argv);
      if (!builtin_command(argv)) {
         if ((pid = Fork()) == 0) {    /* child runs user job */
             if (execve(argv[0], argv, environ) < 0) {
                 printf("%s: Command not found.\n", argv[0]);
                 exit(0);
             }
         }

          if (!bg) {    /* parent waits for fg job to terminate */
              int status;
               if (waitpid(pid, &status, 0) < 0)
                  unix_error("waitfg: waitpid error");
          }
          else          /* otherwise, don’t wait for bg job */
               printf("%d %s", pid, cmdline);
      }
}
–9–                                                           15-213, F’04
 Problem with Simple Shell Example
 Shell correctly waits for and reaps foreground jobs.
 But what about background jobs?
            Will become zombies when they terminate.
            Will never be reaped because shell (typically) will not
             terminate.
            Creates a memory leak that will eventually crash the kernel
             when it runs out of memory.

 Solution: Reaping background jobs requires a
   mechanism called a signal.




– 10 –                                                            15-213, F’04
 Signals
 A signal is a small message that notifies a process that
   an event of some type has occurred in the system.
            Kernel abstraction for exceptions and interrupts.
            Sent from the kernel (sometimes at the request of another
             process) to a process.
            Different signals are identified by small integer ID’s (1-30)
            The only information in a signal is its ID and the fact that it
             arrived.

ID         Name        Default Action      Corresponding Event
         2 SIGINT      Terminate           Interrupt from keyboard (ctl-c)
         9 SIGKILL     Terminate           Kill program (cannot override or ignore)
     11 SIGSEGV        Terminate & Dump    Segmentation violation
     14 SIGALRM        Terminate           Timer signal
     17 SIGCHLD        Ignore              Child stopped or terminated

– 11 –                                                                   15-213, F’04
 Signal Concepts
 Sending a signal
            Kernel sends (delivers) a signal to a destination process by
             updating some state in the context of the destination
             process.
            Kernel sends a signal for one of the following reasons:
               Kernel has detected a system event such as divide-by-zero
                (SIGFPE) or the termination of a child process (SIGCHLD)
               Another process has invoked the kill system call to explicitly
                request the kernel to send a signal to the destination process.




– 12 –                                                                  15-213, F’04
 Signal Concepts (continued)
 Receiving a signal
            A destination process receives a signal when it is forced by
             the kernel to react in some way to the delivery of the signal.
            Three possible ways to react:
               Ignore the signal (do nothing)
               Terminate the process (with optional core dump).
               Catch the signal by executing a user-level function called a
                signal handler.
                  » Akin to a hardware exception handler being called in
                    response to an asynchronous interrupt.




– 13 –                                                                  15-213, F’04
 Signal Concepts (continued)
 A signal is pending if it has been sent but not yet
   received.
            There can be at most one pending signal of any particular
             type.
            Important: Signals are not queued
               If a process has a pending signal of type k, then subsequent
                signals of type k that are sent to that process are discarded.

 A process can block the receipt of certain signals.
            Blocked signals can be delivered, but will not be received until
             the signal is unblocked.

 A pending signal is received at most once.



– 14 –                                                                   15-213, F’04
 Signal Concepts
 Kernel maintains pending and blocked bit vectors in
   the context of each process.
            pending – represents the set of pending signals
               Kernel sets bit k in pending whenever a signal of type k is
                delivered.
               Kernel clears bit k in pending whenever a signal of type k is
                received
            blocked – represents the set of blocked signals
               Can be set and cleared by the application using the
                sigprocmask function.




– 15 –                                                                  15-213, F’04
 Signal Concepts
 Debugging programs with signals
            printf statements
               If tsh stderr is overwritten with stdout, so fprintf(stderr… not
                necessary
               Print statements can tell you that a particular line was reached,
                but now when it was reached.
               It’s important to trace down when and where signals are
                reached. Hard to do.
            dbx
               Useful for tracing, but sometimes the trace signals mask other
                signals.
            strace
               Can tell you order in which signals were sent.




– 16 –                                                                     15-213, F’04
 Process Groups
  Every process belongs to exactly
    one process group
                                   pid=10
                                  pgid=10   Shell




          pid=20    Fore-                    Back-                  Back-
                                                     pid=32                 pid=40
         pgid=20   ground                   ground   pgid=32       ground   pgid=40
                     job                    job #1                 job #2

                                        Background               Background
                                      process group 32         process group 40
             Child      Child
                                            getpgrp() – Return process
             pid=21      pid=22
            pgid=20     pgid=20                group of current process
                                            setpgid() – Change process
               Foreground
                                               group of a process
            process group 20
– 17 –                                                                            15-213, F’04
 Sending Signals with kill Program
 kill program sends
   arbitrary signal to a          linux> ./forks 16
   process or process             linux> Child1: pid=24818 pgrp=24817
                                  Child2: pid=24819 pgrp=24817
   group
                                  linux> ps
                                    PID TTY          TIME CMD
                                  24788 pts/2    00:00:00 tcsh
 Examples                         24818 pts/2    00:00:02 forks
            kill –9 24818        24819 pts/2    00:00:02 forks
               Send SIGKILL to   24820 pts/2    00:00:00 ps
                                  linux> kill -9 -24817
               process 24818      linux> ps
            kill –9 –24817         PID TTY          TIME CMD
                                  24788 pts/2    00:00:00 tcsh
               Send SIGKILL to
                                  24823 pts/2    00:00:00 ps
               every process in   linux>
               process group
               24817.

– 18 –                                                       15-213, F’04
 Sending Signals from the Keyboard
 Typing ctrl-c (ctrl-z) sends a SIGINT (SIGTSTP) to every job in the
    foreground process group.
            SIGINT – default action is to terminate each process
            SIGTSTP – default action is to stop (suspend) each process

                                        pid=10
                                       pgid=10   Shell




                     pid=20    Fore-              Back-   pid=32
                                                                     Back-
                                                                             pid=40
                    pgid=20   ground             ground   pgid=32   ground   pgid=40
                                job              job #1             job #2

                                             Background         Background
                        Child      Child      process            process
                                              group 32           group 40
                       pid=21     pid=22
                      pgid=20    pgid=20

                         Foreground
                      process group 20
– 19 –                                                                             15-213, F’04
 Example of ctrl-c and ctrl-z
         linux> ./forks 17
         Child: pid=24868 pgrp=24867
         Parent: pid=24867 pgrp=24867
          <typed ctrl-z>
         Suspended
         linux> ps a
           PID TTY      STAT   TIME COMMAND
         24788 pts/2     S     0:00 -usr/local/bin/tcsh -i
         24867 pts/2     T     0:01 ./forks 17
         24868 pts/2     T     0:01 ./forks 17
         24869 pts/2     R     0:00 ps a
         bass> fg
         ./forks 17
         <typed ctrl-c>
         linux> ps a
           PID TTY      STAT   TIME COMMAND
         24788 pts/2     S     0:00 -usr/local/bin/tcsh -i
         24870 pts/2     R     0:00 ps a


– 20 –                                                       15-213, F’04
 Sending Signals with kill Function
     void fork12()
     {
         pid_t pid[N];
         int i, child_status;
         for (i = 0; i < N; i++)
              if ((pid[i] = fork()) == 0)
                  while(1); /* Child infinite loop */

         /* Parent terminates the child processes */
         for (i = 0; i < N; i++) {
              printf("Killing process %d\n", pid[i]);
              kill(pid[i], SIGINT);
         }

         /* Parent reaps terminated children */
         for (i = 0; i < N; i++) {
              pid_t wpid = wait(&child_status);
              if (WIFEXITED(child_status))
                  printf("Child %d terminated with exit status %d\n",
                          wpid, WEXITSTATUS(child_status));
              else
                  printf("Child %d terminated abnormally\n", wpid);
         }
     }

– 21 –                                                                  15-213, F’04
 Receiving Signals
 Suppose kernel is returning from an exception handler
   and is ready to pass control to process p.
 Kernel computes pnb = pending & ~blocked
            The set of pending nonblocked signals for process p

 If (pnb == 0)
            Pass control to next instruction in the logical flow for p.

 Else
            Choose least nonzero bit k in pnb and force process p to
             receive signal k.
            The receipt of the signal triggers some action by p
            Repeat for all nonzero k in pnb.
            Pass control to next instruction in logical flow for p.

– 22 –                                                                 15-213, F’04
 Default Actions
 Each signal type has a predefined default action, which
   is one of:
            The process terminates
            The process terminates and dumps core.
            The process stops until restarted by a SIGCONT signal.
            The process ignores the signal.




– 23 –                                                           15-213, F’04
 Installing Signal Handlers
 The signal function modifies the default action
   associated with the receipt of signal signum:
            handler_t *signal(int signum, handler_t *handler)

 Different values for handler:
            SIG_IGN: ignore signals of type signum
            SIG_DFL: revert to the default action on receipt of signals of
             type signum.
            Otherwise, handler is the address of a signal handler
               Called when process receives signal of type signum
               Referred to as “installing” the handler.
               Executing handler is called “catching” or “handling” the signal.
               When the handler executes its return statement, control passes
                back to instruction in the control flow of the process that was
                interrupted by receipt of the signal.

– 24 –                                                                   15-213, F’04
 Signal Handling Example
void int_handler(int sig)
{
    printf("Process %d received signal %d\n",
            getpid(), sig);
    exit(0);
}
                                      linux> ./forks 13
void fork13()                         Killing process 24973
{                                     Killing process 24974
    pid_t pid[N];                     Killing process 24975
    int i, child_status;              Killing process 24976
    signal(SIGINT, int_handler);      Killing process 24977
                                      Process 24977 received   signal 2
    . . .                             Child 24977 terminated   with exit   status 0
}                                     Process 24976 received   signal 2
                                      Child 24976 terminated   with exit   status 0
                                      Process 24975 received   signal 2
                                      Child 24975 terminated   with exit   status 0
                                      Process 24974 received   signal 2
                                      Child 24974 terminated   with exit   status 0
                                      Process 24973 received   signal 2
                                      Child 24973 terminated   with exit   status 0
                                      linux>

– 25 –                                                                 15-213, F’04
 Signal Handler Funkiness
                                                   Pending signals are not
int ccount = 0;                                      queued
void child_handler(int sig)
{                                                         For each signal type,
    int child_status;
    pid_t pid = wait(&child_status);                       just have single bit
    ccount--;                                              indicating whether or
    printf("Received signal %d from process %d\n",
           sig, pid);
                                                           not signal is pending
}                                                         Even if multiple
void fork14()                                              processes have sent
{                                                          this signal
    pid_t pid[N];                                         This code could hang because
    int i, child_status;                                   signals will be lost.
    ccount = N;                                           If we take out the sleep from the child
    signal(SIGCHLD, child_handler);                        the program will probably not hang
    for (i = 0; i < N; i++)                                because children will execute (and
         if ((pid[i] = fork()) == 0) {                     signal) in order.
             sleep(1);
           /* Child: Exit */
             exit(0);                                  The pause system call causes the
           }                                           process to sleep until a signal is
      while (ccount > 0)                               received by the process.
           pause();/* Suspend until signal occurs */
– 26 –
}                                                                            15-213, F’04
  Living With Nonqueuing Signals
         Must check for all terminated jobs
               Typically loop with wait
                                            Means wait for any child
    void child_handler2(int sig)                    Means “return immediately
    {                                               if no child has terminated”
        int child_status;
        pid_t pid;
        while ((pid = waitpid(-1, &child_status, WNOHANG)) > 0) {
           ccount--;
           printf("Received signal %d from process %d\n", sig, pid);
        }
    }

    void fork15()
    {
        . . .
        signal(SIGCHLD, child_handler2);
        . . .
    }


– 27 –                                                                 15-213, F’04
 Waiting for children
 pid_t waitpid(pid_t pid, int *status, int options)
     return value
         the pid of the child that “died”
         0 if WNOHANG and no child was available
         -1 on error
     value of pid:
          < -1   wait for any process who shares a group ID equal to
     the absolute value of pid
         -1     wait for any process to die (same as wait)
        0       wait for any process whose process group ID is the
     same as the calling process
         >0     wait for the child whose process ID is pid
– 28 –                                                       15-213, F’04
 Waiting for children
 pid_t waitpid(pid_t pid, int *status, int options)
     value of options:
         WNOHANG      return immediately if no child has exited
         WUNTRACED return for children who are stopped (but not
                   traced) and whose status has not been
                   reported.




– 29 –                                                     15-213, F’04
 Waiting for children
 pid_t waitpid(pid_t pid, int *status, int options)

 Can determine how child exited with macros. Status is the variable
   that was passed to waitpid.
     WIFEXITED(status)     returns true if child exited by returning from the main function
                           or called exit()
     WIFSIGNALED(status)   returns true if child terminated because of a signal that was not
                           caught.
     WTERMSIG(status)      returns the number of the signal that caused the child process
                           to terminate. Can only use this macro if WIFSIGNALED
                           returned true
     WIFSTOPPED(status)    returns true if the child process that caused the return is
                           currently stopped (e.g., by a ^z or similar). Only possible if the
                           waitpid had an option of WUNTRACED or when the child is
                           being traced.
     WSTOPSIG(status).     Returns the number of the signal that caused the child process
                           to stop. Can only use this if WIFSTOPPED returns true.


– 30 –                                                                            15-213, F’04
 Signal Handler Funkiness (Cont.)
   Signal arrival during long system calls (say a read)
    Signal handler interrupts read() call
            Linux: upon return from signal handler, the read() call is
             restarted automatically
            Some other flavors of Unix can cause the read() call to fail
             with an EINTER error number (errno)
             in this case, the application program can restart the slow
             system call




         Subtle differences like these complicate the writing
         of portable code that uses signals.


– 31 –                                                           15-213, F’04
 Signal Handler Funkiness (Cont.)
   Signal arrival during long system calls (say a read)             /* This code continues from previous column: */
   /* $begin signal3 */
                                                                    /* Manually restart the read call if it is interrupted */
   #include "csapp.h"
                                                                    /* EINTR is an error caused by an interrupt. */

   void handler2(int sig)                                               while ((n = read(STDIN_FILENO, buf, sizeof(buf))) < 0)
                                                                            if (errno != EINTR)
   {                                                                             unix_error("read error");
     pid_t pid;
                                                                        printf("Parent processing input\n");
                                                                        while (1)
       while ((pid = waitpid(-1, NULL, 0)) > 0)                                          ;
              printf("Handler reaped child %d\n", (int)pid);                             ;
                                                                        exit(0);
       if (errno != ECHILD)                                         }
              unix_error("waitpid error");
       Sleep(2);
       return;
   }

   int main() {
      int i, n;
      char buf[MAXBUF];
      pid_t pid;

       if (signal(SIGCHLD, handler2) == SIG_ERR)
              unix_error("signal error");

       /* parent creates children */
       for (i = 0; i < 3; i++) {
              pid = Fork();
              if (pid == 0) {
                  printf("Hello from child %d\n", (int)getpid());
                  Sleep(1);
                  exit(0);
              }
       }



– 32 –                                                                                                                           15-213, F’04
 A Program That Reacts to
 Externally Generated Events (ctrl-c)
         #include <stdlib.h>
         #include <stdio.h>
         #include <signal.h>

         void handler(int sig) {
           printf("You think hitting ctrl-c will stop the bomb?\n");
           sleep(2);
           printf("Well...");
           fflush(stdout);
           sleep(1);
           printf("OK\n");
           exit(0);
         }

         main() {
           signal(SIGINT, handler); /* installs ctl-c handler */
           while(1) {
           }
         }
– 33 –                                                             15-213, F’04
 A Program That Reacts to Internally
 Generated Events
 #include <stdio.h>         main() {
 #include <signal.h>          signal(SIGALRM, handler);
                              alarm(1); /* send SIGALRM in
 int beeps = 0;                            1 second */

 /* SIGALRM handler */          while (1) {
 void handler(int sig) {          /* handler returns here */
   printf("BEEP\n");            }
   fflush(stdout);          }

     if (++beeps < 5)
       alarm(1);            linux> a.out
     else {                 BEEP
       printf("BOOM!\n");   BEEP
       exit(0);             BEEP
     }                      BEEP
 }                          BEEP
                            BOOM!
                            bass>

– 34 –                                               15-213, F’04
 Example: parent that reaps children
    Attempt 1
#include <stdio.h>
#include <signal.h>                               main() {
                                                    int I, n;
 /* SIGCHLD handler */                              char buf[MAXBUF];
 void handler(int sig) {                            if (signal(SIGCHLD, handler)== SOG_ERR)
   pid_t pid;                                               unix_error(“signal error”);
   if ((pid=waitpid(-1, NULL, 0)) < 0)              /* parent creates children */
           unit_error(“waitpid error”);             for (i=0; i<3; i++){
   printf(“Handler reaped child %d\n”, (int)pid);           if (Fork() == 0){
   Sleep(2);                                                  printf(“Hello from child %d\n”,
   return;                                                            (int)getpid());
                            Example fails because:            Sleep(1);
 }
                             1. signals can block             exit(0);
                             2. signals are not
Parent process creates some children that queued
                                                            }
                                                    }
                         3. system calls
run independently for a while and then        can be interrupted
terminate.                                            if ((n=read(STDIN_FILENO, buf,
                                                                        sizeof(buf))) <0)
Parent must reap the children to avoid                        unix_error(“read”);
leaving zombies.
                                                      printf(“Parent processing input\n”);

Parent wants to continue working while                while (1) {
children run.                                           /* handler returns here */
                                                      }
So reap children with a SIGCHLD handler           }
instead of using wait

– 35 –                                                                           15-213, F’04
 Child signals
 When a child terminates, kernel notifies parent with a
   SIGCHLD signal!


 Result of running previous program (it creates 3
   children):

 Linux> ./signal1
 Hello from child 10320
 Hello from child 10321
 Hello from child 10322
 Handler reaped child 10320     What happened to child 10321?
 Handler reaped child 10322
 <cr>
 Parent processing input
– 36 –                                                  15-213, F’04
 Child signals
 Suspend parent and check processes:

                                                                              Signals cannot be used to
 <ctrl-z>                                                                     count the occurrence of
 Suspended                                                                    events in other processes!
 Linux> ps
 PID TTY STAT                 TIME        COMMAND
 …
 10319 p5 T                   0:03 signal1
 10321 p5 Z                   0:00 (signal1 <zombie>)
 10323 p5 R                   0:00 ps
         Problem: signals are not queued!

         1.   First signal is caught by parent
         2.   While handler is still processing the first signal, second signal is delivered and
              added to list of pending signals
         3.   This signal is queued, but does not interrupt the current SIGCHLD handler.
         4.   The third signal arrives. Since there is already a signal pending, it is discarded.


– 37 –                                                                                        15-213, F’04
 Example: parent that reaps children
    Attempt 2
#include <stdio.h>
#include <signal.h>                          main() {
                                               int I, n;
/* SIGCHLD handler */                          char buf[MAXBUF];
void handler(int sig) {                        if (signal(SIGCHLD, handler)== SOG_ERR)
  pid_t pid;                                           unix_error(“signal error”);
                                               /* parent creates children */
    while ((pid=waitpid(-1, NULL, 0)) > 0)
      printf(“Handler reaped child %d\n”,        for (i=0; i<3; i++){
                                (int)pid);               if (Fork() == 0){
                                                           printf(“Hello from child %d\n”,
    if (errno != ECHILD)                                           (int)getpid());
      unit_error(“waitpid error”);                         Sleep(1);
    Sleep(2);                                              exit(0);
    return;                                              }
}                                                }

                                                 if ((n=read(STDIN_FILENO, buf,
                                                                   sizeof(buf))) <0)
    Example fails (on Solaris) because:                  unix_error(“read”);
     3. system calls can be interrupted          printf(“Parent processing input\n”);

                                                 while (1) {
                                                   /* handler returns here */
                                                 }
                                             }


– 38 –                                                                      15-213, F’04
 Child signals
 Result of running previous program on Linux:

 Linux> ./signal2
 Hello from child 10378
 Hello from child 10379
 Hello from child 10380                 Correct!
 Handler reaped child 10378
 Handler reaped child 10379
 Handler reaped child 10380
 <cr>
 Parent processing input




– 39 –                                             15-213, F’04
 Child signals
 Result of running previous program on Solaris:
                                  Problem!
 Linux> ./signal2
 Hello from child 10378           On this Solaris system (but not
 Hello from child 10379           all), slow system calls like read
                                  are not restarted after they are
 Hello from child 10380           interrupted by the delivery of a
 Handler reaped child 10378       signal!
 Handler reaped child 10379
 Handler reaped child 10380       Linux restarts the system call
 <cr>                             automatically!
 read: Interrupted system call




– 40 –                                                   15-213, F’04
 Example: parent that reaps children
    Attempt 3
#include <stdio.h>
#include <signal.h>                          main() {
                                               int I, n;
/* SIGCHLD handler */                          char buf[MAXBUF];
void handler(int sig) {                        if (signal(SIGCHLD, handler)== SOG_ERR)
  pid_t pid;                                           unix_error(“signal error”);
                                               /* parent creates children */
    while ((pid=waitpid(-1, NULL, 0)) > 0)
      printf(“Handler reaped child %d\n”,        for (i=0; i<3; i++){
                                (int)pid);               if (Fork() == 0){
                                                           printf(“Hello from child %d\n”,
    if (errno != ECHILD)                                           (int)getpid());
      unit_error(“waitpid error”);                         Sleep(1);
    Sleep(2);                                              exit(0);
    return;                                              }
}                                                }

                                                 while ((n=read(STDIN_FILENO, buf,
                                                                   sizeof(buf))) <0)
    Works on all systems!                                if (errno != EINTR)
                                                             unix_error(“read”);

                                                 printf(“Parent processing input\n”);

                                                 while (1) {
                                                   /* handler returns here */
                                                 }
                                             }
– 41 –                                                                      15-213, F’04
 Explicitly blocking signals
 #include <signal.h>


 int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);


 int sigemptyset(sigset_t *set);


 int sigfillset(sigset_t *set);


 int sigaddset(sigset_t *set, int signum);


 // following function returns 0 if OK, -1 on error
 int sigdelset(sigset_t *set, int signum);


 // following function returns 1 if member, 0 if not,   -1 on error
 int sigismember(const sigset_t *set, int signum);



– 42 –                                                                15-213, F’04
 Explicitly blocking signals
 int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);


 Changes the set of currently blocked signals (the blocked bit)
 Values of how:
 SIG_BLOCK: add the signals in set to blocked
                                      (blocked = blocked | set)
 SIG_UNBLOCK: remove the signals in set from blocked
                                      (blocked = blocked & ~set)
 SIG_SETMASK:   blocked = set


 If oldset is non-NULL, the previous value of the blocked bit
    vector is stored in oldset




– 43 –                                                      15-213, F’04
 Explicitly blocking signals
 // initializes set to the empty set
 int sigemptyset(sigset_t *set);


 // adds every signal to set
 int sigfillset(sigset_t *set);


 //      adds signum to set
 int sigaddset(sigset_t *set, int signum);


 // deletes signum from set
 // following function returns 0 if OK, -1 on error
 int sigdelset(sigset_t *set, int signum);


 // following function returns 1 if member, 0 if not,   -1 on error
 int sigismember(const sigset_t *set, int signum);



– 44 –                                                                15-213, F’04
 Explicitly blocking signals
 Synchronizing parent and children
                                             main() {
#include <stdio.h>                             int pid;
#include <signal.h>                            sigset_t mask;

/* SIGCHLD handler */                            if (Signal(SIGCHLD, handler)== SOG_ERR)
void handler(int sig) {                                  unix_error(“signal error”);
  pid_t pid;                                     /* parent creates children */

    while ((pid=waitpid(-1, NULL, 0)) > 0)       initjobs(); /* initialize job list */
      /* delete child from job list */
      deletejob(pid);                            while (1) {
                                                   Sigemptyset(&mask);
    if (errno != ECHILD)                           Sigaddset(&mask, SIGCHLD);
      unit_error(“waitpid error”);                 Sigprocmask(SIG_BLOCK, &mask, NULL);
}
                                                   /* child process*/
                                                   if ((pid= Fork()) == 0){
                                                       Sigprocmask(SIG_UNBLOCK, &mask, NULL);
                                                       Execve(“/bin/ls”, argv, NULL);
                                                   }
                                                 /* parent process */
                                                   addjob(pid); /* add child to job list */
                                                   Sigprocmask(SIG_UNBLOCK, &mask, NULL);
                                                 }
                                             }
– 45 –                                                                         15-213, F’04
 Explicitly blocking signals
 Synchronizing parent and children
 If do not synchronize can get problems:
   1. the parent executes fork and child runs
   2. before parent runs, child terminates and becomes a
     zombie; kernel sends sigchld signal to parent
   3. parent receives the sigchld signal before it runs
   4. the handler reaps the terminated child and calls
     deletejob which does nothing because the parent
     has not yet added the child to the job list.
   5. after the handler runs, the parent runs which
     returns from fork and adds the (non-existent) child
     by calling addlist
– 46 –                                              15-213, F’04
  Nonlocal Jumps: setjmp/longjmp
 Powerful (but dangerous) user-level mechanism for transferring
   control to an arbitrary location.
            Controlled to way to break the procedure call / return discipline
            Useful for error recovery and signal handling


 int setjmp(jmp_buf j)
            Must be called before longjmp
            Identifies a return site for a subsequent longjmp.
            Called once, returns one or more times
 Implementation:
            Remember where you are by storing the current register context,
             stack pointer, and PC value in jmp_buf.
            Return 0



– 47 –                                                                    15-213, F’04
 setjmp/longjmp (cont)
  void longjmp(jmp_buf j, int i)
            Meaning:
               return from the setjmp remembered by jump buffer j again...
               …this time returning i instead of 0
            Called after setjmp
            Called once, but never returns



  longjmp Implementation:
            Restore register context from jump buffer j
            Set %eax (the return value) to i
            Jump to the location indicated by the PC stored in jump buf j.


– 48 –                                                               15-213, F’04
 setjmp/longjmp Example

         #include <setjmp.h>
         jmp_buf buf;

         main() {
             if (setjmp(buf) != 0) {
                printf("back in main due to an error\n");
             else
                printf("first time through\n");
             p1(); /* p1 calls p2, which calls p3 */
         }
         ...

         ...

         p3() {
            <error checking code>
            if (error)
                longjmp(buf, 1)
         }

– 49 –                                                      15-213, F’04
 Putting It All Together: A Program
 That Restarts Itself When ctrl-c’d
  #include <stdio.h>             while(1) {
  #include <signal.h>                sleep(1);
  #include <setjmp.h>                printf("processing...\n");
                                   }
  sigjmp_buf buf;                }

  void handler(int sig) {        bass> a.out
    siglongjmp(buf, 1);          starting
  }                              processing...
                                 processing...
  main() {                       restarting                                Ctrl-c
    signal(SIGINT, handler);     processing...
                                 processing...
                                 restarting                                Ctrl-c
     if (!sigsetjmp(buf, 1))
       printf("starting\n");     processing...
     else
       printf("restarting\n");    Notes:
                                  1. sigsetjmp and siglongjmp are similar to setjmp and
                                  longjmp except that they always save the signal mask
                                  while setjmp may not.
                                  2. you must include the setjmp.h library to use
                                  sigsetjmp and siglongjmp
– 50 –                                                                               15-213, F’04
 Limitations of Nonlocal Jumps
 Works within stack discipline
            Can only long jump to environment of function that has been
             called but not yet completed    env
             jmp_buf env;                               P1           P1

             P1()                                                  After longjmp
             {                                         P2
               if (setjmp(env)) {
                  /* Long Jump to here */
                                                       P2
               } else {
                  P2();
               }                                       P2
             }

             P2()                                      P3
             { . . . P2(); . . . P3(); }
                                                  Before longjmp
             P3()
             {
               longjmp(env, 1);
– 51 –       }                                                      15-213, F’04
 Limitations of Long Jumps (cont.)
 Works within stack discipline
            Can only long jump to environment of function that has been
             called but not yet completed
                                                        P1
             jmp_buf env;

             P1()                                       P2
             {                                env
               P2(); P3();
                                                     At setjmp
             }

             P2()                                       P1
             {
                 if (setjmp(env)) {            env
                  /* Long Jump to here */        X      P2
               }                                                                  P1
             }                                       P2 returns
                                                                 env
             P3()                                  Error!!                        P3
                                                                       X
             {                                 Jump to wrong
               longjmp(env, 1);                  subroutine!
             }                                                               At longjmp
– 52 –                                                                     15-213, F’04
 Summary
 Signals provide process-level exception handling
            Can generate from user programs
            Can define effect by declaring signal handler

 Some caveats
            Very high overhead
                 >10,000 clock cycles
               Only use for exceptional conditions
            Don’t have queues
               Just one bit for each pending signal type

 Nonlocal jumps provide exceptional control flow within
   process
            Within constraints of stack discipline


– 53 –                                                       15-213, F’04

				
DOCUMENT INFO
Shared By:
Categories:
Tags:
Stats:
views:0
posted:3/7/2013
language:English
pages:53