当我们在 shell 上输入一个简单的命令时会发生什么?

MS.*_*Kim 38 shell process subshell

我有一个关于执行简单命令的简单问题。根据我的理解,当我们ls在交互式 shell 中键入命令时,

  1. Shell 解释命令。
  2. Shell 创建子进程并在子进程上执行命令。
  3. Shell 等待子进程完成。

如果我的理解是正确的,我们在 shell 提示符下键入的一个简单命令会在子进程上执行,并且该命令的结果不会影响当前 shell 的环境。

如果是这样,那么内置命令cd呢?如果cd在子进程上执行并且不能影响当前shell的环境,那么如何更改当前shell的工作目录?

gol*_*cks 36

shell 只是一个程序,虽然它在系统中扮演着重要的角色。Bash 和我假设大多数其他常见的 shell 都是用 C 实现的。用于创建子进程的两个最重要的本地 C系统调用fork()exec()。这些函数通常也用高级语言实现,包括 shell。

  1. fork()

    “Fork”创建调用进程的副本作为其子进程。这就是系统上除第一个(init)之外的几乎所有进程开始的方式:作为启动它们的进程的副本。Shell 语言实际上没有fork函数,但它确实包含生成子shell 的语法,它们是相同的。

  2. exec()

    exec()C 中实际上没有调用,但它通俗地指的是一组相关的函数;您可以看到带有 的列表man 3 exec,通常以 开头:

    exec() 系列函数用新的进程映像替换当前进程映像...

    这正是它所做的:从可执行文件(例如,/usr/bin/ls)加载的新内容替换当前进程内存堆栈的定义部分。这就是为什么fork()在创建一个新进程时首先是必要的——否则,调用进程不再是它原来的样子,而是变成别的东西;实际上不会创建新进程。

乍一看,这听起来像是一种荒谬且低效的做事方式:为什么不直接创建一个从头开始创建新进程的命令呢?事实上,由于以下几个原因,这可能不会那么有效:

  • 产生的“复制”fork()有点抽象,因为内核使用写时复制系统;真正需要创建的只是一个虚拟内存映射。如果复制然后立即调用exec(),则大部分数据如果已被流程活动修改而将被复制,则实际上不必复制/创建,因为流程不执行任何需要使用它的事情。

  • 子进程的各个重要方面(例如,其环境)不必根据对上下文的复杂分析等单独复制或设置。它们只是假设与调用进程的相同,并且这是我们熟悉的相当直观的系统。

有关将“环境复制”到生成的子进程的确切含义的详细讨论,请参阅我的答案here

如果是这样,那么像 cd 这样的内置命令呢?

同样,这些只是在 C 中实现的 chdir(),就像fork()和一样exec(),是标准 C 的 Unix 平台扩展的一部分,也是 shellcd命令的基础。来自man 2 chdir

chdir() 将调用进程的当前工作目录更改为 path 中指定的目录。

这不需要子进程——它会影响调用者。shell 是一个交互式运行时解释器,这意味着它会在您提供命令时执行用 shell 语言编写的代码。它通常不需要执行新进程来执行此操作,它会自行完成。


str*_*gee 8

如果是这样,那么像 cd 这样的内置命令呢?如果在子进程上执行 cd

这就是你逻辑的缺陷。cd,作为 shell 内置函数,由 shell 专门解释并在进程内“执行”。您可以使用该type命令来确定某个命令是否内置。