【问题标题】:waitpid with execl used in child returns -1 with ECHILD?子进程中使用 execl 的 waitpid 返回 ECHILD 的 -1?
【发布时间】:2014-05-06 00:53:35
【问题描述】:

如果我在可能需要一些时间才能完成的子进程中使用execl,我什么时候需要使用waitpid

当我在父级中使用waitpid 时,它告诉我子级正在运行,因为waitpid 的返回值为0。但是,如果我在另一个函数中调用waitpid 一段时间后,它会返回- 1 将errno 设置为ECHILD。如果我不确定孩子是否完成了,我应该在什么时候使用waitpid

//pid_t Checksum_pid = fork();
Checksum_pid = fork();

if (Checksum_pid == 0)
{
    execl(path, name, argument as needed, ,NULL);
    exit(EXIT_SUCCESS);     
}
else if (Checksum_pid > 0)
{ 
    pid_t returnValue = waitpid(Checksum_pid, &childStatus, WNOHANG);

    if ( returnValue > 0)
    {
        if (WIFEXITED(childStatus))
        {
            printf("Exit Code: _ WEXITSTATUS(childStatus)") ;               
        }
    }
    else if ( returnValue == 0)
    {
        //Send positive response with routine status running (0x03)
        printf("Child process still running") ; 
    }
    else
    {
        if ( errno == ECHILD )
        {
             printf(" Error ECHILD!!") ;
        } 
        else if ( errno == EINTR )
        {
            // other real errors handled here.
            printf(" Error EINTR!!") ;
        }
        else
        {
            printf("Error EINVAL!!") ;
        }       
    }
}
else
{   
    /* Fork failed. */
    printf("Fork Failed") ;
}

【问题讨论】:

  • exit(EXIT_SUCCESS); 后面的execl() 是错误的指示符;如果execl() 返回,它就失败了,所以你真的应该用exit(EXIT_FAILURE); 退出,让父进程知道你失败了。
  • 当我尝试在 execl 之上和 execl 之下的子进程中使用 printf 时。我无法得到任何指纹......我不知道为什么。我认为在 execl 之上我应该得到打印。我在写吗?如果 execl 成功,那么如何退出子进程来终止它?如果我不在父进程中使用waitpid(在某些执行后在其他函数中的某个位置),那么使用与我在父进程中获得的第一个参数相同的childpid的waitpid是否有效?

标签: c linux fork waitpid os.execl


【解决方案1】:

如果你等待WNOHANG,那么除非你的孩子已经死了,否则你不会得到任何有用的状态——甚至可能还没有开始(它可能仍在等待从磁盘加载可执行文件当父级执行waitpid())。

如果您想知道孩子已经完成,请不要使用WNOHANG — 使用0 作为第三个参数,除非您想了解未跟踪或继续的进程。这将等到Checksum_pid 标识的进程退出,或者系统以某种神秘的方式(我从未见过这种情况)失去了对它的跟踪。


此代码产生Exit Code: 0 作为输出:

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main(void)
{
    pid_t Checksum_pid = fork();

    if (Checksum_pid < 0)
        printf("Fork Failed\n");
    else if (Checksum_pid == 0)
    {
        execl("/bin/sleep", "/bin/sleep", "2", NULL);
        exit(EXIT_FAILURE);
    }
    else
    {
        int childStatus;
        pid_t returnValue = waitpid(Checksum_pid, &childStatus, 0);

        if (returnValue > 0)
        {
            if (WIFEXITED(childStatus))
                printf("Exit Code: %d\n", WEXITSTATUS(childStatus));
            else
                printf("Exit Status: 0x%.4X\n", childStatus);
        }
        else if (returnValue == 0)
            printf("Child process still running\n");
        else
        {
            if (errno == ECHILD)
                printf(" Error ECHILD!!\n");
            else if (errno == EINTR)
                printf(" Error EINTR!!\n");
            else
                printf("Error EINVAL!!\n");
        }
    }

    return 0;
}

它与您的代码非常相似;我只是将fork() 的检查移到了顶部。我仍然希望摆脱if 语句的“浓密树木”,但这并不重要。当你运行这段代码时会发生什么?要得到ECHILD 错误,你必须改变什么(你能改变它以便得到ECHILD 错误)吗?

当您设法获得基于此的代码来重现问题时,我们可以找出您出现这种行为的原因。

在带有 GCC 4.8.2 的 Mac OS X 10.9.2 Mavericks 以及带有 GCC 4.8.1 的 Ubuntu 13.10 上测试(我需要添加 -D_XOPEN_SOURCE=700 以使其能够使用我严格的编译标志进行编译;Mac OS X 管理没有那个),但我不希望在其他地方得到不同的结果。

