Какова идея использования сигналов здесь?
Читая и изучая сигналы, я обнаружил программу, которая использует сигналы определенным образом. Я пытался понять это, но я не уверен, как все части кода взаимодействуют с другой.
Ниже приведен вышеупомянутый код, и я добавил комментарии, где у меня возникли трудности:
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define CP 5
static volatile int curprocs =0; ;
static void die() {
exit(EXIT_FAILURE);
}
static void chldhandler() {
int e = errno;
// Why do we use waitpid here? What does it do?
while(waitpid(-1, NULL, WNOHANG) > 0) {
curprocs--;
}
errno = e;
}
void do_work() {
time_t t;
srand((unsigned) time(&t));
sleep(5+ rand() % 4);
}
int main() {
struct sigaction sa = {
.sa_handler = chldhandler,
.sa_flags = SA_RESTART,
};
sigemptyset(&sa.sa_mask);
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
exit(-1);
}
while(1) {
sigset_t chld, empty;
sigemptyset(&empty);
sigemptyset(&chld);
sigaddset(&chld, SIGCHLD);
// What do the following lines of code do??
sigprocmask(SIG_BLOCK, &chld, NULL);
while (curprocs >= CP) { // cap for the number of child processes
sigsuspend(&empty);
}
curprocs++;
sigprocmask(SIG_UNBLOCK, &chld, NULL);
pid_t p = fork();
if (p == -1) {
return -1;
}
if (p == 0) {
// code for the child processes to execute
do_work();
die();
} else {
// Parent process does nothing
}
}
return 0;
}
Очевидно, что вышеприведенная программа должна иметь максимальное количество 42 дочерних процессов, выполняющих работу. Всякий раз, когда мы хотим иметь новый дочерний процесс, мы используем fork и настраиваем curprocs
, Всякий раз, когда дочерний процесс завершается, вызывается chldhandler() и curprocs
также корректируется.
Однако я не понимаю взаимодействие двух sigproc_mask, sigsuspend, waitpid
и наши двое signalsets chld, empty
,
Может кто-нибудь объяснить, что делают эти строки или почему они используются такими, какие они есть?
1 ответ
sigprocmask(SIG_BLOCK, &chld, NULL);
блоки SIGCHLD
так что вы можете быть уверены, что пока вы делаете while (curprocs >= 42)
SIGCHLD
обработчик не будет прерывать код, изменяя curprocs
в середине проверки.
sigsuspends
атомно разблокирует его и ждет SIGCHLD
(любой сигнал на самом деле, так как вы передаете пустую маску), атомно блокируя его, когда он возвращается.
waitpid(-1,/*...*/)
в обработчике получает статус любого (это означает, что -1) дочернего элемента, который ожидает изменения статуса (обычно уведомление о завершении), так что данные, которые ядро связывает с изменением статуса, могут быть освобождены. Вторым аргументом будет то, куда будет отправляться информация об изменении статуса, но так как вы прошли NULL
, информация будет просто отброшена. WNOHANG
означает не ждать, если больше нет уведомлений об изменении статуса.
Поскольку обработчик запускается в ответ на SIGCHLD
должно быть хотя бы одно уведомление об изменении статуса, но может быть больше, потому что SIGCHLD
s может объединиться (также возможно, что нет - если вы звонили waitpid
из нормального контекста в то время как SIGCHLD
был заблокирован). Вот почему обработчик зацикливается. WNOHANG
важно, потому что без него, после того, как все уведомления об изменении статуса были получены, waitpid
вызов заблокирует ваш процесс до получения нового уведомления.