Как я могу отследить смерть дочернего процесса, не заставляя родительский процесс ждать, пока дочерний процесс не будет убит?
Я пытаюсь сценарий клиент-сервер, где сервер принимает соединение от клиента и разветвляется новый процесс для каждого соединения, которое он принимает.
Я игнорирую сигналы SIGCHLD, чтобы предотвратить создание зомби.
signal(SIGCHLD, SIG_IGN);
while(1)
{
accept();
clients++;
if(fork() ==0)
{
childfunction();
clients--;
}
else
{
}
}
Проблема в приведенном выше сценарии заключается в том, что если дочерний процесс будет убит в функции childfunction ()
, глобальная переменная клиентов
не уменьшается.
ПРИМЕЧАНИЕ: Я ищу решение без использования сигнала SIGCHLD ... Если возможно
Обычно вы пишете обработчик для SIGCHLD
, который вызывает waitpid ()
для pid -1
. Вы можете использовать возвращаемое значение, чтобы определить, какой pid умер. Например:
void my_sigchld_handler(int sig)
{
pid_t p;
int status;
while ((p=waitpid(-1, &status, WNOHANG)) != -1)
{
/* Handle the death of pid p */
}
}
/* It's better to use sigaction() over signal(). You won't run into the
* issue where BSD signal() acts one way and Linux or SysV acts another. */
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = my_sigchld_handler;
sigaction(SIGCHLD, &sa, NULL);
В качестве альтернативы вы можете вызвать waitpid (pid, & status, 0)
с указанным идентификатором дочернего процесса и синхронно дождаться его смерти. Или используйте WNOHANG
, чтобы проверить его статус без блокировки.
Вам не нужен зомби. Если дочерний процесс умирает, а родительский все еще RUNNING, но не выдает wait()
/waitpid()
вызов для сбора статуса, система не освобождает ресурсы, связанные с дочерним процессом, и в таблице proc остается зомби/неработающий процесс.
Попробуйте изменить ваш обработчик SIGCHLD
на что-то более близкое к следующему:
void chld_handler(int sig) {
pid_t p;
int status;
/* loop as long as there are children to process */
while (1) {
/* retrieve child process ID (if any) */
p = waitpid(-1, &status, WNOHANG);
/* check for conditions causing the loop to terminate */
if (p == -1) {
/* continue on interruption (EINTR) */
if (errno == EINTR) {
continue;
}
/* break on anything else (EINVAL or ECHILD according to manpage) */
break;
}
else if (p == 0) {
/* no more children to process, so break */
break;
}
/* valid child process ID retrieved, process accordingly */
...
}
}
Вы можете опционально маскировать/блокировать дополнительные SIGCHLD
сигналы во время выполнения обработчика сигнала, используя sigprocmask()
. Заблокированная маска должна быть возвращена в исходное значение после завершения работы обработчика сигналов.
Если вы действительно не хотите использовать обработчик SIGCHLD
, вы можете попробовать добавить цикл обработки дочерних событий куда-нибудь, где он будет регулярно вызываться и опрашивать о завершенных дочерних событиях.
Переменная «клиенты» находится в разных адресных пространствах процесса после fork (), и когда вы уменьшаете переменную в дочернем элементе, это не повлияет на значение в родительском элементе. Я думаю, вам нужно обработать SIGCHLD, чтобы правильно обрабатывать счет.