使用waitpid和WNOHANG区分进程状态

3
在构建一个Shell程序时,我遇到了识别进程状态的问题。我所面临的问题描述如下:我有一个子进程列表,我试图使用waitpidWNOHANG来确定它们的状态。我希望区分三种状态:终止运行暂停。(如下代码所定义) 我希望将进程状态更改为上述三种之一,但现在这个函数会将正在运行的进程状态更改为终止,并且该函数也无法识别已暂停的进程。 我想知道我做错了什么,应该如何编写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
"我想知道我做错了什么。请问您遇到了哪些具体问题?" - kaylum
1
什么没做好?当您运行程序时会发生什么?您原本希望发生什么? - Nate Eldredge
我编辑了帖子,使其更具体。 - Avi Ferdman
你是说即使进程仍在运行,其状态也会变为“TERMINATED”吗?而你是如何停止进程以测试“SUSPENDED”状态的? - kaylum
1
请提供确切的测试用例 - 运行了哪个测试,期望的确切结果是什么,实际的确切结果是什么。 - kaylum
显示剩余4条评论
1个回答

4

来自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. 我在标记中添加了 WUNTRACEDWCONTINUED:只有当您使用 ptrace() 追踪子进程或使用 WUNTRACED 标记时,WIFSTOPPED() 才可能发生;而且只有在使用了 WCONTINUED 之后,WIFCONTINUED() 才会发生。
  2. codeppid 变量应该是 pid_t,而不是 intppid 变量似乎也没必要)。

无论如何,考虑为 SIGCHLD 添加信号处理程序,并在那里更新子进程状态。每个终止/停止/恢复的子进程都会向您的程序发送一个 SIGCHLD 信号。这样做更简单,而且速度更快(不需要在每个单独的子进程上持续调用 waitpid())。


我同意返回值问题,并且也打算对此发表评论。但是,我不清楚的是,如果进程仍在运行,那么这将如何导致TERMINATED被设置,因为假定-1情况不会发生? - kaylum
@kaylum 如果返回值为“-1”,则表示调用者做错了什么,而不是子进程已停止。如果您查看手册页面,您会看到当返回“-1”时可能出现的错误列表:这些错误可能是无效参数、不存在的子进程等。 - Marco Bonelli
@kaylum 只是因为看起来不可能发生,就不意味着不应该进行检查。如果 OP 做得正确,-1 将永远不会被返回。如果发生了一些奇怪的事情(例如,如果代码的其他部分“等待”子进程并且子进程被回收,或者->pid被破坏),那么将返回 -1 - Marco Bonelli
我认为你没有理解我的观点。我完全同意需要修复它。我不确定的是,这样做是否足以解决 OP 的问题。也就是说,你是否看到其他可能导致所描述问题的潜在问题,或者 -1 情况肯定是根本原因?无论如何,希望修复后能够解决问题。 - kaylum
让我们在聊天中继续这个讨论 - Marco Bonelli
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接