Linux программирование в примерах
41 /* childhandler --- перехват SIGCHLD, сбор сведений со всех доступных потомков */4243 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";5051 writed, entered, strlen(entered));52 for (i =0; i < nkids; i++) {53 if (kids[i] == NOT_USED)54 continue;5556 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 вызывает
с PID текущего элементаwaitpid(). Мы предусмотрели опциюkids, которая заставляетWNOHANGвозвращаться немедленно, если затребованный потомок недоступен. Этот вызов необходим, так как возможно, что не все потомки завершились.waitpid()Основываясь на возвращенном значении, код предпринимает соответствующее действие. Строки 57–62 обрабатывают случай обнаружения потомка, выводя сообщение и помещая в соответствующий слот в
значениеkids.NOT_USEDСтроки 63–67 обрабатывают случай, когда затребованный потомок недоступен. В этом случае возвращается значение 0, поэтому выводится сообщение, и выполнение продолжается.
Строки 68–70 обрабатывают случай, при котором был прерван системный вызов. В этом случае самым подходящим способом обработки является
обратно на вызовgoto. (Посколькуwaitpid()блокирует все сигналы при вызове обработчика сигнала [строка 96], это прерывание не должно случиться. Но этот пример показывает, как обработать все случаи.)main()Строки 71–76 обрабатывают любую другую ошибку, выводя соответствующее сообщение об ошибке.
81 /* main --- установка связанных с порожденными процессами сведений и сигналов, создание порожденных процессов */8283 int main(int argc, char **argv)84 {85 struct sigaction sa;86 sigset_t childset, emptyset;87 int i;8889 for (i = 0; i < nkids; i++)90 kids[i] = NOT_USED;9192 sigemptyset(&emptyset);9394 sa.sa_flags =SA_NOCLDSTOP;95 sa.sa_handler = childhandler;96 sigfillset(&sa.sa_mask); /* блокировать все при вызове обработчика */97 sigaction(SIGCHLD, &sa, NULL);9899 sigemptyset(&childset);100 sigaddset(&childset, SIGCHLD);101102 sigprocmask(SIG_SETMASK, &childset, NULL); /* блокировать его в коде main */103104 for (nkids = 0; nkids < 5; nkids++) {105 if ((kids[nkids] = fdrk()) == 0) {106 sleep(3);107 _exit(0);108 }109 }110111 sleep(5); /* дать потомкам возможность завершения */112113 printf("waiting for signal\n");114 sigsuspend(&emptyset);115116 return 0;117 }Строки 89–90 инициализируют
. Строка 92 инициализируетkids. Строки 94–97 настраивают и устанавливают обработчик сигнала дляemptyset. Обратите внимание на использование в строке 94SIGCHLD, тогда как строка 96 блокирует все сигналы при вызове обработчика.SA_NOCLDSTOP