为什么 xtrace 显示管道命令乱序执行?
Why does xtrace show piped commands executing out of order?
这是一个简单的复制器:
cat >sample_pipeline.sh << EOF
set -x
head /dev/urandom | awk '{$2=$3=""; print $0}' | column -t | grep 123 | wc -l >/dev/null
EOF
watch -g -d -n 0.1 'bash sample_pipeline.sh 2>&1 | tee -a /tmp/watchout'
wc -l /tmp/watchout
tail /tmp/watchout
正如预期的那样,通常命令按照它们写入的顺序执行:
+ head /dev/urandom
+ awk '{==""; print [=11=]}'
+ column -t
+ grep 123
+ wc -l
...但有时顺序不同,例如awk
在 head
之前:
+ awk '{==""; print [=12=]}'
+ head /dev/urandom
+ column -t
+ grep 123
+ wc -l
我能理解 shell 预生成进程是否等待输入,但为什么不按顺序生成它们?
将 bash
替换为 dash
(默认 Ubuntu shell)似乎具有相同的行为。
当您编写这样的管道时,shell 将分叉一堆 child 进程到 运行 所有子命令。每个 child 进程将在调用 exec 之前打印它正在执行的命令。由于每个 child 都是一个独立的进程,OS 可能会以任何顺序安排它们,并且发生的各种事情(CPU 核心之间的缓存 misses/thrashing 可能会延迟一些 child人而不是人。所以消息发出的顺序是不可预测的。
发生这种情况是因为管道的实施方式。 shell 将首先派生 N 个 subshell,每个 subshell 将(在计划时)打印出其 xtrace 输出并调用其命令。因此输出的顺序是比赛的结果。
你可以看到与
类似的效果
for i in {1..5}; do echo "$i" & done
即使每个 echo
命令按顺序生成,输出仍可能是 3 4 5 2 1
。
这是一个简单的复制器:
cat >sample_pipeline.sh << EOF
set -x
head /dev/urandom | awk '{$2=$3=""; print $0}' | column -t | grep 123 | wc -l >/dev/null
EOF
watch -g -d -n 0.1 'bash sample_pipeline.sh 2>&1 | tee -a /tmp/watchout'
wc -l /tmp/watchout
tail /tmp/watchout
正如预期的那样,通常命令按照它们写入的顺序执行:
+ head /dev/urandom
+ awk '{==""; print [=11=]}'
+ column -t
+ grep 123
+ wc -l
...但有时顺序不同,例如awk
在 head
之前:
+ awk '{==""; print [=12=]}'
+ head /dev/urandom
+ column -t
+ grep 123
+ wc -l
我能理解 shell 预生成进程是否等待输入,但为什么不按顺序生成它们?
将 bash
替换为 dash
(默认 Ubuntu shell)似乎具有相同的行为。
当您编写这样的管道时,shell 将分叉一堆 child 进程到 运行 所有子命令。每个 child 进程将在调用 exec 之前打印它正在执行的命令。由于每个 child 都是一个独立的进程,OS 可能会以任何顺序安排它们,并且发生的各种事情(CPU 核心之间的缓存 misses/thrashing 可能会延迟一些 child人而不是人。所以消息发出的顺序是不可预测的。
发生这种情况是因为管道的实施方式。 shell 将首先派生 N 个 subshell,每个 subshell 将(在计划时)打印出其 xtrace 输出并调用其命令。因此输出的顺序是比赛的结果。
你可以看到与
类似的效果for i in {1..5}; do echo "$i" & done
即使每个 echo
命令按顺序生成,输出仍可能是 3 4 5 2 1
。