首页 > 解决方案 > 使用 waitpid 和 WNOHANG 区分进程状态

问题描述

在构建 shell 程序时,我面临识别进程状态的问题。waitpid我面临的问题的描述是我有一个子进程列表,我正在尝试使用and找出它们的状态WNOHANG。我希望区分 3 种状态TERMINATEDRUNNINGSUSPENDED。(在下面的代码中定义)我希望将进程状态更改为上述三个之一,但是现在这个函数使正在运行的进程状态为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;
    }
}

标签: cshellwaitpid

解决方案


来自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和:除非您使用标志跟踪孩子,否则不会发生,除非使用标志否则不会发生。WCONTINUEDWIFSTOPPED()ptrace()WUNTRACEDWIFCONTINUED()WCONTINUED
  2. codeandppid变量应该是,而pid_t不是int(该ppid变量似乎也不需要)。

在任何情况下,请考虑为SIGCHLD那里添加信号处理程序并更新子状态。您的程序将为SIGCHLD每个终止/停止/恢复的孩子收到一个。它更简单也更快(不需要连续调用waitpid()每个子进程)。


推荐阅读