【问题标题】:System call fork() and execv function系统调用 fork() 和 execv 函数
【发布时间】:2013-10-09 10:54:36
【问题描述】:

我正在尝试使用此 c 代码连续运行两个可执行文件:

#include <stdio.h>
#include <unistd.h>

int main (int argc, char *argv[])
{
    fork();
    execv("./prcs1", &argv[1]); // GIVE ADDRESS OF 2nd element as starting point to skip source.txt
    fork();
    execv("./prcs2", argv);
    printf("EXECV Failed\n");
}

尽管有 fork,但程序在第一次 execv() 调用后退出,它永远不会到达第二个 execv()。我试过在第一次分叉后调用 wait() ,但我不确定这就是它所缺少的。

任何想法为什么在孩子退出后控制权不返回给父母?

【问题讨论】:

  • 啊,旧的“fork and exec”习语……不幸的是,它不像你所拥有的那样字面意思。
  • 您希望您的主进程在执行子进程时阻塞并等待吗?

标签: c linux unix system-calls


【解决方案1】:

exec 家族只有在调用失败时才会返回。

由于您不检查fork 的返回值,您将在父子进程中调用execv

检查返回值:如果是0你在子进程中,如果大于零那么你在父进程中。小于零表示fork 失败。

【讨论】:

  • 实际上,execv() never 如果成功则返回。见linux.die.net/man/3/execv
  • 它会在失败时返回,但你是对的......我会编辑答案
【解决方案2】:

你有几个问题。首先,如果你只想运行两个程序,你只需要调用fork()一次。然后在父进程中运行一个程序,在子进程中运行一个程序。其次,您正在构造 argv 数组以错误地传递给 execv。第一个条目应该是可执行文件名。执行以下操作:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main(int argc, char **argv)
{
    pid_t i = fork();
    if (i == 0)
    {
        execv("./prcs1", (char *[]){ "./prcs1", argv[1], NULL });
        _exit(1);
    }
    else if (i > 0)
    {
        execv("./prcs2", (char *[]){ "./prcs2", argv[0], NULL });
        _exit(2);
    }
    else
    {
        perror("fork failed");
        _exit(3);
    }
}

请注意,此示例不进行错误检查。

【讨论】:

  • 还有一个完全不合理的return 0:-S
  • 是的;只是试图关闭系统。我想如果 execv 失败了,它应该在那里。
  • 达到该点的任何方式都是错误的......但为了安全起见,我在孩子的后面添加_exit(EXIT_FAILURE) exec.
  • 不想吹毛求疵,但是在最近由于 tcmalloc 和 pthread_atfork 导致的一系列死锁之后,我真的会坚持在孩子身上使用 _exit。在分叉后继续前进只是非常危险。我想如果保证应用程序是单线程的(你的分配器是否承诺?你知道吗?),你可能没问题,但为什么要冒险。
  • 你能保证你的内存分配器没有启动持有锁的线程吗?
【解决方案3】:

您需要了解 fork 和 execv 如何协同工作。

  • fork() 复制当前进程,将 0 返回给子进程,将 childpid 返回给父进程
  • fork() 可能会失败,失败时返回 -1,检查一下
  • execv() 将重复的父进程替换为新进程
  • 典型的 fork/exec 配对将子进程替换为新进程
  • 您通常会分叉多个孩子,并希望他们同时运行,
  • 但是,您要求它们连续运行,即一个接一个
  • 因此,您需要等待第一个完成,然后再开始第二个
  • 因此您需要使用 wait() 的一些变体,下面的示例使用 waitpid() 来等待特定的孩子

您需要 stdlib 用于退出(以防 execv 失败)和 errno,以打印原因,

//I'm trying to run two executables consecutively using this c code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

您可能想检查您的孩子退出的原因(核心转储、信号、正常退出),因此我添加了此功能,

#include <sys/types.h>
#include <sys/wait.h>

