?? p6
字號:
.NHSIGNALS \(em INTERRUPTS AND ALL THAT.PPThis section is concerned with how todeal gracefully with signals fromthe outside world (like interrupts), and with program faults.Since there's nothing very useful thatcan be done from within C about programfaults, which arise mainly from illegal memory referencesor from execution of peculiar instructions,we'll discuss only the outside-world signals:.IT interrupt ,which is sent when the.UC DELcharacter is typed;.IT quit ,generated by the.UC FScharacter;.IT hangup ,caused by hanging up the phone;and.IT terminate ,generated by the.IT killcommand.When one of these events occurs,the signal is sent to.IT all processes which were startedfrom the corresponding terminal;unless other arrangements have been made,the signalterminates the process.In the.IT quitcase, a core image file is written for debuggingpurposes..PPThe routine which alters the default actioniscalled.UL signal .It has two arguments: the first specifies the signal, and the secondspecifies how to treat it.The first argument is just a number code, but the second is theaddress is either a function, or a somewhat strange codethat requests that the signal either be ignored, or that it begiven the default action.The include file.UL signal.hgives names for the various arguments, and should always be includedwhen signals are used.Thus.P1#include <signal.h> ...signal(SIGINT, SIG_IGN);.P2causes interrupts to be ignored, while.P1signal(SIGINT, SIG_DFL);.P2restores the default action of process termination.In all cases,.UL signalreturns the previous value of the signal.The second argument to.UL signalmay instead be the name of a function(which has to be declared explicitly ifthe compiler hasn't seen it already).In this case, the named routine will be calledwhen the signal occurs.Most commonly this facility is usedto allow the program to clean upunfinished business before terminating, for example todelete a temporary file:.P1#include <signal.h>main(){ int onintr(); if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, onintr); /* Process ... */ exit(0);}onintr(){ unlink(tempfile); exit(1);}.P2.PPWhy the test and the double call to.UL signal ?Recall that signals like interrupt are sent to.ulallprocesses started from a particular terminal.Accordingly, when a program is to be runnon-interactively(started by.UL & ),the shell turns off interrupts for itso it won't be stopped by interrupts intended for foreground processes.If this program began by announcing that all interrupts were to be sentto the.UL onintrroutine regardless,that would undo the shell's effort to protect itwhen run in the background..PPThe solution, shown above, is to test the state of interrupt handling,and to continue to ignore interrupts if they are already being ignored.The code as writtendepends on the fact that.UL signalreturns the previous state of a particular signal.If signals were already being ignored, the process should continue to ignore them;otherwise, they should be caught..PPA more sophisticated program may wish to interceptan interrupt and interpret it as a requestto stop what it is doingand return to its own command-processing loop.Think of a text editor:interrupting a long printout should not cause itto terminate and lose the workalready done.The outline of the code for this case is probably best written like this:.P1#include <signal.h>#include <setjmp.h>jmp_buf sjbuf;main(){ int (*istat)(), onintr(); istat = signal(SIGINT, SIG_IGN); /* save original status */ setjmp(sjbuf); /* save current stack position */ if (istat != SIG_IGN) signal(SIGINT, onintr); /* main processing loop */}.P2.P1onintr(){ printf("\nInterrupt\n"); longjmp(sjbuf); /* return to saved state */}.P2The include file.UL setjmp.hdeclares the type.UL jmp_bufan object in which the statecan be saved..UL sjbufis such an object; it is an array of some sort.The.UL setjmproutine then savesthe state of things.When an interrupt occurs,a call is forced to the.UL onintrroutine,which can print a message, set flags, or whatever..UL longjmptakes as argument an object stored into by.UL setjmp ,and restores controlto the location after the call to.UL setjmp ,so control (and the stack level) will pop backto the place in the main routine wherethe signal is set up and the main loop entered.Notice, by the way, thatthe signalgets set again after an interrupt occurs.This is necessary; most signals are automaticallyreset to their default action when they occur..PPSome programs that want to detect signals simply can't be stoppedat an arbitrary point,for example in the middle of updating a linked list.If the routine called on occurrence of a signalsets a flag and thenreturns instead of calling.UL exitor.UL longjmp ,execution will continueat the exact point it was interrupted.The interrupt flag can then be tested later..PPThere is one difficulty associated with thisapproach.Suppose the program is reading theterminal when the interrupt is sent.The specified routine is duly called; it sets its flagand returns.If it were really true, as we saidabove, that ``execution resumes at the exact point it was interrupted,''the program would continue reading the terminaluntil the user typed another line.This behavior might well be confusing, since the usermight not know that the program is reading;he presumably would prefer to have the signal take effect instantly.The method chosen to resolve this difficultyis to terminate the terminal read when executionresumes after the signal, returning an error codewhich indicates what happened..PPThus programs which catch and resumeexecution after signals should be prepared for ``errors''which are caused by interruptedsystem calls.(The ones to watch out for are reads from a terminal,.UL wait ,and.UL pause .)A programwhose.UL onintrprogram just sets.UL intflag ,resets the interrupt signal, and returns,should usually include code like the following when it readsthe standard input:.P1if (getchar() == EOF) if (intflag) /* EOF caused by interrupt */ else /* true end-of-file */.P2.PPA final subtlety to keep in mind becomes importantwhen signal-catching is combined with execution of other programs.Suppose a program catches interrupts, and also includesa method (like ``!'' in the editor)whereby other programs can be executed.Then the code should look something like this:.P1if (fork() == 0) execl(...);signal(SIGINT, SIG_IGN); /* ignore interrupts */wait(&status); /* until the child is done */signal(SIGINT, onintr); /* restore interrupts */.P2Why is this?Again, it's not obvious but not really difficult.Suppose the program you call catches its own interrupts.If you interrupt the subprogram,it will get the signal and return to itsmain loop, and probably read your terminal.But the calling program will also pop out ofits wait for the subprogram and read your terminal.Having two processes readingyour terminal is very unfortunate,since the system figuratively flips a coin to decidewho should get each line of input.A simple way out is to have the parent programignore interrupts until the child is done.This reasoning is reflected in the standard I/O library function.UL system :.P1#include <signal.h>system(s) /* run command string s */char *s;{ int status, pid, w; register int (*istat)(), (*qstat)(); if ((pid = fork()) == 0) { execl("/bin/sh", "sh", "-c", s, 0); _exit(127); } istat = signal(SIGINT, SIG_IGN); qstat = signal(SIGQUIT, SIG_IGN); while ((w = wait(&status)) != pid && w != -1) ; if (w == -1) status = -1; signal(SIGINT, istat); signal(SIGQUIT, qstat); return(status);}.P2.PPAs an aside on declarations,the function.UL signalobviously has a rather strange second argument.It is in fact a pointer to a function delivering an integer,and this is also the type of the signal routine itself.The two values.UL SIG_IGNand.UL SIG_DFLhave the right type, but are chosen so they coincide withno possible actual functions.For the enthusiast, here is how they are defined for the PDP-11;the definitions should be sufficiently uglyand nonportable to encourage use of the include file..P1#define SIG_DFL (int (*)())0#define SIG_IGN (int (*)())1.P2
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -