“子shell”和“子进程”之间的确切区别是什么?

“子shell”和“子进程”之间的确切区别是什么?

根据,子 shell 通过使用括号开始(…)

( echo "Hello" )

根据,,当命令以a结束时,进程被分叉&

echo "Hello" &

Posix 规范的使用subshell此页中的单词但没有定义它,而且在同一页上,没有定义“子进程”

两者都使用核fork()函数,对吗?

将某些分叉称为“子外壳”而将其他分叉称为“子进程”的确切区别是什么?

答案1

在 POSIX 术语中,子 shell 环境与以下概念相关联:Shell执行环境

子 shell 环境是作为父环境的副本创建的单独 shell 执行环境。该执行环境包括打开的文件、umask、工作目录、shell 变量/函数/别名等内容...

对该子 shell 环境的更改不会影响父环境。

传统上,在 POSIX 规范所基于的 Bourne shell 或 ksh88 中,这是通过派生子进程来完成的。

POSIX 要求或允许命令在子 shell 环境中运行的区域是传统 ksh88 派生子 shell 进程的区域。

然而,它并不强制实现为此使用子进程。

shell 可以选择以任何他们喜欢的方式实现该单独的执行环境。

例如,ksh93 通过保存父执行环境的属性并在可以避免分叉的上下文中的子 shell 环境终止时恢复它们来实现这一点(作为一种优化,因为分叉在大多数系统上相当昂贵)。

例如,在:

cd /foo; pwd
(cd /bar; pwd)
pwd

POSIX 确实要求cd /bar在单独的环境中运行并输出如下内容:

/foo
/bar
/foo

它不需要它在单独的进程中运行。例如,如果 stdout 成为损坏的管道,则pwd在子 shell 环境中运行很可能会将 SIGPIPE 发送到唯一的 shell 进程。

大多数 shell 都bash将通过评估子进程中的代码(...)(而父进程等待其终止)来实现它,但 ksh93 会在运行内部代码时执行(...),所有这些都在同一个进程中:

  • 请记住它是在子 shell 环境中。
  • 之后cd,保存先前的工作目录(通常在使用 O_CLOEXEC 打开的文件描述符上),保存 OLDPWD、PWD 变量的值以及任何cd可能修改的内容,然后执行chdir("/bar")
  • 从子 shell 返回后,当前工作目录将被恢复(带有fchdir()保存的 fd),以及子 shell 可能已修改的其他所有内容。

在某些情况下,子进程是无法避免的。 ksh93 不分叉:

  • var=$(subshell)
  • (subshell)

但确实在

  • { subshell; } &
  • { subshell; } | other command

也就是说,事物必须在单独的进程中运行才能同时运行。

ksh93 的优化远不止于此。例如,当在

var=$(pwd)

大多数 shell 会分叉一个进程,让子进程运行命令pwd,并将其 stdout 重定向到管道,pwd将当前工作目录写入该管道,然后父进程读取管道另一端的结果,ksh93虚拟化所有这些需要叉子或管子。叉子和管道仅用于非内置命令。

请注意,除了子 shell 之外,还有其他上下文可供 shell 派生子进程。例如,要运行存储在单独的可执行文件中的命令(并且该命令不是用于同一 shell 解释器的脚本),shell 必须分叉一个进程才能在其中运行该命令,否则就不会该命令返回后能够运行更多命令。

在:

/bin/echo "$((n += 1))"

这不是一个子shell,该命令将在当前shell执行环境中计算,n当前shell执行环境的变量将增加,但shell将分叉一个子进程来/bin/echo在其中执行该命令,并扩展为$((n += 1))参数。

shell -c 'inline-script'许多 shell 实现了一种优化,如果它是内联脚本 ( ) 或子 shell(对于那些作为子进程实现的子 shell)的最后一个命令,它们不会派生子进程来运行该外部命令。 (bash但是,仅当该命令是子 shell 的唯一命令时才会执行此操作)。

这意味着,对于这些 shell,如果子 shell 中的最后一个命令是外部命令,则子 shell 不会导致生成额外的进程。如果比较的话:

a=1; /bin/echo "$a"; a=2; /bin/echo "$a"

a=1; /bin/echo "$a"; (a=2; /bin/echo "$a")

将创建相同数量的进程,仅在第二种情况下,第二个 fork 提前完成,以便a=2在子 shell 环境中运行。

答案2

两者(子 shell 和子 shell)都是与父 shell 不同的独立进程(两者都是父 shell 的子代)。也就是说,它们有不同的PID。两者都以父 shell 的分支(副本)开始。

子 shell 是父 shell 的副本,其中变量、函数、标志和所有内容都像在父 shell 中一样可用。此类值的修改不会影响父级。

子 shell 作为 fork 启动,但它会重置为启动配置给定的 shell 默认值。它成为用于执行某些代码(shell 或命令)的进程。

子 shell 可以访问变量值:

$ x=123; ( echo "$x")
123

子 shell 不能(未导出的变量):

$ x=234; sh -c 'echo "x=$x"'
x=

答案3

子壳

子shell也称为子shell。子 shell 可以从父 shell 和另一个 shell 创建。可以使用以下命令创建子shell:

1. 进程列表

进程列表是用括号括起来的命令分组。例子:

( pwd ; (echo $BASH_SUBSHELL)) 

这将打印当前工作目录和生成的 shell 的数量。笔记调用 subshel​​l 的成本很高。

2. 协同处理

它在后台模式下生成一个子 shell,并在该子 shell 中执行命令。

coproc sleep 10

如果你输入jobs命令

[1]+  Running                 coproc COPROC sleep 10 &

您将看到睡眠作为后台进程在后台运行。

分叉子进程

计算中的子进程是由另一个进程创建的进程。每当执行外部命令时,就会创建一个子进程。此操作称为分叉。

$ps -f
UID        PID  PPID  C STIME TTY          TIME CMD  
umcr7     3647  3638  0 13:54 pts/0    00:00:00 bash
umcr7     3749  3647  0 13:59 pts/0    00:00:00 ps -f

ps -f外部命令一样(即外部命令,有时称为文件系统命令,是存在于 bash shell 之外的程序。)这将创建具有执行该命令的 bash shell 的父 id 的子进程。

相关内容