5

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

为什么这没有实现为分配一个空的内存区域然后调用 exec()?这会节省时间和资源,对吗?

4

4 回答 4

4

中间步骤使您能够在子进程中设置共享资源,而外部程序不知道它。典型的例子是构造一个管道:

// 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);
    ...
}

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

至于内存的浪费,可以通过写时复制技术来避免。fork()不会为子进程分配新内存,而是将子进程指向父进程的内存,并指示仅当页面被写入时才复制页面。这fork()不仅节省了内存,而且速度很快,因为它只需要复制一个“目录”。

于 2013-04-04T17:55:03.923 回答
2

这是一个古老的抱怨。很多人先问为什么fork()通常他们会建议一个操作,既可以从头开始创建一个新进程,又可以在其中运行一个程序。这个操作被称为spawn().

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

事实上,除了 Unix 家族以外的所有系统采用了“衍生”方式。只有 Unix 是基于fork()exec().

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

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

Kool-Aid。

于 2013-04-04T17:33:34.410 回答
0

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

如果您在此处查看文档,它本质上需要生成一个新进程(fork调用),以便父进程恢复控制并等待它完成或像守护进程一样坐下。

于 2013-04-04T17:27:46.543 回答
0

只有在调用 exec() 时,这个子进程才会变成一个新进程。

并不真地。在一个分叉之后,你已经有了新的进程,甚至和它的父进程没有太大的不同。在某些情况下,没有 exec 需要遵循分叉。

那么为什么需要中间步骤(创建一个与父级具有相同上下文的子级)?

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

这不是浪费时间和资源吗,因为我们正在创建一个上下文(消耗时间并浪费内存)然后重写它?

由于使用了写时复制机制,因此大部分资源是虚拟的,因此不会浪费时间和资源。此外,说创建的上下文被覆盖是不正确的。鉴于实际上没有任何东西首先被写入,因此没有任何东西被重写。这就是 COW 的全部意义所在。“仅”进程地址空间(代码、堆和堆栈)被替换,而不是被覆盖。许多进程上下文被部分或全部保留,包括环境、文件描述符、优先级、忽略的信号、当前和根目录、限制、各种掩码、处理器绑定、特权以及与进程地址空间无关的其他一些东西。

于 2013-04-04T22:06:59.123 回答