Linux программирование в примерах
Строки 99–100 создают набор сигналов, представляющих
, а строка 102 устанавливает их в качестве маски сигналов процесса для программы.SIGCHLDСтроки 104–109 создают пять порожденных процессов, каждый из которых засыпает на три секунды. По ходу дела они обновляют массив
и переменнуюkids.nkidsСтрока 111 дает затем потомкам шанс завершиться, заснув на еще больший промежуток времени. (Это не гарантирует, что порожденные процессы завершатся, но шансы довольно велики.)
Наконец, строки 113–114 выводят сообщение и приостанавливаются, заменив маску сигналов процесса, блокирующую
, пустой маской. Это дает возможность появиться сигналуSIGCHLD, что в свою очередь вызывает запуск обработчика сигнала. Вот что происходит:SIGCHLD$ <b>ch10-reap1</b> /* Запуск программы */waiting for signalEntered childhandlerreaped process 23937reaped process 23938reaped process 23939reaped process 23940reaped process 23941Exited childhandlerОбработчик сигнала собирает сведения о потомках за один проход.
Следующая программа,
, сходна сch10-reap2.c. Разница в том, что она допускает появление сигналаch10-reap1.cв любое время. Такое поведение увеличивает шанс получения более одногоSIGCHLD, но не гарантирует это. В результате обработчик сигнала все равно должен быть готов обработать в цикле несколько потомков.SIGCHLD1 /* ch10-reap2.c — демонстрирует управление SIGCHLD, один сигнал на потомка */2/* ...не изменившийся код пропущен... */1213 pid_t kids[MAX_KIDS];14 size_t nkids = 0;15 size_t kidsleft = 0; /* <<< Добавлено */16/* ...не изменившийся код пропущен... */4142 /* childhandler --- перехват SIGCHLD, опрос всех доступных потомков */4344 void childhandler(int sig)45 {46 int status, ret;47 int i;48 char buf[100];49 static const char entered[] = "Entered childhandler\n";50 static const char exited[] = "Exited childhandler\n";5152 write(1, entered, strlen(entered));53 for (i = 0; i < nkids; i++) {54 if (kids[i] == NOT_USED)55 continue;5657 retry:58 if ((ret = waitpid(kids[i], &status, WNOHANG)) == kids[i]) {59 strcpy(buf, "\treaped process ");60 strcat(buf, format_num(ret));61 strcat(buf, "\n");62 write(1, buf, strlen(buf));63 kids[i] = NOT_USED;64 kidsleft--; /* <<< Добавлено */65 } else if (ret == 0) {/* ...не изменившийся код пропущен... */80 write(1, exited, strlen(exited));81 }Это идентично предыдущей версии за тем исключением, что у нас есть новая переменная,
, указывающая, сколько имеется не опрошенных потомков. Строки 15 и 64 помечают новый код.kidsleft83 /* main --- установка относящейся к порожденным процессам сведенийи сигналов, создание порожденных процессов */8485 int main(int argc, char **argv)86 {/* ...не изменившийся код пропущен... */100101 sigemptyset(&childset);102 sigaddset(&childset, SIGCHLD);103104 /* sigprocmask(SIG_SETMASK, &childset, NULL); /* блокирование в коде main */105106 for (nkids = 0; nkids < 5; nkids++) {107 if ((kids[nkids] = fork()) == 0) {108 sleep(3);109 _exit(0);110 }111 kidsleft++; /* <<< Added */112 }113114 /* sleep(5); /* дать потомкам шанс завершиться */115116 while (kidsleft > 0) { /* <<< Добавлено */117 printf("waiting for signals\n");118 sigsuspend(&emptyset);119 } /* <<< Добавлено */120121 return 0;122 }Здесь код также почти идентичен. Строки 104 и 114 закомментированы из предыдущей версии, а строки 111, 116 и 119 добавлены. Удивительно, при запуске поведение меняется в зависимости от версии ядра!
$ <b>uname -a</b> /* Отобразить версию системы */Linux example1 2.4.20-8 #1 Thu Mar 13 17:54:28 EST 2003 i686 i686 i386 GNU/Linux