在exec()之前fork()的用法是什么?

时间:2013-04-04 17:18:01

标签: c unix process

在* nix系统中,使用fork()系统调用创建进程。例如,考虑init进程创建另一个进程。首先它自己分叉并创建一个具有init等上下文的进程。只有在调用exec()时,这个子进程才会成为一个新进程。那么为什么需要中间步骤(创建具有与父相同的上下文的子项)?这不是浪费时间和资源,因为我们正在创建一个上下文(消耗时间和浪费内存),然后过度编写它?

为什么这没有实现为分配空闲内存区域然后调用exec()?这样可以节省时间和资源吗?

4 个答案:

答案 0 :(得分:3)

通过中间步骤,您可以在子进程中设置共享资源,而无需外部程序了解它。规范示例是构建管道:

// read output of "ls"
// (error checking omitted for brevity)
int pipe_fd[2];
pipe(&pipe_fd);
if (fork() == 0) {       // child:
    close(pipe_fd[0]);   // we don't want to read from the pipe
    dup2(pipe_fd[1], 1); // redirect stdout to the write end of the pipe
    execlp("ls", "ls", (char *) NULL);
    _exit(127);          // in case exec fails
}
// parent:
close(pipe_fd[1]);
fp = fdopen(pipe_fd[0], "r");
while (!feof(fp)) {
    char line[256];
    fgets(line, sizeof line, fp);
    ...
}

注意如何在forkexec之间在子项中完成标准输出到管道的重定向。当然,对于这个简单的情况,可以有一个spawning API,只要给出适当的参数,就可以自动执行此操作。但是fork()设计允许对子进程中的每进程资源进行任意操作 - 可以关闭不需要的文件描述符,修改每进程限制,删除权限,操纵信号掩码等等。如果没有fork(),产生过程的API最终会变得非常胖或者非常有用。实际上,产生竞争操作系统的过程产生的过程通常介于两者之间。

至于内存的浪费,使用copy on write技术可以避免。 fork()不会为子进程分配新内存,而是将子进程指向父进程的内存,并指示只有在页面被写入时才创建页面副本。这使fork()不仅内存效率高,而且速度快,因为它只需要复制“目录”。

答案 1 :(得分:2)

这是一个古老的抱怨。很多人首先要求为什么fork(),通常他们会建议一个从头开始创建新流程并在其中运行程序的操作。此操作称为spawn().

他们总是说,那会不会更快?

事实上,除了Unix系列之外的每个系统都采用“spawn”方式。只有Unix基于fork()exec().

但有趣的是,Unix一直比其他全功能系统快得多。它总是处理更多的用户和负载。

多年来Unix的发展速度更快。 Fork()不再真正复制地址空间,它只是使用一种名为copy-on-write.的技术共享它(一个名为vfork()的非常古老的叉优化也仍然存在。)

Kool-Aid.

答案 2 :(得分:0)

我不知道 init 进程在分叉方面是如何在内核上运行的,但是为了回答你为什么需要调用 fork 然后 exec 只是因为一旦你执行就没有回头路了。

如果查看文档here,它实质上需要生成一个新进程( fork 调用),以便父进程恢复控制并且等待让它完成或作为守护进程坐下。

答案 3 :(得分:0)

  

只有在调用exec()时,这个子进程才会成为新进程   过程

不是真的。在fork之后,您已经拥有了新的进程,即使与其父进程没有太大的不同。在某些情况下,没有exec需要遵循fork。

  

那么为什么是中间步骤(创建一个相同的孩子   作为父母的上下文)需要吗?

一个原因是因为它是创造整个shebang的有效方式。克隆通常不如从头开始创建。

  

这不是浪费时间和资源,因为我们正在创造一个   上下文(消耗时间和浪费记忆),然后过度写作?

由于使用了写入机制的复制,因此大多数此资源都是虚拟的,这不是浪费时间和资源。此外,说明创建的上下文被覆盖是不正确的。没有任何东西可以改写,因为事实上并没有真正写出来。这是COW的重点。 “仅”进程地址空间(代码,堆和堆栈)被替换,而不是被覆盖。部分或全部保留了许多进程上下文,包括环境,文件描述符,优先级,忽略的信号,当前和根目录,限制,各种掩码,处理器绑定,特权以及进程地址空间异常的其他一些内容。