fork和exec之间的区别

Sas*_*shi 194 c unix fork exec

fork和之间有什么区别exec

pax*_*blo 353

使用forkexec体现UNIX的精神,它提供了一种非常简单的方法来启动新进程.

fork调用基本上复制了当前进程,几乎在所有方面都相同(并非所有内容都被复制,例如,某些实现中的资源限制,但想法是尽可能创建尽可能接近的副本).

新进程(子进程)获取不同的进程ID(PID),并将旧进程(父进程)的PID作为其父PID(PPID).因为这两个进程现在运行完全相同的代码,所以它们可以通过返回代码fork- 孩子得到0 来判断哪个进程,父进程获取子进程的PID.当然,这就是假设fork调用有效 - 如果没有,则不会创建子节点并且父节点获取错误代码.

exec调用是一种基本上用新程序替换整个当前进程的方法.它将程序加载到当前进程空间并从入口点运行它.

因此,forkexec通常的顺序来获取所有正在运行的当前进程的孩子一个新的程序.每当你试图运行像findshell 这样的程序时,shell通常会这样做,然后子程序将find程序加载到内存中,设置所有命令行参数,标准I/O等等.

但它们不需要一起使用.例如,如果程序包含父代码和子代码,那么程序fork本身是完全可以接受的exec(你需要小心你做什么,每个实现可能都有限制).这对于守护进程使用了​​很多(现在仍然如此),它们只是监听TCP端口和fork自己的副本来处理特定请求,同时父进程回去监听.

同样地,知道它们已经完成并且只想运行另一个程序的程序不需要fork,exec然后wait对于孩子.他们可以直接将孩子加载到他们的流程空间.

某些UNIX实现已经过优化fork,它们使用了所谓的copy-on-write.这是一个延迟复制进程空间的技巧,fork直到程序尝试更改该空间中的某些内容.这对于那些仅使用fork而不是exec因为它们不必复制整个进程空间的程序非常有用.

如果exec 称为以下fork(这是什么会发生大多),导致进程空间写,然后将其复制子进程.

请注意,是全家exec电话(execl,execle,execve等),但exec在上下文中这里是指任何人.

下图说明了使用shell通过命令列出目录的典型fork/exec操作:bashls

+--------+
| pid=7  |
| ppid=4 |
| bash   |
+--------+
    |
    | calls fork
    V
+--------+             +--------+
| pid=7  |    forks    | pid=22 |
| ppid=4 | ----------> | ppid=7 |
| bash   |             | bash   |
+--------+             +--------+
    |                      |
    | waits for pid 22     | calls exec to run ls
    |                      V
    |                  +--------+
    |                  | pid=22 |
    |                  | ppid=7 |
    |                  | ls     |
    V                  +--------+
+--------+                 |
| pid=7  |                 | exits
| ppid=4 | <---------------+
| bash   |
+--------+
    |
    | continues
    V
Run Code Online (Sandbox Code Playgroud)


Dou*_* T. 52

fork()将当前进程拆分为两个进程.换句话说,你很好的线性易思考程序突然变成了运行一段代码的两个独立程序:

 int pid = fork();

 if (pid == 0)
 {
     printf("I'm the child");
 }
 else
 {
     printf("I'm the parent, my child is %i", pid);
     // here we can kill the child, but that's not very parently of us
 }
Run Code Online (Sandbox Code Playgroud)

这可能会让你大吃一惊.现在你有一段代码,两个进程执行相同的状态.子进程继承了刚刚创建它的进程的所有代码和内存,包括从fork()刚刚停止的调用开始.唯一的区别是fork()返回代码告诉您您是父母还是孩子.如果您是父级,则返回值是子级的ID.

exec更容易掌握,您只需告诉exec使用目标可执行文件执行进程,并且您没有两个进程运行相同的代码或继承相同的状态.就像@Steve Hawkins所说,exec可以在你fork执行当前进程后使用目标可执行文件.

  • 当`pid <0`和`fork()`调用失败时,还有一个条件 (6认同)
  • 这根本不会让我大吃一惊:-)每次使用共享库或DLL时,都会发生由两个进程执行的一段代码. (3认同)

Mic*_*urr 31

我认为Marc Rochkind的"高级Unix编程"中的一些概念有助于理解fork()/ 的不同角色exec(),特别是对于习惯于Windows CreateProcess()模型的人:

一个方案是被保存在磁盘上的常规文件的指令和数据的集合.(来自1.1.2程序,进程和线程)

.

为了运行程序,首先要求内核创建一个新进程,该进程是程序执行的环境.(也来自1.1.2程序,进程和线程)

.

如果不完全理解进程和程序之间的区别,就无法理解exec或fork系统调用.如果这些条款对您而言是新的,您可能需要返回并查看第1.1.2节.如果您现在准备好了,我们将用一句话概括区别:流程是一个执行环境,包括指令,用户数据和系统数据段,以及在运行时获取的许多其他资源程序是包含指令和数据的文件,用于初始化进程的指令和用户数据段.(来自5.3 exec系统调用)

一旦理解了程序和进程之间的区别,就可以将行为fork()exec()函数概括为:

  • fork() 创建当前进程的副本
  • exec() 用另一个程序替换当前进程中的程序

(这实际上是一个简化的'for dummies '版本的paxdiablo更详细的答案)


小智 29

Fork创建一个调用进程的副本.一般遵循结构 在此输入图像描述

int cpid = fork( );

if (cpid = = 0) 
{

  //child code

  exit(0);

}

