fork 和 exec 如何工作?

fork 和 exec 如何工作?

我没有太多经验,只是试图参与它们如何从用户级别解释为硬件的过程。

因此,当从 shell 触发命令时,fork()会继承它的子进程并将exec()子进程加载到内存并执行。

  1. 如果子进程包含了父进程(即原始进程)的所有属性,那么这个子进程还需要什么呢?原始进程也可能已加载到内存中。
  2. 这个概念是否fork适用exec于 UNIX 中的所有可执行程序?也像 shell 脚本一样还是仅用于命令?它也适用于 shell 内置命令吗?
  3. 什么时候是写时复制如果我要执行命令/脚本,会使用什么概念?

很抱歉一次问了很多问题,但是当我想到任何命令执行时,所有这些问题都会立即浮现在我的脑海中。

答案1

因此,当从 shell 发出命令时,fork() 继承它的子进程,并且 exec() 将子进程加载到内存并执行。

不完全的。 fork()克隆当前进程,创建一个相同的子进程。 exec()加载一个新的程序进入当前流程,替换现有流程。

我的qs是:

如果子进程包含父进程(即原始进程)的所有属性,那么这个子进程需要什么?原始进程也可能已加载到内存中。

需要是因为父进程还不想终止;它希望启动一个新进程并在继续执行的同时执行某些操作。

这个 fork 和 exec 概念是否适用于 UNIX 中的所有可执行程序?就像 shell 脚本一样还是仅适用于命令?它也适用于 shell 内置命令吗?

对于外部命令,shell 会执行 afork()操作,以便该命令在新进程中运行。内置函数仅由 shell 直接运行。另一个值得注意的命令是exec,它无需先 ing 即可将 shell 告知exec()外部程序fork()。这意味着 shell 本身被新程序替换,因此该程序退出时不再返回。如果您说,,exec true那么/bin/true将替换您的 shell,并立即退出,终端中不再运行任何内容,因此它将关闭。

当使用写时复制概念时,我是否会执行命令/脚本?

回到石器时代,fork()实际上必须将调用进程中的所有内存复制到新进程中。写入时复制是一种优化,其中设置页表,以便两个进程开始共享所有相同的内存,并且仅在需要时复制任一进程写入的页面。

答案2

  1. 对于某些程序,子进程做一件事(从串口读取,写入终端),而父进程继续做其他事情(从终端读取,写入串口)。另一个经典的例子是子进程对正在发生的任何长时间运行的计算执行检查点。大多数情况下,子进程会执行一些设置,例如更改目录、重置信号处理程序或重置文件描述符,然后调用execve()以使用不同的代码覆盖自身。
  2. fork()并且exec()确实适用于所有可执行文件 - 事实上,与 argc 和 argv、管道、fork 和 exec 一起,是 Unix 与其他操作系统的区别。存在一些专门化或概括fork(),例如 BSD vfork()、Plan 9rfork()和 Linux clone(),但原理保持不变。
  3. “写时复制”并没有真正向用户显示,它更多的是一种优化创建子进程及其执行过程的技术。调用堆栈和堆(用 分配的内存malloc(),甚至静态或全局范围变量)可能是“写时复制”。当通过调用创建子进程时fork(),内核会将子进程设置为与父进程具有完全相同的内存页面(堆和堆栈)。如果硬件(内存管理单元)检测到堆或堆栈的写入,内核将获取新的物理内存页面,将父进程的页面复制到新页面,并将该新页面映射到子进程的堆栈或堆中。这构成了一种优化,因为内核花在设置页面映射上的时间少于为子进程完全复制堆栈和堆的时间。

答案3

如果子进程包含了父进程(即原始进程)的所有属性,那么这个子进程还需要什么呢?原始进程也可能已加载到内存中。

通过看一下最早的 Unix 实现就可以很好地回答这个问题,这些实现必须在严格的内存限制下工作,并且内存/地址空间中一次只有一个执行进程。

多任务处理是通过将一个进程换出到磁盘并换入另一个进程来实现的。

现在fork系统调用几乎是一样的:它将一个进程换出到磁盘,但不是将另一个进程换入,而是为内存中的副本提供了另一个进程 ID 并返回给它。exec毕竟,这是该进程决定进入另一个可执行文件的合适时机。

fork+exec因此,实际上并没有在生成过程中产生明显的开销:无论如何,您都必须将进程交换到磁盘,并且无论如何,旧的进程映像都位于可用的内存位置中。

随着可用内存和内存管理单元以及多个内存进程的数量不断增加,最初可以忽略不计的分叉成本对于某些架构来说变得更加麻烦:因此vfork诞生了。

答案4

为了使这一点尽可能容易理解,我将使用一个类比。我们来烤个馅饼吧!

我们拿起食谱书,开始阅读,然后选择了草莓大黄派(我最喜欢的)和手工制作的外壳。除了鸡蛋和水果之外,我们需要的几乎所有东西都在厨房里,但由于我们住在农场,而且水果是应季的,所以这不是问题。问题是烤箱坏了,没有足够的时间做每件事。拥有多个我不是很好吗?

fork() 来救援。现在有两个我了。我们俩都去厨房开始做馅饼皮。哎呀。所以我们看看 fork 的返回。我得到了一个很大的数字,他得到了零,所以我去了厨房,而他则去了鸡舍和花园。当我走过烤箱时,我再次 fork() ,看看返回值:真糟糕,我得到了零。当我盯着坏掉的烤箱时,他继续吃面粉。我打开门,没有光,我关上门。有谁知道怎么修烤箱吗?

exec() 来救援。我伸手去拿工具带上的电压表,灯泡可以进行诊断,所以我检查了电源,确实跳闸了断路器,很容易修复。当我走到断路器面板时,我看到一个人在摘大黄。哎呀!我更喜欢巧克力丝派。

相关内容