2009-10-31 74 views
169

forkexec之间有什么区别?fork和exec之间的区别

+84

...我们希望谷歌链接到这里下一次有人网上搜寻这...这是一个有效的问题。 – Polaris878 2009-10-31 04:29:23

+1

呃。 Google涵盖了这一点。为什么阻塞它以获得更多的结果,所以我们可以说有另一个SO等级的答案呢? – 2009-10-31 04:38:29

+3

@Justin在http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html – 2009-10-31 04:38:32

回答

306

采用forkexec体现了UNIX的精神,因为它提供了一个非常简单的方式开始新的进程。

fork通话基本上使得当前进程的副本,在几乎各种方式(不是一切被复制,例如相同的,在一些实现资源限制,但这个想法是创造尽可能接近的副本尽可能)。

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

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

因此,forkexec通常顺序使用,以获得作为当前进程的子节点运行的新程序。当你尝试运行程序find - 外壳分支时,shell通常会这样做,然后子程序将find程序加载到内存中,设置所有命令行参数,标准I/O等等。

但他们不需要一起使用。例如,如果程序同时包含父代码和子代码(您需要小心你的操作,每个实现可能有限制),那么对于fork本身没有exec的程序是完全可以接受的。这被用于很多(现在仍然),守护进程只需监听一个TCP端口并自己复制一个副本,以便在父节点返回到监听时处理特定请求。

类似地,知道他们已完成并且只想运行其他程序的程序不需要forkexec,然后wait为孩子。他们可以直接将孩子加载到他们的流程空间中。

某些UNIX实现有一个优化的fork,它使用了他们所谓的写入时拷贝(copy-on-write)。这是一个延迟复制fork中的进程空间的技巧,直到程序尝试更改该空间中的某些内容。这对那些只使用fork而不是exec的程序非常有用,因为它们不必复制整个进程空间。

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

注意,这里的exec电话(execlexecleexecve等),但在上下文中exec全家在这里是指任何人。

下图说明了典型fork/exec操作,其中bash壳用于列出与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 
44

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 
} 

这样的打击你的想法。现在你有一段由两个进程执行的完全相同状态的代码。子进程继承刚刚创建的进程的所有代码和内存,包括从刚刚停止的进程开始。唯一的区别是fork()返回代码告诉你,如果你是父母或孩子。如果您是父母,则返回值是孩子的ID。

exec更易于掌握,您只需告知exec使用目标可执行文件执行进程,并且没有两个进程运行相同的代码或继承相同的状态。像@Steve Hawkins说的那样,exec可以用于在当前进程中执行目标可执行文件之后执行fork

+6

还有'pid <0'和'fork()'调用失败的情况 – 2009-10-31 04:37:25

+3

这根本不会让我大跌眼镜:-)一段代码每次使用共享库或DLL时,都会执行由两个进程执行的操作。 – paxdiablo 2009-10-31 05:01:18

4

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

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

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

IIUC,可能不适用于使用GUI的流程? – moala 2012-01-05 12:54:43

+1

在第7行中提到exec()函数创建子进程。是否真的如此,因为fork()已经创建了子进程,而exec()调用只是替换了刚刚创建的新进程的程序 – cbinder 2014-05-25 02:54:34

28

我想从"Advanced Unix Programming" by Marc Rochkind一些概念理解fork()不同角色是有帮助/ exec(),特别是对于一个用于Windows的CreateProcess()型号:

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

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

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

一旦你理解程序和过程中,fork()exec()功能的行为之间的区别可以概括为:

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

(这基本上是“傻瓜”版本的paxdiablo's much more detailed answer简化)

21

沃伦年轻了贴切的描述。

叉创建调用进程的副本。 通常遵循结构 enter image description here

int cpid = fork(); 

if (cpid = = 0) 
{ 

    //child code 

    exit(0); 

} 

//parent code 

wait(cpid); 

// end 

(子进程文本(代码),数据,堆栈是与调用过程) 子进程中,如果块执行代码。

EXEC替换新进程的代码,数据,堆栈中的当前进程。 通常遵循结构 enter image description here INT CPID =叉();

if (cpid = = 0) 
{ 

    //child code 

    exec(foo); 

    exit(0); 

} 

//parent code 

wait(cpid); 

// end 

(Exec之后调用UNIX内核将清除子进程的文本,数据,堆栈和以foo过程充满相关文件/数据) 这样子进程是不同的代码(Foo的代码{不一样父})

1

enter image description herefork()

它创建正在运行的进程的副本。在运行过程被称为父进程 &新创建的进程被称为子进程。区分这两种方式是通过查看返回值:

  1. fork()返回子进程的进程标识符(PID)在儿童的父

  2. fork()返回0。

exec()

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

fork() + exec()

当启动一个新的程序是先fork(),创建一个新的过程,然后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; 
} 
0

最典型的例子来了解fork()exec()概念是,命令解释程序,用户登录到系统。壳之后通常执行解释命令行的第一个字作为命令名称

对于许多命令,则和子进程可执行文件与命名关联的命令将命令行上的其余字作为命令的参数处理。

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