2014-01-21 9 views
0

Я пишу модуль для инструментария, который должен выполнить некоторые подпроцессы и прочитать их вывод. Однако основная программа, использующая инструментарий, может также порождать некоторые подпроцессы и настроить обработчик сигнала для SIGCHLD, который вызывает wait(NULL), чтобы избавиться от процессов зомби. В результате, если подпроцесс я создаю выход внутри своего waitpid(), дочерний процесс обрабатывается до вызова обработчика сигнала, и поэтому wait() в обработчике сигнала будет ждать завершения следующего процесса (что может потребоваться навсегда). Такое поведение описано в the man page of waitpid (см. Грантополучатель 2), поскольку реализация linux, похоже, не позволяет семейству wait() обрабатывать SIGCHLD. Я пробовал popen() и posix_spawn(), и у обоих из них такая же проблема. Я также попытался использовать double fork(), так что прямой ребенок существует немедленно, но я все еще не могу оградить то, что waitpid() вызывается после получения SIGCHLD.Как безопасно `waitpid()` в плагине с обработчиком `SIGCHLD`, вызывающим` wait() `setup в основной программе

Мой вопрос в том, что если другая часть программы устанавливает обработчик сигнала, который вызывает wait() (возможно, он должен скорее позвонить waidpid, но это не то, что я могу контролировать), есть ли способ безопасно выполнять дочерние процессы без перезаписи обработчик SIGCHLD (поскольку он может быть полезен в некоторых программах) или любые процессы зомби.

Небольшая программа, которая показывает проблему здесь (Замечено, что основная программа единственный выход после долгосрочной перспективе ребенка выхода, вместо короткой, которая является то, что он непосредственно ждет с waitpid()):

#include <signal.h> 
#include <sys/wait.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <string.h> 

static void 
signalHandler(int sig) 
{ 
    printf("%s: %d\n", __func__, sig); 
    int status; 
    int ret = waitpid(-1, &status, 0); 
    printf("%s, ret: %d, status: %d\n", __func__, ret, status); 
} 

int 
main() 
{ 
    struct sigaction sig_act; 
    memset(&sig_act, 0, sizeof(sig_act)); 
    sig_act.sa_handler = signalHandler; 
    sigaction(SIGCHLD, &sig_act, NULL); 

    if (!fork()) { 
     sleep(20); 
     printf("%s: long run child %d exit.\n", __func__, getpid()); 
     _exit(0); 
    } 

    pid_t pid = fork(); 
    if (!pid) { 
     sleep(4); 
     printf("%s: %d exit.\n", __func__, getpid()); 
     _exit(0); 
    } 
    printf("%s: %d -> %d\n", __func__, getpid(), pid); 

    sleep(1); 
    printf("%s, start waiting for %d\n", __func__, pid); 
    int status; 
    int ret = waitpid(pid, &status, 0); 
    printf("%s, ret: %d, pid: %d, status: %d\n", __func__, ret, pid, status); 

    return 0; 
} 

ответ

0

Если процесс является однопоточным, вы можете временно заблокировать сигнал CHLD (используя sigprocmask), fork/waitpid, а затем разблокировать.

Не забудьте разблокировать сигнал в разветвленном дочернем элементе - хотя POSIX заявляет, что сигнальная маска не определена при запуске процесса, большинство существующих программ ожидают, что она будет полностью отключена.

+0

Это не работает по многим причинам, кроме того, что нет возможности узнать, является ли основная программа однопоточной или нет: 1) Я сказал, что я не хочу переопределять обработчик сигналов, поскольку процесс, который основное приложение хочет ждать, может закончиться в любое время, когда я настроил другой обработчик сигнала (который может быть «SIG_IGN'); 2) если «SIG_CHLD» игнорируется, 'wait' вернет ошибку и не сможет получить состояние выхода дочернего процесса, поэтому мне будет лучше просто настроить собственный обработчик, а не игнорировать его. – yuyichao

+0

Во-первых, если программа однопоточная, то тривиально знать, является ли она однопоточной - она ​​по определению и существует множество способов обеспечить это из библиотеки. Во-вторых, вы путаете обработчики сигналов с сигнальной маской (разные вещи, читайте man-страницу для sigprocmask). В-третьих, waitpid НЕ будет возвращаться с ошибкой до тех пор, пока ребенок не будет извлечен, даже если CHLD будет проигнорирован, поэтому он будет работать (попробуйте) - вы можете не получить статус выхода надежно, но вы можете спокойно подождать для ребенка. –

+0

Если вы хотите получить ответы здесь, пожалуйста, внимательно прочитайте ответы и попробуйте все, прежде чем принимать утверждения о неправильных ответах. Кроме того, если вы зададите неправильный вопрос, не удивляйтесь, если вы получите неправильный ответ на свою проблему. –