The program prompts for input lines in a loop until it receives an error or end of file on stdin. On each line, the first nonblank token is interpreted as the number of seconds to wait, and the rest of the line (up to 64 characters) is a message that will be printed when the wait completes. I will offer two additional versions— one using multiple processes, and one using multiple threads. We'll use the three examples to compare the approaches.
1 Include the header file errors.h, which includes standard headers like
9-26 The "baseline" version, alarm.c, is a synchronous alarm program with a single routine, main. Most of main is a loop, which processes simple commands until fgets returns a NULL (error or end of file). Each line is "parsed" with sscanf to separate the number of seconds to wait (%d, the first sequence of digits) from the message string to print (%64 [^\n], the rest of the line, up to 64 characters excluding newline).
■ alarm.c
1 #include "errors.h" 2
3 int main (int argc, char *argv[])
4 {
5 int seconds;
6 char line[128];
7 char message[64]; 8
9 while (1) {
10 printf ("Alarm> ");
11 if (fgets (line, sizeof (line), stdin) == NULL) exit (0);
12 if (strlen (line) <= 1) continue; 13
14 /*
15 * Parse input line into seconds (%d) and a message
16 * (%64[^\n]), consisting of up to 64 characters
17 * separated from the seconds by whitespace.
18 */
19 if (sscanf (line, "%d %64[^\n]",
20 &seconds, message) < 2) {
21 fprintf (stderr, "Bad command\n");
22 } else {
23 sleep (seconds);
24 printf ("(%d) %s\n", seconds, message);
25 }
26 }
27 }
The problem with the program alarm.c is that only one alarm request can be active at a time. If you set an alarm to remind you to do something in 10 minutes (600 seconds), you can't decide to have it remind you of something else in 5 minutes. The program is doing something synchronously that you would probably like to be asynchronous.
There are lots of ways to make this program asynchronous; for example, you could run more than one copy of the program. One way to run multiple copies is to fork a child process for each command, as shown in alarm_fork.c. The new version is asynchronous—you can enter commands at any time, and they will be carried out independently. It isn't much more complicated than the original, which is nice.
27-37 The main difference between alarm.c and alarm_fork.c is that instead of calling sleep directly, it uses fork to create a new child process, which then calls sleep (and, eventually, printf)
42-46 The primary complication in this version is the need to "reap" any child processes that have terminated. If the program fails to do this, the system will save them all until the program terminates. The normal way to reap terminated child processes is to call one of the wait functions. In this case, we call waitpid, which allows the caller to specify the WNOHANG flag. The function will immediately reap one child process if any have terminated, or will immediately return with a process ID (pid) of 0. The parent process continues to reap terminated child processes until there are no more to reap. When the loop terminates, main loops back to line 13 to read a new command.
■ alarm_fork.c
1 #include
2 #include
3 #include "errors.h"
5 int main (int argc, char *argv[])
6 {
7 int status;
8 char line[128];
9 int seconds;
10 pid_t pid;
11 char message[64]; 12
13 while (1) {
14 printf ("Alarm> " ) ;
15 if (fgets (line, sizeof (line), stdin) == NULL) exit (0);