Массив потомков отслеживает ID порожденных процессов. Если элемент содержит NOT_USED
, он не представляет необработанного потомка. (Его инициализируют строки 89–90 внизу) nkids
указывает, сколько значений в kids
следует проверить.
16 /* format_num --- вспомогательная функция, поскольку нельзя использовать [sf]printf() */
17
18 const char *format_num(int num)
19 {
20 #define NUMSIZ 30
21 static char buf[NUMSIZ];
22 int i;
23
24 if (num <= 0) {
25 strcpy(buf, "0");
26 return buf;
27 }
28
29 i = NUMSIZ - 1;
30 buf[i--] = '\0';
31
32 /* Преобразует цифры обратно в строку. */
33 do {
34 buf[i--] = (num % 10) + '0';
35 num /= 10;
36 } while (num > 0);
37
38 return &buf[i+1];
39 }
Поскольку обработчики сигналов не должны вызывать функции семейства printf()
, мы предусмотрели для преобразования десятичного сигнала или номера PID в строку простую «вспомогательную» функцию format_num()
. Это примитивно, но работает.
41 /* childhandler --- перехват SIGCHLD, сбор сведений со всех доступных потомков */
42
43 void childhandler(int sig)
44 {
45 int status, ret;
46 int i;
47 char buf[100];
48 static const char entered[] = "Entered childhandler\n" ;
49 static const char exited[] = "Exited childhandler\n";
50
51 writed, entered, strlen(entered));
52 for (i =0; i < nkids; i++) {
53 if (kids[i] == NOT_USED)
54 continue;
55
56 retry:
57 if ((ret = waitpid(kids[i], &status, WNOHANG)) == kids[i]) {
58 strcpy(buf, "\treaped process ");
59 strcat(buf, format_num(ret));
60 strcat(buf, "\n");
61 write(1, buf, strlen(buf));
62 kids[i] = NOT_USED;
63 } else if (ret == 0) {
64 strcpy(buf, "\tpid ");
65 strcat(buf, format_num(kids[i]));
66 strcat(buf, " not available yet\n");
67 write(1, buf, strlen(buf));
68 } else if (ret == -1 && errno == EINTR) {
69 write(1, "\tretrying\n", 10);
70 goto retry;
71 } else {
72 strcpy(buf, "\twaitpid() failed: ");
73 strcat(buf, strerror(errno));
74 strcat(buf, "\n");
75 write(1, buf, strlen(buf));
76 }
77 }
78 write(1, exited, strlen(exited));
79 }
Строки 51 и 58 выводят «входное» и «завершающее» сообщения, так что мы можем ясно видеть, когда вызывается обработчик сигнала. Другие сообщения начинаются с ведущего символа TAB.
Главной частью обработчика сигнала является большой цикл, строки 52–77. Строки 53–54 проверяют на NOT_USED
и продолжают цикл, если текущий слот не используется.
Строка 57 вызывает waitpid()
с PID текущего элемента kids
. Мы предусмотрели опцию WNOHANG
, которая заставляет waitpid()
возвращаться немедленно, если затребованный потомок недоступен. Этот вызов необходим, так как возможно, что не все потомки завершились.
Основываясь на возвращенном значении, код предпринимает соответствующее действие. Строки 57–62 обрабатывают случай обнаружения потомка, выводя сообщение и помещая в соответствующий слот в kids
значение NOT_USED
.
Строки 63–67 обрабатывают случай, когда затребованный потомок недоступен. В этом случае возвращается значение 0, поэтому выводится сообщение, и выполнение продолжается.
Строки 68–70 обрабатывают случай, при котором был прерван системный вызов. В этом случае самым подходящим способом обработки является goto
обратно на вызов waitpid()
. (Поскольку main()
блокирует все сигналы при вызове обработчика сигнала [строка 96], это прерывание не должно случиться. Но этот пример показывает, как обработать все случаи.)