-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsigintro.html
776 lines (679 loc) · 30.1 KB
/
sigintro.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Introduction To Unix Signals Programming</title>
</head>
<body>
<center><h1>Introduction To Unix Signals Programming</h1></center>
<font color="brown"><h2>Table Of Contents</h2></font>
<ol>
<li> <a href="#the_what">What Are Signals?</a>
</li><li> <a href="#sending">Sending Signals To Processes</a>
</li><li> <a href="#catching">Catching Signals - Signal Handlers</a>
</li><li> <a href="#installing_handlers">Installing Signal Handlers</a>
</li><li> <a href="#masking">Avoiding Signal Races - Masking Signals</a>
</li><li> <a href="#timers">Implementing Timers Using Signals</a>
</li><li> <a href="#do_dont">Summary - "Do" and "Don't" inside A Signal Handler</a>
</li></ol>
<hr size="4">
<a name="the_what">
<font color="brown"><h2>What Are Signals?</h2></font>
</a>
<p>
Signals, to be short, are various notifications sent to a process
in order to notify it of various "important" events. By their nature,
they interrupt whatever the process is doing at this minute, and force
it to handle them immediately. Each signal has an integer number that
represents it (1, 2 and so on), as well as a symbolic name that is
usually defined in the file /usr/include/signal.h or one of the
files included by it directly or indirectly (<code>HUP</code>,
<code>INT</code> and so on. Use the command <code>'kill -l'</code> to see
a list of signals supported by your system).
</p>
<p>
Each signal may have a signal handler, which is a function that gets
called when the process receives that signal. The function is called
in "asynchronous mode", meaning that no where in your program you have
code that calls this function directly. Instead, when the signal is
sent to the process, the operating system stops the execution of the
process, and "forces" it to call the signal handler function. When that
signal handler function returns, the process continues execution from
wherever it happened to be before the signal was received, as if
this interruption never occurred.
</p>
<p>
Note for "hardwarists": If you are familiar with interrupts (you are,
right?), signals are very similar in their behavior. The difference
is that while interrupts are sent to the operating system by the hardware,
signals are sent to the process by the operating system, or by other
processes. Note that signals have nothing to do with software interrupts,
which are still sent by the hardware (the CPU itself, in this case).
</p>
<hr size="4">
<a name="sending">
<font color="brown"><h2>Sending Signals To Processes</h2></font>
</a>
<hr>
<font color="brown"><h3>Sending Signals Using The Keyboard</h3></font>
<p>
The most common way of sending signals to processes is using the keyboard.
There are certain key presses that are interpreted by the system as requests
to send signals to the process with which we are interacting:
</p><dl>
<dt><u>Ctrl-C</u>
</dt><dd>
Pressing this key causes the system to send an <code>INT</code> signal
(<code>SIGINT</code>) to the
running process. By default, this signal causes the process to immediately
terminate.
</dd>
<br><br>
<dt><u>Ctrl-Z</u>
</dt><dd>
Pressing this key causes the system to send a <code>TSTP</code> signal
(<code>SIGTSTP</code>) to the
running process. By default, this signal causes the process to suspend
execution.
</dd>
<br><br>
<dt><u>Ctrl-\</u>
</dt><dd>
Pressing this key causes the system to send a <code>ABRT</code> signal
(<code>SIGABRT</code>) to the
running process. By default, this signal causes the process to immediately
terminate. Note that this redundancy (i.e. Ctrl-\ doing the same as Ctrl-C)
gives us some better flexibility. We'll explain that later on.
</dd>
</dl>
<p></p>
<hr>
<font color="brown"><h3>Sending Signals From The Command Line</h3></font>
<p>
Another way of sending signals to processes is done using various
commands, usually internal to the shell:
</p>
<p>
</p><dl>
<dt><code><u>kill</u></code>
</dt><dd>
The kill command accepts two parameters: a signal name (or number), and a
process ID. Usually the syntax for using it goes something like:
<br>
<pre><code>
kill -<signal> <PID>
</code></pre>
<br>
For example, in order to send the <code>INT</code> signal to process with
PID 5342, type:
<br><br>
<code>
kill -INT 5342
</code>
<br><br>
This has the same affect as pressing Ctrl-C in the shell that runs
that process.<br>
If no signal name or number is specified, the default is to send a
<code>TERM</code> signal to the process, which normally causes its
termination, and hence the name of the <code>kill</code> command.
</dd>
<br><br>
<dt><code><u>fg</u></code>
</dt><dd>
On most shells, using the <code>'fg'</code> command will resume execution
of the process (that was suspended with Ctrl-Z), by sending it a
<code>CONT</code> signal.
</dd>
</dl>
<p></p>
<hr>
<font color="brown"><h3>Sending Signals Using System Calls</h3></font>
<p>
A third way of sending signals to processes is by using the kill system
call. This is the normal way of sending a signal from one process to another.
This system call is also used by the <code>'kill'</code> command or by the
<code>'fg'</code> command.
Here is an example code that causes a process to suspend its own execution
by sending itself the <code>STOP</code> signal:
</p><pre><code>
#include <unistd.h> <font color="brown">/* standard unix functions, like getpid() */</font>
#include <sys/types.h> <font color="brown">/* various type definitions, like pid_t */</font>
#include <signal.h> <font color="brown">/* signal name macros, and the kill() prototype */</font>
<font color="brown">/* first, find my own process ID */</font>
pid_t my_pid = getpid();
<font color="brown">/* now that i got my PID, send myself the STOP signal. */</font>
kill(my_pid, SIGSTOP);
</code></pre>
An example of a situation when this code might prove useful, is inside a
signal handler that catches the TSTP signal (Ctrl-Z, remember?) in order
to do various tasks before actually suspending the process. We will see
an example of this later on.
<p></p>
<hr size="4">
<a name="catching">
<font color="brown"><h2>Catching Signals - Signal Handlers</h2></font>
</a>
<hr>
<font color="brown"><h3>Catchable And Non-Catchable Signals</h3></font>
<p>
Most signals may be caught by the process, but there are a few signals
that the process cannot catch, and cause the process to terminate. For example,
the <code>KILL</code> signal (<code>-9</code> on all unices I've met so far)
is such a signal. This is why you usually see a process being shut down using
this signal if it gets "wild". One process that uses this signal is a system
shutdown process. It first sends a <code>TERM</code> signal to all processes,
waits a while, and after allowing them a "grace period" to shut down cleanly,
it kills whichever are left using the <code>KILL</code> signal.
</p>
<p>
<code>STOP</code> is also a signal that a process cannot catch, and forces
the process's suspension immediately. This is useful when debugging programs
whose behavior depends on timing. Suppose that process A needs to send
some data to process B, and you want to check some system parameters after
the message is sent, but before it is received and processed by process B.
One way to do that would be to send a <code>STOP</code> signal to process B,
thus causing its suspension, and then running process A and waiting until
it sends its oh-so important message to process B. Now you can check whatever
you want to, and later on you can use the <code>CONT</code> signal to continue
process B's execution, which will then receive and process the message sent from
process A.
</p>
<p>
Now, many other signals are catchable, and this includes the famous
<code>SEGV</code> and <code>BUS</code> signals. You probably have seen
numerous occasions when a program has exited with a message such as
<i>'Segmentation Violation - Core Dumped'</i>, or
<i>'Bus Error - core dumped'</i>. In the first occasion, a <code>SEGV</code>
signal was
sent to your program due to accessing an illegal memory address. In the
second case, a <code>BUS</code> signal was sent to your program, due to
accessing a memory address with invalid alignment. In both cases, it is
possible to catch these signals in order to do some cleanup - kill child
processes, perhaps remove temporary files, etc. Although in both cases,
the memory used by your process is most likely corrupt, it's probable that
only a small part of it was corrupt, so cleanup is still usually possible.
</p>
<hr>
<font color="brown"><h3>Default Signal Handlers</h3></font>
<p>
If you install no signal handlers of your own (remember what a signal handler
is? yes, that function handling a signal?), the runtime environment sets up
a set of default signal handlers for your program. For example, the default
signal handler for the <code>TERM</code> signal calls the
<code>exit()</code> system call. The default handler for the <code>ABRT</code>
signal calls the <code>abort()</code> system call, which causes the
process's memory image to be dumped into a file named 'core' in the process's
current directory, and then exit.
</p>
<hr size="4">
<a name="installing_handlers">
<font color="brown"><h2>Installing Signal Handlers</h2></font>
</a>
<p>
There are several ways to install signal handlers. We'll use the most
basic form here, and refer you to your manual pages for further reading.
</p>
<hr>
<font color="brown"><h3>The <code>signal()</code> System Call</h3></font>
<p>
The <code>signal()</code> system call is used to set a signal handler for a
single signal type. <code>signal()</code> accepts a signal number and a
pointer to a signal handler function, and sets that handler to accept the
given signal. As an example, here is a code snippest that causes the program
to print the string "Don't do that" when a user presses Ctrl-C:
</p>
<hr width="40%">
<pre><code>
#include <stdio.h> <font color="brown">/* standard I/O functions */</font>
#include <unistd.h> <font color="brown">/* standard unix functions, like getpid() */</font>
#include <sys/types.h> <font color="brown">/* various type definitions, like pid_t */</font>
#include <signal.h> <font color="brown">/* signal name macros, and the signal() prototype */</font>
<font color="brown">/* first, here is the signal handler */</font>
void catch_int(int sig_num)
{
<font color="brown">/* re-set the signal handler again to catch_int, for next time */</font>
signal(SIGINT, catch_int);
<font color="brown">/* and print the message */</font>
printf("Don't do that");
fflush(stdout);
}
.
.
.
<font color="brown">/* and somewhere later in the code.... */</font>
.
.
<font color="brown">/* set the INT (Ctrl-C) signal handler to 'catch_int' */</font>
signal(SIGINT, catch_int);
<font color="brown">/* now, lets get into an infinite loop of doing nothing. */</font>
for ( ;; )
pause();
</code></pre>
<hr width="40%">
<p>
The complete source code for this program is found in the
<a href="http://titania.ctie.monash.edu.au/signals/catch-ctrl-c.c">catch-ctrl-c.c</a> file.
</p>
<p>
<em>Notes:</em>
</p><ul>
<li> the <code>pause()</code> system call causes the process to
halt execution, until a signal is received. it is surely better then a
'busy wait' infinite loop.
</li><li> the name of a function in C/C++ is actually a pointer
to the function, so when you're asked to supply a pointer to a function,
you may simply specify its name instead.
</li><li> On some systems (such as Linux), when a signal handler is called,
the system automatically resets the signal handler for that signal to
the default handler. Thus, we re-assign the signal handler immediately
when entering the handler function. Otherwise, the next time this
signal is received, the process will exit (default behavior for
<code>INT</code> signals). Even on systems that do not behave in this way,
it still won't hurt, so adding this line always is a good idea.
</li></ul>
<p></p>
<hr>
<font color="brown"><h3>Pre-defined Signal Handlers</h3></font>
<p>
For our convenience, there are two pre-defined signal handler functions
that we can use, instead of writing our own: <code>SIG_IGN</code> and
<code>SIG_DFL</code>.
</p><dl>
<dt> <code><u>SIG_IGN</u></code>:
</dt><dd>
Causes the process to ignore the specified signal. For example, in order
to ignore Ctrl-C completely (useful for programs that must NOT be
interrupted in the middle, or in critical sections), write this:
<br><br>
<code>signal(SIGINT, SIG_IGN);</code>
<br><br>
</dd>
<dt> <code><u>SIG_DFL</u></code>:
</dt><dd>
Causes the system to set the default signal handler for the given signal
(i.e. the same handler the system would have assigned for the signal when
the process started running):
<br><br>
<code>signal(SIGTSTP, SIG_DFL);</code>
<br><br>
</dd>
</dl>
<p></p>
<hr size="4">
<a name="masking">
<font color="brown"><h2>Avoiding Signal Races - Masking Signals</h2></font>
</a>
<p>
One of the nasty problems that might occur when handling a signal, is
the occurrence of a second signal while the signal handler function executes.
Such a signal might be of a different type then the one being handled, or
even of the same type. Thus, we should take some precautions inside the signal
handler function, to avoid races.
</p>
<p>Luckily, the system also contains some features that will allow us to
block signals from being processed. These can be used in two 'contexts' -
a global context which affects all signal handlers, or a per-signal type
context - that only affects the signal handler for a specific signal type.
</p>
<hr>
<font color="brown"><h3>Masking signals with sigprocmask()</h3></font>
<p>
the (modern) "POSIX" function used to mask signals in the global context,
is the <code>sigprocmask()</code> system call. It allows us to specify a set
of signals to block, and returns the list of signals that were previously
blocked. This is useful when we'll want to restore the previous masking
state once we're done with our critical section. <code>sigprocmask()</code>
accepts 3 parameters:
<br><br>
</p><dl>
<dt><code>int how</code>
</dt><dd>defines if we want to add signals to the current
mask (<code>SIG_BLOCK</code>), remove them from the current mask
(<code>SIG_UNBLOCK</code>),
or completely replace the current mask with the new mask
(<code>SIG_SETMASK</code>).
</dd>
<dt><code>const sigset_t *set</code>
</dt><dd>The set of signals to be blocked, or to be added to the current mask, or
removed from the current mask (depending on the 'how' parameter).
</dd>
<dt><code>sigset_t *oldset</code>
</dt><dd>If this parameter is not <code>NULL</code>, then it'll contain the
previous mask. We can later use this set to restore the situation
back to how it was before we called <code>sigprocmask()</code>.
</dd>
</dl>
<br><br>
<em>Note: Older systems do not support the <code>sigprocmask()</code>
system call. Instead, one should use the <code>sigmask()</code> and
<code>sigsetmask()</code> system calls. If you have
such an operating system handy, please read the manual pages for these
system calls. They are simpler to use then sigprocmask, so it shouldn't
be too hard understanding them once you've read this section.
</em>
<p></p>
<p>
You probably wonder what are these <code>sigset_t</code> variables, and how
they are manipulated. Well, i wondered too, so i went to the manual page of
<code>sigsetops</code>, and found the answer. There are several functions to
handle these sets. Lets learn them using some example code:
</p>
<hr width="40%">
<pre><code>
<font color="brown">/* define a new mask set */</font>
sigset_t mask_set;
<font color="brown">/* first clear the set (i.e. make it contain no signal numbers) */</font>
sigemptyset(&mask_set);
<font color="brown">/* lets add the TSTP and INT signals to our mask set */</font>
sigaddset(&mask_set, SIGTSTP);
sigaddset(&mask_set, SIGINT);
<font color="brown">/* and just for fun, lets remove the TSTP signal from the set. */</font>
sigdelset(&mask_set, SIGTSTP);
<font color="brown">/* finally, lets check if the INT signal is defined in our set */</font>
if (sigismember(&mask_set, SIGINT)
printf("signal INT is in our set\n");
else
printf("signal INT is not in our set - how strange...\n");
<font color="brown">/* finally, lets make the set contain ALL signals available on our system */</font>
sigfillset(&mask_set)
</code></pre>
<hr width="40%">
<p>
Now that we know all these little secrets, lets see a short code example
that counts the number of Ctrl-C signals a user has hit, and on the 5th time
(note - this number was "Stolen" from some quite famous Unix program) asks
the user if they really want to exit. Further more, if the user hits Ctrl-Z,
the number of Ctrl-C presses is printed on the screen.
</p>
<hr width="40%">
<pre><code>
<font color="brown">/* first, define the Ctrl-C counter, initialize it with zero. */</font>
int ctrl_c_count = 0;
#define CTRL_C_THRESHOLD 5
<font color="brown">/* the Ctrl-C signal handler */</font>
void catch_int(int sig_num)
{
sigset_t mask_set; <font color="brown">/* used to set a signal masking set. */</font>
sigset_t old_set; <font color="brown">/* used to store the old mask set. */</font>
<font color="brown">/* re-set the signal handler again to catch_int, for next time */</font>
signal(SIGINT, catch_int);
<font color="brown">/* mask any further signals while we're inside the handler. */</font>
sigfillset(&mask_set);
sigprocmask(SIG_SETMASK, &mask_set, &old_set);
<font color="brown">/* increase count, and check if threshold was reached */</font>
ctrl_c_count++;
if (ctrl_c_count >= CTRL_C_THRESHOLD) {
char answer[30];
<font color="brown">/* prompt the user to tell us if to really exit or not */</font>
printf("\nRealy Exit? [y/N]: ");
fflush(stdout);
gets(answer);
if (answer[0] == 'y' || answer[0] == 'Y') {
printf("\nExiting...\n");
fflush(stdout);
exit(0);
}
else {
printf("\nContinuing\n");
fflush(stdout);
<font color="brown">/* reset Ctrl-C counter */</font>
ctrl_c_count = 0;
}
}
<font color="brown">/* restore the old signal mask */{{/COMMENT_FONT}*/</font>
sigprocmask(SIG_SETMASK, &old_set, NULL);
}
<font color="brown">/* the Ctrl-Z signal handler */</font>
void catch_suspend(int sig_num)
{
sigset_t mask_set; <font color="brown">/* used to set a signal masking set. */</font>
sigset_t old_set; <font color="brown">/* used to store the old mask set. */</font>
<font color="brown">/* re-set the signal handler again to catch_suspend, for next time */</font>
signal(SIGTSTP, catch_suspend);
<font color="brown">/* mask any further signals while we're inside the handler. */</font>
sigfillset(&mask_set);
sigprocmask(SIG_SETMASK, &mask_set, &old_set);
<font color="brown">/* print the current Ctrl-C counter */</font>
printf("\n\nSo far, '%d' Ctrl-C presses were counted\n\n", ctrl_c_count);
fflush(stdout);
<font color="brown">/* restore the old signal mask */</font>
sigprocmask(SIG_SETMASK, &old_set, NULL);
}
.
.
<font color="brown">/* and somewhere inside the main function... */</font>
.
.
<font color="brown">/* set the Ctrl-C and Ctrl-Z signal handlers */</font>
signal(SIGINT, catch_int);
signal(SIGTSTP, catch_suspend);
.
.
<font color="brown">/* and then the rest of the program */</font>
.
.
</code></pre>
<hr width="40%">
<p>
The complete source code for this program is found in the
<a href="http://titania.ctie.monash.edu.au/signals/count-ctrl-c.c">count-ctrl-c.c</a> file.
</p>
<p>
You should note that using <code>sigprocmask()</code> the way we did now
does not resolve all possible race conditions. For example, It is possible
that after we entered the signal handler, but before we managed to call the
<code>sigprocmask()</code> system call, we receive another signal, which WILL
be called. Thus, if the user is VERY quick (or the system is very slow),
it is possible to get into races. In our current functions, this will
probably not disturb the flow, but there might be cases where this kind
of race could cause problems.
</p>
<p>
The way to guarantee no races at all, is to
let the system set the signal masking for us before it calls the signal
handler. This can be done if we use the <code>sigaction()</code> system
call to define both the signal handler function AND the signal mask to be used
when the handler is executed. You would probably be able to read the manual
page for <code>sigaction()</code> on your own, now that you're familiar with
the various concepts of signal handling. On old systems, however, you won't
find this system call, but you still might find the <code>sigvec()</code>
call, that enables a similar functionality.
</p>
<hr size="4">
<a name="timers">
<font color="brown"><h2>Implementing Timers Using Signals</h2></font>
</a>
<p>
One of the weak aspects of Unix-like operating systems is their lack of
proper support for timers. Timers are important to allow one to check timeouts
(e.g. wait for user input up to 30 seconds, or exit), check some conditions
on a regular basis (e.g. check every 30 seconds that a server we're talking to
is still active, or close the connection and notify the user about the problem),
and so on. There are various ways to get around the problem for programs that
use an "event loop" based on the <code>select()</code> system call (or its
new replacement, the <code>poll()</code> system call), but not all programs
work that way, and this method is too complex for short and simple programs.
</p>
<p>
Yet, the operating system gives us a simple way of setting up timers that
don't require too much hassles, by using special alarm signals. They are
generally limited to one timer active at a time, but that will suffice in
simple cases.
</p>
<hr>
<font color="brown"><h3>The <code>alarm()</code> System Call</h3></font>
<p>
The <code>alarm()</code> system call is used to ask the system to send our
process a special signal, named <code>ALRM</code>, after a given number of
seconds. Since Unix-like systems don't operate as real-time systems,
your process might receive this signal after a longer time then requested.
Combining this system call with a proper signal handler enables us to
do some simple tricks. Lets see an example of a program that waits for
user input, but exits if none was given after a certain timeout.
</p>
<hr width="40%">
<pre><code>
#include <unistd.h> <font color="brown">/* standard unix functions, like alarm() */</font>
#include <signal.h> <font color="brown">/* signal name macros, and the signal() prototype */</font>
char user[40]; <font color="brown">/* buffer to read user name from the user */</font>
<font color="brown">/* define an alarm signal handler. */</font>
void catch_alarm(int sig_num)
{
printf("Operation timed out. Exiting...\n\n");
exit(0);
}
.
.
<font color="brown">/* and inside the main program... */</font>
.
.
<font color="brown">/* set a signal handler for ALRM signals */</font>
signal(SIGALRM, catch_alarm);
<font color="brown">/* prompt the user for input */</font>
printf("Username: ");
fflush(stdout);
<font color="brown">/* start a 30 seconds alarm */</font>
alarm(30);
<font color="brown">/* wait for user input */</font>
gets(user);
<font color="brown">/* remove the timer, now that we've got the user's input */</font>
alarm(0);
.
.
<font color="brown">/* do something with the received user name */</font>
.
.
</code></pre>
<hr width="40%">
<p>
The complete source code for this program is found in the
<a href="http://titania.ctie.monash.edu.au/signals/use-alarms.c">use-alarms.c</a> file.
</p>
<p>
As you can see, we start the timer right before waiting for user input.
If we started it earlier, we'll be giving the user less time then promised
to perform the operation. We also stop the timer right after getting the
user's input, before doing any tests or processing of the input, to avoid
a random timer from setting off due to slow processing of the input. Many
bugs that occur using the <code>alarm()</code> system call occur due to
forgetting to set off the timer at various places when the code gets
complicated. If you need to make some tests during the input phase,
put the whole piece of code in a function, so it'll be easier to make sure
that the timer is always set off after calling the function. Here is an
example of how NOT to do this:
</p>
<hr width="40%">
<pre><code>
int read_user_name(char user[])
{
int ok_len;
int result = 0; <font color="brown">/* assume failure */</font>
<font color="brown">/* set an alarm to timeout the input operation */</font>
alarm(3); <font color="brown">/* <a href="#mis_1">Mistake 1</a> */</font>
printf("Enter user name: "); <font color="brown">/* <a href="#mis_2">Mistake 2</a> */</font>
fflush(stdout);
if (gets(user) == NULL) {
printf("End of input received.\n");
return 0; <font color="brown">/* <a href="#mis_3">Mistake 3</a> */</font>
}
<font color="brown">/* count the size of prefix of 'user' made only of characters */</font>
<font color="brown">/* in the given set. if all characters in 'user' are in the */</font>
<font color="brown">/* set, then ok_len with be equal to the length of 'user'. */</font>
ok_len = strspn(user, "abcdefghijklmnopqrstuvwxyz0123456789");
if (ok_len == strlen(user)) {
<font color="brown">/* check if the user exists in our database */</font>
result = find_user_in_database(user); <font color="brown">/* <a href="#mis_4">Mistake 4</a> */</font>
}
alarm(0);
return result;
}
</code></pre>
<hr width="40%">
<p>
Lets count the mistakes and bad programming practices in the above function:
</p><ol>
<li><a name="mis_1">*</a> <u>Too short timeout</u>:
<p>
3 seconds might be enough for superman to type his user name,
but a normal user obviously needs more time. Such a timeout should
actually be tunable, because not all people type at the same pace,
or should be long enough for even the slowest of users.
</p>
</li><li><a name="mis_2">*</a> <u>Printing while timer is ticking</u>:
<p>
Norty Norty. This printing should have been done before starting up
the timer. Printing may be a time consuming operation, and thus will leave
less time then expected for the user to type in the expected input.
</p>
</li><li><a name="mis_3">*</a> <u>Exiting function without turning off timer</u>:
<p>
This kind of mistake is hard to catch. It will cause the program to
randomally exit somewhere LATER during the execution. If we'll trace it with
a debugger, we'll see that the signal was received while we were already
executing a completely different part of the program, leaving us scratching
our head and looking up to the sky, hoping somehow inspiration will fall
from there to guide us to the location of the problem.
</p>
</li><li><a name="mis_4">*</a> <u>Making long checks before turning off timer</u>:
<p>
As if it's not enough that we gave the poor user a short time to check
for the input, we're also inserting some database checking operation
while the timer is still ticking. Even if we also want to timeout
the database operation, we should probably set up a different timer
(and a different <code>ALRM</code> signal handler), so as not to confuse
a slow user with a slow database server. It will also allow the user to
know <em>why</em> we are timing out. Without this information, a person
trying to figure out why the program suddenly exits, will have hard time
finding where the fault lies.
</p>
</li></ol>
<p></p>
<hr size="4">
<a name="do_dont">
<font color="brown"><h2>Summary - "Do" and "Don't" inside A Signal Handler</h2></font>
</a>
<p>
We have seen a few "thumb rules" all over this tutorial, and there are quite
many of those, as the area of signals handling is rather tricky. Lets try
to summarize these rules of thumb here:
</p>
<ul>
<li> <u>Make it short</u> - the signal handler should be a short function that
returns quickly. Instead of doing complex operations inside the signal
handler, it is better that the function will raise a flag (e.g.
a global variable, although these are evil by themselves) and have
the main program check that flag occasionally.
</li><li> <u>Proper Signal Masking</u> - don't be too lazy to define proper signal
masking for a signal handler, preferably using the
<code>sigaction()</code> system call. It takes a little more effort
then just using the <code>signal()</code> system call, but it'll help
you sleep better at night, knowing that you haven't left an extra place
for race conditions to occur. Remember - if some bug has a probability
of 1/10,000 to occur, it WILL occur when many people use that program
many times, as tends to be the case with good programs (you write only
good programs, no?).
</li><li> <u>Careful with "fault" signals</u> - If you catch signals that indicate
a program bug (<code>SIGBUS</code>, <code>SIGSEGV</code>,
<code>SIGFPE</code>), don't try to be too smart and
let the program continue, unless you know exactly what you are doing
(which is a very rare case) - just do the minimal required cleanup,
and exit, preferably with a core dump (using the <code>abort()</code>
function). Such signals usually indicate a bug in the program, that if
ignored will most likely cause it to crush sooner or later, making
you think the problem is somewhere else in the code.
</li><li> <u>Careful with timers</u> - when you use timers, remember that you
can only use one timer at a time, unless you also (ab)use the
<code>VTALRM</code>
signal. If you need to have more then one timer active at a time,
don't use signals, or devise a set of functions that will allow you
to have several virtual timers using a delta list of some sort. If
you've no idea what I'm talking about, you probably don't need several
simultaneous timers in the first place.
</li><li> <u>Signals are NOT an event driven framework</u> - it is easy to get
carried away and try turning the signals system into an event-driven
driver for a program, but signal handling functions were not meant for
that. If you need such a thing, use some framework that is more
suitable for the application (e.g. use an event loop of a windowing
program, use a select-based loop inside a network server, etc.).
</li></ul>
</body></html>