//WIFEXITED(status) returns true if the child terminated normally, that is, by calling exit(3) or _exit(2), or by returning from main().
//WEXITSTATUS(status) returns the exit status of the child.  This consists of the least significant 8 bits of the status argument that the child specified in a call to exit(3) or _exit(2) or as the argument for a return statement in main().  This macro should only be employed if WIFEXITED returned true.
//WIFSIGNALED(status) returns true if the child process was terminated by a signal.
//WTERMSIG(status) returns the number of the signal that caused the child process to terminate.  This macro should only be employed if WIFSIGNALED returned true.
//WCOREDUMP(status) returns true if the child produced a core dump.  This macro should only be employed if WIFSIGNALED returned true.  This macro is not specified in POSIX.1-2001 and is not available on some UNIX implementations (e.g., AIX, SunOS).  Only use this enclosed in #ifdef WCOREDUMP ... #endif.
//WIFSTOPPED(status) returns true if the child process was stopped by delivery of a signal; this is only possible if the call was done using WUNTRACED or when the child is being traced (see ptrace(2)).
//WSTOPSIG(status) returns the number of the signal which caused the child to stop.  This macro should only be employed if WIFSTOPPED returned true.
//WIFCONTINUED(status) (since Linux 2.6.10) returns true if the child process was resumed by delivery of SIGCONT.
int
exitreason(pid_t cid, int status)
{
    if( WIFEXITED(status) )
    {
        printf("child %d terminated normally, that is, by calling exit(3) or _exit(2), or by returning from main().\n",cid);
        if( WEXITSTATUS(status) )
        {
        printf("child %d exit status %d.  This consists of the least significant 8 bits of the status argument that the child specified in a call to exit(3) or _exit(2) or as the argument for a return statement in main().\n",cid,WEXITSTATUS(status));
        }
    }
    if( WIFSIGNALED(status) )
    {
        printf("child %d process was terminated by a signal.\n",cid);
        if( WTERMSIG(status) )
        {
        printf("child %d signal %d that caused the child process to terminate.\n",cid,WTERMSIG(status));
        }
        if( WCOREDUMP(status) )
        {
        printf("child %d produced a core dump.  WCOREDUMP() is not specified in POSIX.1-2001 and is not available on some UNIX implementations (e.g., AIX, SunOS).  Only use this enclosed in #ifdef WCOREDUMP ... #endif.\n",cid);
        }
    }
    if( WIFSTOPPED(status) )
    {
        printf("child %d process was stopped by delivery of a signal; this is only possible if the call was done using WUNTRACED or when the child is being traced (see ptrace(2)).\n",cid);
        if( WSTOPSIG(status) )
        {
        printf("child %d number of the signal which caused the child to stop.\n",cid);
        }
    }
    if( WIFCONTINUED(status) )
    {
        printf("child %d process was resumed by delivery of SIGCONT.\n");
    }
}

这是你的程序,用 cmets 注释,解释哪些代码段由父级处理,哪些由子级处理。

int main (int argc, char *argv[])
{
    char proc1[] = "/bin/echo"; //"./prcs1";
    char proc2[] = "/bin/echo"; //"./prcs2";
    pid_t cid1, cid2, cidX;
    int status=0;
    int waitoptions = 0;
    //WNOHANG    return immediately if no child has exited.
    //WUNTRACED  also return if a child has stopped (but not traced via ptrace(2)).  Status for traced children which have stopped is provided even if this option is not specified.
    //WCONTINUED also return if a stopped child has been resumed by delivery of SIGCONT.
    int res;

    if( (cid1 = fork()) == 0 ) //child1
    {
        printf("in child1\n");
        if( (res = execv(proc1, &argv[1])) < 0 ) // GIVE ADDRESS OF 2nd element as starting point to skip source.txt
        {
        printf("error: child1: %d exec failed %d\n", cid1, errno);
        printf("error: cannot execv %s\n",proc1);
        exit(91); //must exit child
        }
    }
    else if( cid1 > 0 ) //cid>0, parent, waitfor child
    {
        cidX = waitpid(cid1, &status, waitoptions);
        printf("child1: %d res %d\n", cid1, res);
        exitreason(cid1, status);
    }
    else //cid1 < 0, error
    {
        printf("error: child1 fork failed\n");
    }

    if( (cid2 = fork()) == 0 ) //child2
    {
        printf("in child2\n");
        if( (res = execv(proc2, &argv[1])) < 0 ) // GIVE ADDRESS OF 2nd element as starting point to skip source.txt
        {
        printf("error: child2: %d exec failed %d\n", cid2, errno);
        printf("error: cannot execv %s\n",proc2);
        exit(92); //must exit child
        }
    }
    else if( cid2 > 0 ) //cid>0, parent, waitfor child
    {
        cidX = waitpid(cid2, &status, waitoptions);
        printf("child2: %d res %d\n", cid2, res);
        exitreason(cid2, status);
    }
    else //cid2 < 0, error
    {
        printf("error: child2 fork failed\n");
    }
}

【讨论】:

  • Bullet #3:你可以说 execv() 用一个新程序替换了 child 进程
【解决方案4】:

我猜你对 fork() 的阅读不多。

当您调用 fork() 时,它会创建一个子进程,该进程将从 fork 运行相同的代码。

fork() 返回三种值

  • 否定表示错误
  • 肯定表示您在父进程中,值显示子进程 ID
  • 零表示您在子进程中。

您的代码应如下所示。

#include <stdio.h>
#include <unistd.h>

int main (int argc, char *argv[])
{

    int ret = fork();
    if(ret==0)
    {
       //child process
       execv("./prcs1", &argv[1]); // GIVE ADDRESS OF 2nd element as starting point to skip source.txt
       printf("EXECV Failed from child\n");
    }
    else if(ret>0)
    {
       //parent process
       execv("./prcs2", argv);
       printf("EXECV Failed from parent\n");
    }
    else
    {
       //you will come here only if fork() fails.
       printf("forkFailed\n");
    }
    return 0;
}

【讨论】:

  • +1 for - 运行相同的code from fork。这很重要,因为代码不会重新开始,而是复制整个数据和堆栈,而区域表中的文本与程序计数器的新副本保持不变。我认为这是最相关的答案。
猜你喜欢
  • 2012-08-09
  • 1970-01-01
  • 1970-01-01
  • 2016-01-13
  • 1970-01-01
  • 1970-01-01
  • 2013-10-24
  • 2011-10-03
  • 1970-01-01
相关资源
最近更新 更多