//parent code

wait(cpid);

// end
Run Code Online (Sandbox Code Playgroud)

(对于子进程文本(代码),数据,堆栈与调用进程相同)子进程在if块中执行代码.

EXEC用新进程的代码,数据和堆栈替换当前进程.一般遵循结构 在此输入图像描述

int cpid = fork( );

if (cpid = = 0) 
{   
  //child code

  exec(foo);

  exit(0);    
}

//parent code

wait(cpid);

// end
Run Code Online (Sandbox Code Playgroud)

(在exec调用unix内核之后清除子进程文本,数据,堆栈并填充与foo进程相关的文本/数据)因此子进程使用不同的代码(foo的代码{与父对象不同})

  • 这与问题有点无关,但如果子进程碰巧先完成它的代码,上面的代码是否会导致竞争条件?在这种情况下,父进程将永远等待子进程终止自己,对吗? (3认同)
  • @stdout:回答你的问题有点晚了,但我认为不会发生竞争条件。当一个进程在父进程等待它之前退出时,它就会进入僵尸状态(它已经死了,但仍然在某种程度上徘徊)。徘徊的部分基本上是退出代码,以便父级最终可以“等待”并接收该信息。到那时,僵尸就完全消失了。并且,如果父进程先消失,则“init”进程将继承子进程并最终收获退出进程(当子进程退出时)。 (2认同)

Ste*_*ins 7

它们一起用于创建新的子进程.首先,调用fork创建当前进程的副本(子进程).然后,exec从子进程内调用"替换"父进程的副本和新进程.

这个过程是这样的:

child = fork();  //Fork returns a PID for the parent process, or 0 for the child, or -1 for Fail

if (child < 0) {
    std::cout << "Failed to fork GUI process...Exiting" << std::endl;
    exit (-1);
} else if (child == 0) {       // This is the Child Process
    // Call one of the "exec" functions to create the child process
    execvp (argv[0], const_cast<char**>(argv));
} else {                       // This is the Parent Process
    //Continue executing parent process
}
Run Code Online (Sandbox Code Playgroud)

  • 在第 7 行中提到 exec() 函数创建了子进程。真的是这样吗,因为 fork() 已经创建了子进程,而 exec() 调用只是替换了刚刚创建的新进程的程序 (2认同)

Abd*_*inu 7

fork()和之间的主要区别exec()在于,

\n\n

系统fork()调用创建当前运行程序的克隆。原始程序在 fork() 函数调用后继续执行下一行代码。克隆也会从下一行代码开始执行。\n看看我从http://timmurphy.org/2014/04/26/using-fork-in-cc-a-minimum-working-example/获得的以下代码

\n\n
#include <stdio.h>\n#include <unistd.h>\nint main(int argc, char **argv)\n{\n    printf("--beginning of program\\n");\n    int counter = 0;\n    pid_t pid = fork();\n    if (pid == 0)\n    {\n        // child process\n        int i = 0;\n        for (; i < 5; ++i)\n        {\n            printf("child process: counter=%d\\n", ++counter);\n        }\n    }\n    else if (pid > 0)\n    {\n        // parent process\n        int j = 0;\n        for (; j < 5; ++j)\n        {\n            printf("parent process: counter=%d\\n", ++counter);\n        }\n    }\n    else\n    {\n        // fork failed\n        printf("fork() failed!\\n");\n        return 1;\n    }\n    printf("--end of program--\\n");\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

该程序在 ing 之前声明了一个计数器变量,并将其设置为零fork()。在 fork 调用之后,我们有两个并行运行的进程,两个进程都递增自己的计数器版本。每个进程都会运行到完成并退出。由于进程是并行运行的,因此我们无法知道哪个进程将首先完成。运行该程序将打印类似于下图所示的内容,但每次运行的结果可能会有所不同。

\n\n
--beginning of program\nparent process: counter=1\nparent process: counter=2\nparent process: counter=3\nchild process: counter=1\nparent process: counter=4\nchild process: counter=2\nparent process: counter=5\nchild process: counter=3\n--end of program--\nchild process: counter=4\nchild process: counter=5\n--end of program--\n
Run Code Online (Sandbox Code Playgroud)\n\n

系统调用系列exec()将进程当前正在执行的代码替换为另一段代码。该进程保留其 PID,但它成为一个新程序。例如,考虑以下代码:

\n\n
#include <stdio.h> \n#include <unistd.h> \nmain() {\n char program[80],*args[3];\n int i; \nprintf("Ready to exec()...\\n"); \nstrcpy(program,"date"); \nargs[0]="date"; \nargs[1]="-u"; \nargs[2]=NULL; \ni=execvp(program,args); \nprintf("i=%d ... did it work?\\n",i); \n} \n
Run Code Online (Sandbox Code Playgroud)\n\n

该程序调用该execvp()函数将其代码替换为日期程序。如果代码存储在名为 exec1.c 的文件中,则执行它会产生以下输出:

\n\n
Ready to exec()... \nTue Jul 15 20:17:53 UTC 2008 \n
Run Code Online (Sandbox Code Playgroud)\n\n

程序将行 \xe2\x80\x95Ready 输出到 exec() 。。。\xe2\x80\x96 并在调用 execvp() 函数后,将其代码替换为日期程序。请注意行 \xe2\x80\x95 。。。did it work\xe2\x80\x96 不显示,因为此时代码已被替换。相反,我们看到执行 \xe2\x80\x95date -u.\xe2\x80\x96 的输出

\n