fork和exec之间的区别

forkexec什么区别?

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

fork调用基本上是复制当前进程的, 几乎在所有方面都是一样的(并不是所有东西都被复制过,例如,在一些实现中资源限制,但是想法是尽可能地创build一个副本)。

新进程(subprocess)获取不同的进程ID(PID),并将旧进程(父进程)的PID作为其父PID(PPID)。 因为这两个进程现在运行完全相同的代码,所以他们可以知道哪个是由fork的返回代码 – 子代得到0,父代获得子代的PID。 当然,这一切都假设fork调用起作用 – 否则,不会创build子对象,并且父对象将获得错误代码。

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

所以, forkexec经常被用来依次获得一个新的程序作为当前进程的subprocess运行。 当你试图运行程序如find – shell fork时,Shell通常会这样做,然后子程序将find程序加载到内存中,设置所有命令行参数,标准I / O等等。

但是他们不需要一起使用。 例如,如果程序同时包含父代码和子代码(你需要小心你所做的事情,每个实现可能有限制),那么程序就可以自行fork而不需要执行。 守护进程只使用TCP端口侦听,并且在父节点返回侦听的同时,分发自己的副本以处理特定的请求,这已经被使用了很多(现在仍然是这样)。

同样,知道他们已经完成并且只想运行其他程序的程序不需要forkexec然后wait这个孩子。 他们可以直接将孩子加载到他们的stream程空间。

一些UNIX实现有一个优化的fork ,它使用了他们所谓的copy-on-write。 这是一个延迟复制fork中的进程空间的技巧,直到程序尝试改变该空间中的某些东西。 这对于那些只使用fork而不使用exec程序很有用,因为它们不需要复制整个进程空间。

如果在fork调用exec (主要是这种情况),那么会导致写入进程空间,然后将其复制到subprocess。

请注意, exec调用( execlexecleexecve等)的整个系列,但这里的exec是指其中的任何一个。

下图说明了典型的fork/exec操作,其中bash shell用于使用ls命令列出目录:

 +--------+ | 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 

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 } 

这可能会打击你的思想。 现在你有两个进程执行的代码几乎完全相同。 subprocessinheritance了刚刚创build的进程的所有代码和内存,包括从fork()调用刚刚停止的地方开始。 唯一的区别是fork()返回代码告诉你,如果你是父母或孩子。 如果你是父母,返回值是孩子的ID。

exec更易于掌握,你只需要告诉exec使用目标可执行文件执行一个进程,而没有两个进程运行相同的代码或者inheritance相同的状态。 像@Steve Hawkins所说的那样, exec可以在fork之后使用,在当前进程中执行目标可执行文件。

我认为Marc Rochkind的“Advanced Unix Programming”中的一些概念有助于理解fork() / exec()的不同angular色,特别是对于习惯于Windows CreateProcess()模型的人员:

程序是保存在磁盘上的常规文件中的指令和数据的集合。 (来自1.1.2程序,进程和线程)

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

如果不充分理解进程和程序之间的区别,就不可能理解exec或fork系统调用。 如果这些条款对您而言是新的,您可能要回顾一下第1.1.2节。 如果您现在准备好了,我们将用一句话来概括这个区别:进程是一个由指令,用户数据和系统数据段组成的执行环境,以及在运行时获取的大量其他资源而程序是一个包含指令和数据的文件,用于初始化进程的指令和用户数据段。 (从5.3系统调用)

一旦你理解了程序和进程之间的区别, fork()exec()函数的行为可以总结为:

  • fork()创build当前进程的副本
  • exec()用另一个程序replace当前进程中的程序

(这实质上是一个简化的“傻瓜”版本的paxdiablo的更详细的答案 )

沃伦·杨给予了描述。

fork创build一个调用进程的副本。 一般遵循结构 在这里输入图像描述

 int cpid = fork( ); if (cpid = = 0) { //child code exit(0); } //parent code wait(cpid); // end 

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

EXEC用新进程的代码,数据,堆栈来replace当前进程。 一般遵循结构 在这里输入图像描述 int cpid = fork();

 if (cpid = = 0) { //child code exec(foo); exit(0); } //parent code wait(cpid); // end 

(exec调用unix内核清除subprocess的文本,数据,堆栈和foo进程相关的文本/数据填充),因此subprocess使用不同的代码(foo的代码{不同于父母})

它们一起用来创build一个新的subprocess。 首先,调用fork创build当前进程的副本(subprocess)。 然后,从subprocess中调用exec来用新进程“replace”父进程的副本。

这个过程是这样的:

 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 } 

fork()创build当前进程的一个副本,并在fork()调用之后开始执行新的subprocess。 在fork()之后,它们是相同的,除了fork()函数的返回值。 (RTFM的更多细节。)这两个进程然后可以进一步发散,除了可能通过任何共享文件句柄,一个不能干扰另一个。

exec()用新的replace当前进程。 它与fork()无关,除非exec()经常跟在fork()之后,所需要的是启动一个不同的subprocess,而不是replace当前的subprocess。

在这里输入图像描述 fork()

它创build一个正在运行的进程的副本。 运行过程称为父进程 ,新创build的进程称为subprocess 。 区分二者的方法是通过查看返回的值:

  1. fork()返回父进程中subprocess的进程标识符(PID)

  2. fork()在子项中返回0。

exec()

它在一个过程中启动一个新的过程。 它将一个新程序加载到当前进程中,replace现有程序。

fork() + exec()

当启动一个新程序时,首先是fork() ,创build一个新进程,然后exec() (即加载到内存中并执行)它应该运行的程序二进制文件。

 int main( void ) { int pid = fork(); if ( pid == 0 ) { execvp( "find", argv ); } //Put the parent to sleep for 2 sec,let the child finished executing wait( 2 ); return 0; } 

理解fork()exec()概念的主要示例是shell ,即用户通常在login到系统后执行的命令解释程序。shell将命令行的第一个字解释为命令

对于许多命令, shell 分支和subprocess执行与命名关联的命令,将命令行中的其余单词作为命令的参数。

shell允许三种types的命令。 首先,一个命令可以是一个可执行文件 ,其中包含通过编译源代码 (例如C程序)生成的目标代码。 其次,命令可以是包含一系列shell命令行的可执行文件。 最后,一个命令可以是一个内部shell命令(而不是可执行文件ex-> cdls等)