bash 是否创建一个子进程来运行每个命令

bash 是否创建一个子进程来运行每个命令

每次我们ps多次运行一个普通命令时,它都有不同的进程 ID (PID)。我想知道这是否意味着我们在终端(bash)中运行的每个命令都会创建一个 bash 子进程来执行该命令。

答案1

是的,几乎您在命令行上运行的每个命令都在其自己的进程中运行,并且这些进程是启动它们的 shell 的子进程。

这里的例外是 shell 的内置命令。 Bash 本身实现了一些标准实用程序,例如printfechotruefalse和/ ,因此运行这些实用kill程序并不涉及分叉子项。这同样适用于诸如、和 之类的东西,尽管它们会影响 shell 的内部状态,因此它们必须是内置的。[testcdreadmapfile

(此外breakcontinue、 和return,奇怪的是它们是内置实用程序,而不是像if和 那样的 shell 关键字while。)

shell 确实没有办法在与自身相同的进程中运行外部程序,同时仍然能够返回。然而,外壳有可能代替本身与另一个程序。例如,如果您运行echo $$查看 shell 的 PID,然后运行exec ps​​,您将看到ps使用相同的 PID 运行。当ps退出时,该 shell 不再存在。实际上,每次以正常方式运行程序时都会发生类似的情况,只是 shell 在fork()用要运行的程序替换子程序 ( execve()) 之前先对其自身进行复制(系统调用)。在子进程中运行的 shell 程序之间,负责为子进程设置任何重定向等。

shell 也可以将其他工具实现为内置工具,例如 Busybox 实现是同一程序文件中较大的标准实用程序集。但据我测试,它在运行它们时仍然分叉一个子进程,可能是因为这是确保实用程序不会不必要地扰乱 shell 状态的简单方法。

答案2

是的,为了几乎任何命令[1]。

除了作为内置命令实现的命令之外,您可以help在 bash [2] 中获得这些命令的列表。

请注意,bash 在运行子 shell 时也会分叉,即使它们只运行内置命令;例如,(echo a | read p)除了运行主 shell 的进程之外,您还有 3 个独立的进程。

[1] 第一个 Unix shell(Thompson shell)的最初想法是全部命令应该是单独的二进制文件并作为单独的进程运行。然而,设计限制和实现怪癖导致它很快崩溃,因为首先chdir必须作为内置实现,然后是变量分配,等等。更多的炮弹越来越侵蚀了最初的想法。

[2] 即使在运行 buitins 时,bash 也会尝试模仿单独的进程;例如,重定向 from 的echo > file处理方式与 from 的方式完全不同(而且非常复杂),ls > file以便维持echo作为单独命令运行的错觉,并且重定向不适用于整个 shell,而仅适用于命令echo。 Bash 并不总是能做到这一点;例如,在

bash -c 'trap "echo Ctrl-C" INT; read p; echo DONE'

陷阱INT不会被执行read命令已返回,就像任何其他命令一样(其他 shell 如 dash 确实可以做到这一点)。此外,阻塞内置echo可能会返回一个愚蠢的“中断系统调用”,这是外部/bin/echo永远不会做的事情(因为echo不需要也不设置任何信号处理程序;-))——以及数千个类似的情况拼凑的痕迹通过接缝显现出来。

注意:busybox 与此无关——busybox 是多调用二进制文件的一个示例,它是一个可以根据第一个命令行参数执行不同功能的程序;就像ex/viod/hexdump等一样。但是,busybox 中的小程序(包括它的 shellashhush)能够运行一些其他小程序作为相同的进程内置程序,如果明确以这种方式编译(NOFORK/NOEXEC小程序)。

答案3

您在 bash 中运行的大多数正常事物确实会获得新的 PID,因为它们是不同的进程(进程以给定的 PID 启动,运行,然后完成,PID 可用于新进程...最终...检查zombie processes) 。有一些内置程序直接由 bash 运行,而不创建新进程(例如 echo)....然后有一些创建 bash 子进程的东西(每个子进程都有自己的 PID,例如while read,这不允许你在 while 内部设置变量的值以便在外部使用它,因为新值在while完成后就会消失)。

相关内容