为什么shell脚本(或任何进程)的子进程继承其父进程的标准输入?

时间:2016-06-16 21:21:33

标签: linux file shell unix

我有一个像这样被调用的Bash脚本:

script.sh < input_file.txt

所有script.sh都运行其他程序:

#!/bin/bash
otherprogram 

现在&#34;其他程序&#34;从stdin读取,它从input_file.txt获取文本,而不需要将script.sh的标准输入显式重定向到其他程序。

我不太了解进程是如何开始的,但我已经读过,当fork()被调用时,来自父进程的所有文件描述符(包括stdin)都与子进程共享 - 这使得感觉,因为fork()只是在父进程中创建了所有内容的相同副本。记忆。但是为什么在子进程用新程序替换父进程的副本之后仍然可以共享所有文件描述(可能是通过调用exec ...())?

如果子进程总是从父进程继承所有文件描述符,有人可以解释为什么这实际上有意义并且是个好主意吗?

2 个答案:

答案 0 :(得分:2)

当调用fork时()PCB的大多数字段(过程控制块)都是从原始字段复制到新创建的PCB(正如你所说的那样,也是打开的文件)。

总结一下,执行fork之后立即:

  • 除了fork(2)手册页中描述的差异外,有2个进程完全相同,如果你想看一下。

  • 两个进程都在同一行代码中(紧接在fork之后的行)。

  • 在子进程中,fork的返回值为0.

  • 在父进程中,fork的返回值大于0.

让我们转到执行官:

所以我们现在有两个shell副本。但他们仍然在运行shell程序;我们希望孩子运行任何程序。子进程使用exec替换自己作为参数传递的程序。 Exec不会创建新流程;它只是更改现有进程正在运行的程序文件。

因此exec首先清除调用进程的内存状态。然后进入文件系统查找所请求的程序文件,并将该文件复制到程序的内存中并初始化寄存器状态,包括PC。

exec不会改变PCB中的大多数其他字段 - 这很重要,因为这意味着调用exec的进程可以根据需要进行设置,例如更改打开的文件,如子复制继承了指向输入文件的stdin的文件描述符的情况。

另一个例子可以是:
您希望子进程在标准输出(2)上打印时,例如通过回显,它实际上打印在文件上。你可以做的是在调用exec(父进程)之前改变文件描述符2指向的位置(使用例如dup2()),然后在子进程中调用fork()ad和exec。

答案 1 :(得分:0)

在Unix和相关操作系统中,启动程序的一般方法是:

  • 处理分叉。
  • 子进程使得需要对环境进行任何更改,例如(可能):
    • 更改文件描述符指向的位置(尤其是stdin / stdout / stderr)。
    • 更改正在运行的用户。
    • 更改当前的工作目录。
    • 更改/添加/删除环境变量。 (虽然您也可以在下一步中执行此操作。)
  • 然后,子进程将替换为所需的程序。

这种方法具有很大的灵活性;父程序可以准确地决定应该和不应该继承什么。 (它也非常适合Unix哲学,做一件事并做得很好:exec不需要50个bajillion参数来处理你可能希望新程序拥有的不同的东西,因为你只需要设置它们切换到该程序之前的过程。)

它还可以很容易地编写“包装”程序,将几乎所有功能委托给其他程序。例如,像这样的脚本:

#!/bin/bash

cd /directory/that/foo/requires/
foo "$@"

这是foo的替代品,它只是更改了其运行的目录,不必担心foo 应该继承的所有内容,只有不应该的一件事。 (这再次与Unix哲学非常吻合。)