home *** CD-ROM | disk | FTP | other *** search
/ minnie.tuhs.org / unixen.tar / unixen / PDP-11 / Trees / V7 / usr / doc / uprog / p6 < prev    next >
Encoding:
Text File  |  1979-01-10  |  8.2 KB  |  322 lines

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