Annotation of 43BSDReno/share/doc/ps2/03.uprog/p6, revision 1.1.1.1

1.1       root        1: .\"    @(#)p6  6.2 (Berkeley) 5/9/86
                      2: .\"
                      3: .NH
                      4: SIGNALS \(em INTERRUPTS AND ALL THAT
                      5: .PP
                      6: This section is concerned with how to
                      7: deal gracefully with signals from
                      8: the outside world (like interrupts), and with program faults.
                      9: Since there's nothing very useful that
                     10: can be done from within C about program
                     11: faults, which arise mainly from illegal memory references
                     12: or from execution of peculiar instructions,
                     13: we'll discuss only the outside-world signals:
                     14: .IT interrupt ,
                     15: which is sent when the
                     16: .UC DEL
                     17: character is typed;
                     18: .IT quit ,
                     19: generated by the
                     20: .UC FS
                     21: character;
                     22: .IT hangup ,
                     23: caused by hanging up the phone;
                     24: and
                     25: .IT terminate ,
                     26: generated by the
                     27: .IT kill
                     28: command.
                     29: When one of these events occurs,
                     30: the signal is sent to
                     31: .IT  all 
                     32: processes which were started
                     33: from the corresponding terminal;
                     34: unless other arrangements have been made,
                     35: the signal
                     36: terminates the process.
                     37: In the
                     38: .IT quit
                     39: case, a core image file is written for debugging
                     40: purposes.
                     41: .PP
                     42: The routine which alters the default action
                     43: is
                     44: called
                     45: .UL signal .
                     46: It has two arguments: the first specifies the signal, and the second
                     47: specifies how to treat it.
                     48: The first argument is just a number code, but the second is the
                     49: address is either a function, or a somewhat strange code
                     50: that requests that the signal either be ignored, or that it be
                     51: given the default action.
                     52: The include file
                     53: .UL signal.h
                     54: gives names for the various arguments, and should always be included
                     55: when signals are used.
                     56: Thus
                     57: .P1
                     58: #include <signal.h>
                     59:  ...
                     60: signal(SIGINT, SIG_IGN);
                     61: .P2
                     62: causes interrupts to be ignored, while
                     63: .P1
                     64: signal(SIGINT, SIG_DFL);
                     65: .P2
                     66: restores the default action of process termination.
                     67: In all cases,
                     68: .UL signal
                     69: returns the previous value of the signal.
                     70: The second argument to
                     71: .UL signal
                     72: may instead be the name of a function
                     73: (which has to be declared explicitly if
                     74: the compiler hasn't seen it already).
                     75: In this case, the named routine will be called
                     76: when the signal occurs.
                     77: Most commonly this facility is used
                     78: to allow the program to clean up
                     79: unfinished business before terminating, for example to
                     80: delete a temporary file:
                     81: .P1
                     82: #include <signal.h>
                     83: 
                     84: main()
                     85: {
                     86:        int onintr();
                     87: 
                     88:        if (signal(SIGINT, SIG_IGN) != SIG_IGN)
                     89:                signal(SIGINT, onintr);
                     90: 
                     91:        /* Process ... */
                     92: 
                     93:        exit(0);
                     94: }
                     95: 
                     96: onintr()
                     97: {
                     98:        unlink(tempfile);
                     99:        exit(1);
                    100: }
                    101: .P2
                    102: .PP
                    103: Why the test and the double call to
                    104: .UL signal ?
                    105: Recall that signals like interrupt are sent to
                    106: .ul
                    107: all
                    108: processes started from a particular terminal.
                    109: Accordingly, when a program is to be run
                    110: non-interactively
                    111: (started by
                    112: .UL & ),
                    113: the shell turns off interrupts for it
                    114: so it won't be stopped by interrupts intended for foreground processes.
                    115: If this program began by announcing that all interrupts were to be sent
                    116: to the
                    117: .UL onintr
                    118: routine regardless,
                    119: that would undo the shell's effort to protect it
                    120: when run in the background.
                    121: .PP
                    122: The solution, shown above, is to test the state of interrupt handling,
                    123: and to continue to ignore interrupts if they are already being ignored.
                    124: The code as written
                    125: depends on the fact that
                    126: .UL signal
                    127: returns the previous state of a particular signal.
                    128: If signals were already being ignored, the process should continue to ignore them;
                    129: otherwise, they should be caught.
                    130: .PP
                    131: A more sophisticated program may wish to intercept
                    132: an interrupt and interpret it as a request
                    133: to stop what it is doing
                    134: and return to its own command-processing loop.
                    135: Think of a text editor:
                    136: interrupting a long printout should not cause it
                    137: to terminate and lose the work
                    138: already done.
                    139: The outline of the code for this case is probably best written like this:
                    140: .P1
                    141: #include <signal.h>
                    142: #include <setjmp.h>
                    143: jmp_buf        sjbuf;
                    144: 
                    145: main()
                    146: {
                    147:        int (*istat)(), onintr();
                    148: 
                    149:        istat = signal(SIGINT, SIG_IGN);        /* save original status */
                    150:        setjmp(sjbuf);  /* save current stack position */
                    151:        if (istat != SIG_IGN)
                    152:                signal(SIGINT, onintr);
                    153: 
                    154:        /* main processing loop */
                    155: }
                    156: .P2
                    157: .P1
                    158: onintr()
                    159: {
                    160:        printf("\enInterrupt\en");
                    161:        longjmp(sjbuf); /* return to saved state */
                    162: }
                    163: .P2
                    164: The include file
                    165: .UL setjmp.h
                    166: declares the type
                    167: .UL jmp_buf
                    168: an object in which the state
                    169: can be saved.
                    170: .UL sjbuf
                    171: is such an object; it is an array of some sort.
                    172: The
                    173: .UL setjmp
                    174: routine then saves
                    175: the state of things.
                    176: When an interrupt occurs,
                    177: a call is forced to the
                    178: .UL onintr
                    179: routine,
                    180: which can print a message, set flags, or whatever.
                    181: .UL longjmp
                    182: takes as argument an object stored into by
                    183: .UL setjmp ,
                    184: and restores control
                    185: to the location after the call to
                    186: .UL setjmp ,
                    187: so control (and the stack level) will pop back
                    188: to the place in the main routine where
                    189: the signal is set up and the main loop entered.
                    190: Notice, by the way, that
                    191: the signal
                    192: gets set again after an interrupt occurs.
                    193: This is necessary; most signals are automatically
                    194: reset to their default action when they occur.
                    195: .PP
                    196: Some programs that want to detect signals simply can't be stopped
                    197: at an arbitrary point,
                    198: for example in the middle of updating a linked list.
                    199: If the routine called on occurrence of a signal
                    200: sets a flag and then
                    201: returns instead of calling
                    202: .UL exit
                    203: or
                    204: .UL longjmp ,
                    205: execution will continue
                    206: at the exact point it was interrupted.
                    207: The interrupt flag can then be tested later.
                    208: .PP
                    209: There is one difficulty associated with this
                    210: approach.
                    211: Suppose the program is reading the
                    212: terminal when the interrupt is sent.
                    213: The specified routine is duly called; it sets its flag
                    214: and returns.
                    215: If it were really true, as we said
                    216: above, that ``execution resumes at the exact point it was interrupted,''
                    217: the program would continue reading the terminal
                    218: until the user typed another line.
                    219: This behavior might well be confusing, since the user
                    220: might not know that the program is reading;
                    221: he presumably would prefer to have the signal take effect instantly.
                    222: The method chosen to resolve this difficulty
                    223: is to terminate the terminal read when execution
                    224: resumes after the signal, returning an error code
                    225: which indicates what happened.
                    226: .PP
                    227: Thus programs which catch and resume
                    228: execution after signals should be prepared for ``errors''
                    229: which are caused by interrupted
                    230: system calls.
                    231: (The ones to watch out for are reads from a terminal,
                    232: .UL wait ,
                    233: and
                    234: .UL pause .)
                    235: A program
                    236: whose
                    237: .UL onintr
                    238: program just sets
                    239: .UL intflag ,
                    240: resets the interrupt signal, and returns,
                    241: should usually include code like the following when it reads
                    242: the standard input:
                    243: .P1
                    244: if (getchar() == EOF)
                    245:        if (intflag)
                    246:                /* EOF caused by interrupt */
                    247:        else
                    248:                /* true end-of-file */
                    249: .P2
                    250: .PP
                    251: A final subtlety to keep in mind becomes important
                    252: when signal-catching is combined with execution of other programs.
                    253: Suppose a program catches interrupts, and also includes
                    254: a method (like ``!'' in the editor)
                    255: whereby other programs can be executed.
                    256: Then the code should look something like this:
                    257: .P1
                    258: if (fork() == 0)
                    259:        execl(...);
                    260: signal(SIGINT, SIG_IGN);       /* ignore interrupts */
                    261: wait(&status); /* until the child is done */
                    262: signal(SIGINT, onintr);        /* restore interrupts */
                    263: .P2
                    264: Why is this?
                    265: Again, it's not obvious but not really difficult.
                    266: Suppose the program you call catches its own interrupts.
                    267: If you interrupt the subprogram,
                    268: it will get the signal and return to its
                    269: main loop, and probably read your terminal.
                    270: But the calling program will also pop out of
                    271: its wait for the subprogram and read your terminal.
                    272: Having two processes reading
                    273: your terminal is very unfortunate,
                    274: since the system figuratively flips a coin to decide
                    275: who should get each line of input.
                    276: A simple way out is to have the parent program
                    277: ignore interrupts until the child is done.
                    278: This reasoning is reflected in the standard I/O library function
                    279: .UL system :
                    280: .P1
                    281: #include <signal.h>
                    282: 
                    283: system(s)      /* run command string s */
                    284: char *s;
                    285: {
                    286:        int status, pid, w;
                    287:        register int (*istat)(), (*qstat)();
                    288: 
                    289:        if ((pid = fork()) == 0) {
                    290:                execl("/bin/sh", "sh", "-c", s, 0);
                    291:                _exit(127);
                    292:        }
                    293:        istat = signal(SIGINT, SIG_IGN);
                    294:        qstat = signal(SIGQUIT, SIG_IGN);
                    295:        while ((w = wait(&status)) != pid && w != -1)
                    296:                ;
                    297:        if (w == -1)
                    298:                status = -1;
                    299:        signal(SIGINT, istat);
                    300:        signal(SIGQUIT, qstat);
                    301:        return(status);
                    302: }
                    303: .P2
                    304: .PP
                    305: As an aside on declarations,
                    306: the function
                    307: .UL signal
                    308: obviously has a rather strange second argument.
                    309: It is in fact a pointer to a function delivering an integer,
                    310: and this is also the type of the signal routine itself.
                    311: The two values
                    312: .UL SIG_IGN
                    313: and
                    314: .UL SIG_DFL
                    315: have the right type, but are chosen so they coincide with
                    316: no possible actual functions.
                    317: For the enthusiast, here is how they are defined for the PDP-11;
                    318: the definitions should be sufficiently ugly
                    319: and nonportable to encourage use of the include file.
                    320: .P1
                    321: #define        SIG_DFL (int (*)())0
                    322: #define        SIG_IGN (int (*)())1
                    323: .P2

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.