等待进程组时,使用waitpid()出现“无子进程错误”。

10

我正在编写自己的玩具shell,并遇到了实现作业控制的问题。

我在子进程中和父进程中都使用setpgid设置子进程的进程组。我的等待调用如下:

pid = waitpid(-pid, &status, 0)

然而,waitpid返回-1并且perror显示“没有子进程”。不过,它似乎每次都在等待。此外,当从shell中运行时,ps输出看起来是正确的。由于ps父进程是kbsh,像我所预期的那样。

% ps -o pid,ppid,pgrp,session,tpgid,comm
Forking
In Parent: Setting process group to 20809 of process 20809 with setpgid
In Child Processes, pid of child process is 20809
in Child: Setting process group to 20809 of process 20809 with setpgid
Requesting that Process Group 20809 becomes the foreground process with tcsetpgrp
Waiting for job with process group 20809
  PID  PPID  PGRP  SESS TPGID COMMAND
12002 32573 12002 12002 20809 zsh
20808 12002 20808 12002 20809 kbsh
20809 20808 20809 12002 20809 ps
Wait Error: No child processes
Restoring Shell process group 20808 to forground

有人能看出我哪里做错了吗?如果需要,可以发布更多的代码...

似乎发生在 -1 和 -pid。 - Kyle Brandt
1
忽略了SIGCHILD信号,这就是它没有工作的原因 :-P - Kyle Brandt
3个回答

16

我忽略了sigchld信号,引用自waitpid手册:

POSIX.1-2001规定,如果SIGCHLD的处理方式被设置为SIG_IGN或SA_NOCLDWAIT标志被设置为SIGCHLD(参见sigaction(2)),则终止的子进程将不会成为僵尸进程。调用wait() 或 waitpid() 将一直阻塞,直到所有子进程都终止,然后返回错误码ECHILD。(最初的POSIX标准未指定设置SIGCHLD为SIG_IGN的行为。)Linux 2.6符合此规范。然而,Linux 2.4(及更早版本)不符合:如果在忽略SIGCHLD信号时进行wait()或waitpid()调用,该调用的行为就像没有忽略SIGCHLD一样,即调用将一直阻塞,直到下一个子进程终止,然后返回该子进程的进程ID和状态。


谢谢您的回答,自从看到这篇文章(http://www.gnu.org/software/libc/manual/html_node/Implementing-a-Shell.html#Implementing-a-Shell)以来,我一直在苦恼。 - John Ledbetter

6
我发现这个帖子时我正在尝试为我的计算机科学课程实现一个微型shell,并想和大家分享一下我的经验。我遇到了以下错误:
Waitpid error: No child processes

在我的情况下,我使用了《计算机系统:程序员的视角》教材提供的包装器。为了修复我的错误,我在csapp.c中将Waitpid更改为:
pid_t Waitpid(pid_t pid, int *iptr, int options) 
{
    pid_t retpid;

    if ((retpid  = waitpid(pid, iptr, options)) < 0) 
        unix_error("Waitpid error");
    return(retpid);
}

to

pid_t Waitpid(pid_t pid, int *iptr, int options)
{
        pid_t retpid;

        retpid  = waitpid(pid, iptr, options);
        if (retpid < 0 && errno != ECHILD)
                unix_error("Waitpid error");
        return(retpid);
}

2

您不需要设置进程组ID。子进程默认继承父进程的pid作为进程组。 当您等待时,需要等待父进程的pid:

int main(int argc, char **argv)
{
    pid_t pid;
    int stat;

    if ((pid = fork()))
    {
        printf("PARENT: %d | CHILD: %d\n", getpid(), pid);
        waitpid(-getpid(), &stat, 0);
        printf("DONE: %m\n");
    }
    else
    {
        printf("CHILD: %d\n", getpid());
        sleep(3);
    }
    return 0;
}

似乎是这样说的:“每次fork一个进程时,它应该通过调用setpgid将自己放入新的进程组中。” - Kyle Brandt
我认为这是因为进程组是控制前台和后台的方式。 - Kyle Brandt

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