Различия состояний процессов с помощью waitpid и WNOHANG - PullRequest
2 голосов
/ 08 мая 2020

При создании программы оболочки я столкнулся с проблемой распознавания состояний процессов. Описание проблемы, с которой я столкнулся, заключается в том, что у меня есть список дочерних процессов, и я пытаюсь выяснить их состояние, используя waitpid и WNOHANG. Я использую sh, чтобы различать guish между 3 состояниями: TERMINATED, RUNNING и SUSPENDED. (как определено в приведенном ниже коде) Я использую sh, чтобы изменить состояния процессов на одно из этих трех выше, однако прямо сейчас эта функция делает статус запущенных процессов terminated, и эта функция также не распознает приостановленные процессы . Я хотел бы знать, что я делаю не так и как должна быть написана функция updateProcessList для этого?

#define TERMINATED  -1
#define RUNNING 1
#define SUSPENDED 0

typedef struct process{
    cmdLine* cmd;                     /* the parsed command line*/
    pid_t pid;                        /* the process id that is running the command*/
    int status;                       /* status of the process: RUNNING/SUSPENDED/TERMINATED */
    struct process *next;             /* next process in chain */
} process;

void updateProcessList(process **process_list) {
    process *p = *process_list;
    int code = 0, status = 0,pidd = 0;
    while (p) {
        pidd = p->pid;
        code = waitpid(pidd, &status, WNOHANG);
        if (code == -1) {            /* child terminated*/
            p->status = TERMINATED;
        } else if(WIFEXITED(status)){
            p->status = TERMINATED;
        }else if(WIFSTOPPED(status)){
            p->status = SUSPENDED;
        }
        p = p->next;
    }
}

1 Ответ

4 голосов
/ 08 мая 2020

From man 2 waitpid:

RETURN VALUE

    waitpid():  on  success, returns the process ID of the child whose state has changed;
    if WNOHANG was specified and one or more child(ren) specified by pid exist, but  have
    not yet changed state, then 0 is returned.  On error, -1 is returned.

Вы должны проверить возвращаемое значение для 0 ..., а также исправить остальные проверки .

code = waitpid(ppid, &status, WNOHANG | WUNTRACED | WCONTINUED);

if (code == -1) {
    // Handle error somehow... 
    // This doesn't necessarily mean that the child was terminated!
    // See manual page section "ERRORS".

    if (errno == ECHILD) {
        // Child was already terminated by something else.
        p->status = TERMINATED;
    } else {
        perror("waitpid failed");
    }
} else if (code == 0) {
    // Child still in previous state.
    // Do nothing.
} else if (WIFEXITED(status)) {
    // Child exited.
    p->status = TERMINATED;
} else if (WIFSIGNALED(status)) {
    // Child killed by a signal.
    p->status = TERMINATED;
} else if (WIFSTOPPED(status)) {
    // Child stopped.
    p->status = SUSPENDED;
} else if (WIFCONTINUED(status)) {
    // This branch seems unnecessary, you should already know this
    // since you are the one that should kill(pid, SIGCONT) to make the
    // children continue.
    p->status = RUNNING; 
} else {
    // This should never happen!
    abort();
}

Также обратите внимание:

  1. Мое добавление WUNTRACED и WCONTINUED во флаги: WIFSTOPPED() не может произойти, если вы не отслеживаете ребенка с помощью ptrace() или вы использовали флаг WUNTRACED, а WIFCONTINUED() не может произойти, если не используется WCONTINUED.
  2. Переменные code и ppid должны быть pid_t, а не int ( переменная ppid также кажется ненужной).

В любом случае рассмотрите возможность добавления обработчика сигнала для SIGCHLD и обновления там дочерних статусов. Ваша программа получит SIGCHLD за каждого дочернего элемента, который завершает / останавливает / возобновляет работу. Это намного проще и быстрее (не требует постоянного вызова waitpid() для каждого дочернего процесса).

...