Linux 系统调用 fork() 和 execv 函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19147386/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-07 00:59:59  来源:igfitidea点击:

System call fork() and execv function

clinuxunixsystem-calls

提问by Mike

I'm trying to run two executables consecutively using this c code:

我正在尝试使用此 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");
}

The program exits after the first execv() call despite the fork, it never gets to the second execv(). I've tried calling wait() after the first fork but I'm not sure that's what it's missing.

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

Any ideas why control doesn't return to the parent after the child exits?

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

采纳答案by Carl Norum

You have a couple of problems. First, if you only want to run two programs, you only need to call fork()once. Then run one program in the parent process and one in the child. Second, you're constructing the argvarray to be passed to execvincorrectly. The first entry should be the executable name. Do something like:

你有几个问题。首先,如果你只想运行两个程序,你只需要调用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);
    }
}

Note that this example does no error checking.

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

回答by LostBoy

The exec family will only return if the call fails.

exec 系列只会在调用失败时返回。

Since you do not check the return value of fork you will call execv in parent and child process.

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

Check the return value: if it is 0 you are in the child process, if it is bigger than zero you are in the parent process. Below zero means the fork failed.

检查返回值:如果它是 0 你在子进程中,如果它大于零你在父进程中。低于零意味着分叉失败。

回答by Dhaval

You haven't had much reading on fork() I guess.

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

when you call fork(), it creates a child process which will run the same code from fork.

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

fork()returns three kind of values

fork()返回三种值

  • negative which shows an error
  • positive which says you are in parent process and value shows childprosess ID
  • zero which says you are in child process.
  • 显示错误的负数
  • 正面表示您在父进程中,值显示子进程 ID
  • 零表示您处于子进程中。

your code should look like this.

你的代码应该是这样的。

#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;
}

回答by ChuckCottrill

You need to understand how fork and execv work together.

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

  • fork() makes a duplicate of the current process, returning 0 to child, childpid to parent
  • fork() can fail, and returns -1 on failure, check for that
  • execv() replaces the duplicated parent process with a new process
  • typical fork/exec pairing replaces the child process with a new process
  • often you fork more than one child, and want them to run simultaneously,
  • however, you asked for them to run consecutively, that is one after another
  • thus, you need to wait for the first to complete before starting the second
  • thus you need to use some variant of wait(), example below uses waitpid() to wait for specific child
  • fork() 复制当前进程,将 0 返回给 child,将 childpid 返回给 parent
  • fork() 可能会失败,并在失败时返回 -1,检查是否存在
  • execv() 用新进程替换重复的父进程
  • 典型的 fork/exec 配对用新进程替换子进程
  • 通常你分叉不止一个孩子,并希望他们同时运行,
  • 但是,你要求它们连续运行,即一个接一个
  • 因此,您需要在开始第二个之前等待第一个完成
  • 因此你需要使用wait()的一些变体,下面的例子使用waitpid()来等待特定的孩子

You need stdlib for exit (in case execv fails), and errno, to print the reason,

您需要 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>

You may want to examine the reason your child exited (core dump, signal, normal exit), thus I have added this function,

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

#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");
    }
}

And here is your program annotated with comments explaining which sections of code are processed by the parent, and which by the child(ren).

这是您的程序注释,解释了哪些代码段由父级处理,哪些由子级(ren)处理。

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");
    }
}