Различать состояния процессов с помощью waitpid и WNOHANG

При создании программы оболочки я столкнулся с проблемой распознавания состояний процессов. Описание проблемы, с которой я столкнулся, заключается в том, что у меня есть список дочерних процессов, и я пытаюсь выяснить их состояние, используя waitpid и WNOHANG. Я хочу различать 3 состояния: TERMINATED, RUNNING и SUSPENDED. (как определено в приведенном ниже коде) Я хочу изменить состояния процессов на одно из этих трех выше, однако прямо сейчас эта функция делает статусы запущенных процессов равными 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;
    }
}

person Avi Ferdman    schedule 07.05.2020    source источник
comment
Я хотел бы знать, что я делаю неправильно. Расскажите, пожалуйста, с какими конкретно проблемами вы сталкиваетесь?   -  person kaylum    schedule 08.05.2020
comment
Что не работает? Что происходит, когда вы запускаете свою программу, и чего вы ожидали?   -  person Nate Eldredge    schedule 08.05.2020
comment
Я отредактировал сообщение, чтобы быть более конкретным   -  person Avi Ferdman    schedule 08.05.2020
comment
Вы говорите, что состояние становится TERMINATED, даже когда процесс действительно запущен? И как вы останавливаете процесс, чтобы проверить состояние SUSPENDED?   -  person kaylum    schedule 08.05.2020
comment
То есть, пожалуйста, дайте нам точный тестовый пример - какой тест был проведен, каков был точный ожидаемый результат и каков был точный фактический результат.   -  person kaylum    schedule 08.05.2020
comment
Да, точно. И я проверяю это с помощью отладки и печати.   -  person Avi Ferdman    schedule 08.05.2020
comment
Где вы устанавливаете RUNNING? Покажите код как минимальный проверяемый пример.   -  person kaylum    schedule 08.05.2020
comment
когда процесс создается, его статус устанавливается равным RUNNING, с этого момента его статус остается таким, какой он есть, до тех пор, пока не будет вызвана эта функция. Эта функция предназначена для изменения статусов процессов.   -  person Avi Ferdman    schedule 08.05.2020
comment
@AviFerdman Запустив отладчик, можете ли вы сказать нам, в каких случаях устанавливается TERMINATED? Дело if (code == -1)?   -  person kaylum    schedule 08.05.2020


Ответы (1)


Из 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() для каждого дочернего процесса).

person Marco Bonelli    schedule 08.05.2020
comment
Я согласен с проблемой возвращаемого значения и собирался прокомментировать и это. Но что мне не ясно, так это то, как это приведет к установке TERMINATED, потому что, предположительно, случай -1 не вступит в игру, если процесс все еще выполняется? - person kaylum; 08.05.2020
comment
@kaylum, если возвращаемое значение равно -1, то вызывающая сторона сделала что-то не так. Это не значит, что ребенок остановился. Если вы посмотрите на страницу руководства, у вас есть список возможных ошибок, когда возвращается -1: эти ошибки являются либо недопустимыми аргументами, несуществующим дочерним элементом и т. д. - person Marco Bonelli; 08.05.2020
comment
Я это понимаю и согласен, что такое может быть. Но я не понимаю из кода, почему waitpid будет возвращать -1 в коде OP. Может быть, но просто мне это не очевидно. - person kaylum; 08.05.2020
comment
@kaylum только потому, что кажется, что это не может произойти, это не значит, что это не следует проверять. Если ОП все сделал правильно, -1 никогда не вернется. Если произойдет что-то странное (например, если какая-то другая часть кода waits для дочернего элемента и дочерний будет пожат, или если ->pid будет поврежден), то будет возвращено -1. - person Marco Bonelli; 08.05.2020
comment
Я думаю, вы упускаете мою мысль. Полностью согласен, надо исправлять. В чем я не уверен, так это в том, будет ли этого достаточно для решения проблемы ОП. То есть, видите ли вы какую-либо другую потенциальную проблему, которая может привести к описанной проблеме, или это, безусловно, случай -1, который является основной причиной? В любом случае, надеюсь, исправление этого действительно решит проблему. - person kaylum; 08.05.2020
comment
@kaylum основной причиной проблемы ОП является случай WIFSTOPPED. waitpid() не возвращает -1, возвращает 0, но так как первая ветвь не введена, то вторая введена ошибочно, так как status инициализировалось в 0 (соответствует WIFEXITED). - person Marco Bonelli; 08.05.2020
comment
Хм, этот последний комментарий не имеет смысла для меня. Даже у waitpid man есть пример кода, который выполняет именно эту WIFEXITED проверку, когда waitpid возвращает 0. - person kaylum; 08.05.2020
comment
Давайте продолжим обсуждение в чате. - person Marco Bonelli; 08.05.2020