【讨论】:

  • 那我需要用什么?如果我将 0 作为可选的第三个参数而不是 WNOHANG 传递,这是什么意思?
  • 我需要在 waitpid 中使用哪些参数?我尝试了 WCONTINUED 但没有成功。我应该使用 WUNTRACED 吗?
  • 我尝试使用 0 作为 waitpid 的第三个参数,但仍然给出 ERROR : ECHILD!!如果子进程中的 exit(0) 是否意味着它将以退出代码 0 终止?这里在我的例子中 execl 是成功的。
  • 我有两组函数 1) start :我想在子进程中使用 execl,它将执行脚本文件并生成输出文件。 2) parse_display_res:在这个函数中,我将使用生成的输出文件(来自 start 函数)来提取结果。所以在提取数据之前需要有输出文件。
  • 您可以使用旧的int corpse = wait(&amp;status);,它只是等待一个孩子死亡并报告它,或者如果没有孩子剩下,则返回-1。否则,发布问题代码的完整 SSCCE (Short, Self-Contained, Correct Example) 或 MCVE ([Minimal, Complete, Verifiable Example](http:stackoverflow.com/help/mcve)) (设置为运行sleep 5 作为子进程,例如)。
【解决方案2】:
int start(int Area)
{

        int childStatus;
        pid_t Checksum_pid = fork();

        if(Checksum_pid < 0)
        {   
            /* Fork failed. */
            printf(" Fork Failed") ;

        }   
        else if (Checksum_pid == 0)         
        {

            for (int i = 0; i<5; i++)

            {

                if ( g_area[i] == Area)
                {

                    execl(ScriptPath, ScriptName, NULL);

                }
            }

            exit(EXIT_FAILURE);

        }

        else        
        { 
            /* This is the parent process. */           
            pid_t returnValue = waitpid(Checksum_pid, &childStatus, 0);


            if ( returnValue > 0)
            {
                // Check if child ended normally 
                printf("WAITPID Successful") ;                  

                if (WIFEXITED(childStatus))
                {
            printf("Exit Code: _ WEXITSTATUS(childStatus)");
                }

            }

            else if ( returnValue == 0)
            {

                printf("Child process still running") ;

            }
            else
            {

                if ( errno == ECHILD )
                {
                     printf("Error ECHILD!!") ;
                } 
                else if ( errno == EINTR )
                {
                    printf("Error EINTR!!") ;
                }
                else
                {
                    printf(" Error EINVAL!!") ;
                }       
                retCode = FAILED;
            }        

        }            
}

【讨论】:

  • 请研究如何创建一个 MCVE How to create a Minimal, Complete, and Verifiable Example? 或 SSCCE (Short, Self-Contained, Correct Example),这两个名称的基本思想相同。在上面的代码中,g_area 是什么? ScriptPathScriptName 的值是多少?你通过了Area 的什么值?为什么您的任何消息都没有以换行符结尾(除非它们确实出现,否则它们不会可靠地出现)。为什么代码中有这么多空行?为什么不打印 WEXITSTATUS 值,而不是在输出中包含宏名称?
【解决方案3】:

如果忽略信号 SIGCHLD(这是默认值)并且您跳过 Jonathan 提到的 WNOHANG,waitpid 将挂起,直到子退出,然后失败并显示代码 ECHILD。我自己在父进程应该等待子进程完成并且为 SIGCHLD 注册处理程序似乎有点矫枉过正的场景中遇到了这个问题。自然的后续问题是“在这种情况下可以将 ECHILD 视为预期事件,还是我总是应该为 SIGCHLD 编写处理程序?”, 但对此我没有答案。

来自documentation for waitpid(2)

孩子

(for waitpid() or waitid()) pid指定的进程(waitpid()) 或 idtype 和 id (waitid()) 不存在或不是 调用过程。 (如果采取行动,这可能发生在自己的孩子身上 对于 SIGCHLD 设置为 SIG_IGN。另请参阅有关 Linux 说明的部分 线程。)

再往下

POSIX.1-2001 指定如果 SIGCHLD 的处置设置为 为 SIGCHLD 设置了 SIG_IGN 或 SA_NOCLDWAIT 标志(参见 sigaction(2)),那么终止的孩子不会变成僵尸,并且 调用 wait() 或 waitpid() 将阻塞,直到所有孩子都有 终止,然后失败,errno 设置为 ECHILD。 (原本的 POSIX 标准保留了将 SIGCHLD 设置为 SIG_IGN 的行为 未指定。请注意,即使 SIGCHLD 的默认配置 是“忽略”,将处置显式设置为 SIG_IGN 会导致 僵尸进程子进程的不同处理。)Linux 2.6 符合 本规范。但是,Linux 2.4(及更早版本)没有:如果 在忽略 SIGCHLD 时调用 wait() 或 waitpid(), call 的行为就像 SIGCHLD 没有被忽略一样,也就是说, 调用阻塞,直到下一个孩子终止,然后返回 该子进程的进程 ID 和状态。

另见this answer

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-03-03
    • 1970-01-01
    • 2018-04-11
    • 1970-01-01
    • 2014-05-16
    • 1970-01-01
    • 2014-06-09
    • 1970-01-01
    相关资源
    最近更新 更多