据我了解管道和命令,bash 接受每个命令,为每个命令生成一个进程,并将前一个命令的标准输出与下一个命令的标准输入连接起来。
例如,在“ls -lsa | grep feb”中,bash将创建两个进程,并将“ls -lsa”的输出连接到“grep feb”的输入。
当您在 bash 中执行“sleep 30 &”等后台命令时,您将获得运行该命令的后台进程的 pid。让我惊讶的是,当我写“ls -lsa | grep feb &”时,bash 仅返回一个 PID。
这应该如何解释呢?一个进程同时运行“ls -lsa”和“grep feb”?创建了多个进程,但我只获得其中一个进程的 pid?
答案1
当您在后台运行一项作业时,bash 会打印其子进程的进程 ID,即在该作业中运行命令的进程 ID。如果该作业碰巧创建了更多子进程,则与父 shell 无关。
当后台作业是管道时(即命令的形式为something1 | something2 &
,而不是 eg { something1 | something2; } &
),POSIX 强烈建议并由包括 bash 在内的大多数 shell 执行优化:管道的每个元素都直接作为子进程执行原来的外壳。 POSIX 的要求是多变的$!
在本例中设置为管道中的最后一个命令。在大多数 shell 中,最后一个命令是原始进程的子进程,管道中的其他命令也是如此。
当您运行 时ls -lsa | grep feb
,会涉及三个进程:一个运行管道左侧的进程(完成管道设置然后执行 的子 shell ls
),一个运行管道右侧的进程(一个子 shell)完成管道设置然后执行grep
),以及等待管道完成的原始进程。
您可以通过跟踪进程来观察发生了什么:
$ strace -f -e clone,wait4,pipe,execve,setpgid bash --norc
execve("/usr/local/bin/bash", ["bash", "--norc"], [/* 82 vars */]) = 0
setpgid(0, 24084) = 0
bash-4.3$ sleep 10 | sleep 20 &
…
请注意第二个sleep
是如何报告和存储的$!
,但进程组 ID 是第一个sleep
。 Dash 有同样的奇怪之处,而 ksh 和 mksh 则没有。
答案2
产生 2 个进程。显示&
第二个进程的PID。下面的例子。
$ echo $$
13358
$ sleep 100 | sleep 200 &
[1] 13405
$ ps -ef|grep 13358
ec2-user 13358 13357 0 19:02 pts/0 00:00:00 -bash
ec2-user 13404 13358 0 19:04 pts/0 00:00:00 sleep 100
ec2-user 13405 13358 0 19:04 pts/0 00:00:00 sleep 200
ec2-user 13406 13358 0 19:04 pts/0 00:00:00 ps -ef
ec2-user 13407 13358 0 19:04 pts/0 00:00:00 grep --color=auto 13358
$