在 Ubuntu 中,我date
在一个 pid 为 6913 的交互式 bash shell 中运行,同时通过 .trace 从另一个交互式 bash shell 中跟踪该 bash shell strace
。
通过使用跟踪,我想了解如何date
直接运行(请参阅这里用于跟踪输出)和后台(请参阅这里用于跟踪输出):
运行时date
,在第二个 shell 中跟踪第一个 shell 6913 的输出为:
$ sudo strace -f -e trace=process -p 6913
[sudo] password for t:
Process 6913 attached
clone(Process 12918 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f457c05ca10) = 12918
[pid 6913] wait4(-1, <unfinished ...>
[pid 12918] execve("/bin/date", ["date"], [/* 66 vars */]) = 0
[pid 12918] arch_prctl(ARCH_SET_FS, 0x7ff00c632740) = 0
[pid 12918] exit_group(0) = ?
[pid 12918] +++ exited with 0 +++
<... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WSTOPPED|WCONTINUED, NULL) = 12918
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=12918, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, 0x7ffea6781518, WNOHANG|WSTOPPED|WCONTINUED, NULL) = -1 ECHILD (No child processes)
运行时date &
,在第二个 shell 中跟踪第一个 shell 6913 的输出为:
$ sudo strace -f -e trace=process -p 6913
Process 6913 attached
clone(Process 12931 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f457c05ca10) = 12931
[pid 12931] execve("/bin/date", ["date"], [/* 66 vars */]) = 0
[pid 12931] arch_prctl(ARCH_SET_FS, 0x7f530c5ee740) = 0
[pid 12931] exit_group(0) = ?
[pid 12931] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=12931, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG|WSTOPPED|WCONTINUED, NULL) = 12931
wait4(-1, 0x7ffea6780718, WNOHANG|WSTOPPED|WCONTINUED, NULL) = -1 ECHILD (No child processes)
运行时echo $b &
,在第二个 shell 中跟踪第一个 shell 6913 的输出为:
$ sudo strace -f -e trace=process -p 6913
Process 6913 attached
clone(Process 31319 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f457c05ca10) = 31319
[pid 31319] exit_group(0) = ?
[pid 31319] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=31319, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG|WSTOPPED|WCONTINUED, NULL) = 31319
wait4(-1, 0x7ffea6780718, WNOHANG|WSTOPPED|WCONTINUED, NULL) = -1 ECHILD (No child processes)
运行时{ date; } &
,在第二个 shell 中跟踪第一个 shell 6913 的输出为:
$ sudo strace -f -e trace=process -p 6913
Process 6913 attached
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f457c05ca10) = 31294
Process 31294 attached
[pid 31294] clone(Process 31295 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f457c05ca10) = 31295
[pid 31294] wait4(-1, <unfinished ...>
[pid 31295] execve("/bin/date", ["date"], [/* 67 vars */]) = 0
[pid 31295] arch_prctl(ARCH_SET_FS, 0x7f78b7f0b740) = 0
[pid 31295] exit_group(0) = ?
[pid 31295] +++ exited with 0 +++
[pid 31294] <... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 31295
[pid 31294] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=31295, si_status=0, si_utime=0, si_stime=0} ---
[pid 31294] wait4(-1, 0x7ffea67811d8, WNOHANG, NULL) = -1 ECHILD (No child processes)
[pid 31294] exit_group(0) = ?
[pid 31294] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=31294, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG|WSTOPPED|WCONTINUED, NULL) = 31294
wait4(-1, 0x7ffea6780718, WNOHANG|WSTOPPED|WCONTINUED, NULL) = -1 ECHILD (No child processes)
问题:
就运行命令而言
date
,直接运行它和在后台运行它似乎做同样的事情:直接运行命令时,原始 bash shell 6913
clone()
本身,然后是其克隆 12918execve()
命令。当在后台运行该命令时,原始 bash shell 6913
clone()
本身,然后是其克隆 12931execve()
该命令。两者
clone()
都只调用一次。它们之间有什么区别?命令是外部的还是内置的并不重要,例如,运行
date&
和运行echo $b &
都调用clone()
一次。为什么会有这样的差异呢?比较跑步
date &
和{ date; } &
. Bash 手册说大括号不会创建子 shell。为什么他们的跟踪输出不同:- 在 的跟踪输出中
date&
,只有一个克隆(6913clone()
创建 12931)和克隆 12931execve()
date
, - 在 的跟踪输出中
{ date; }&
,有两个克隆(6913clone()
创建 31294,31294clone()
创建 31295)和最后一个克隆 31295execve()
date
- 在 的跟踪输出中
据说在后台运行的命令(例如
date&
)可以访问父 shell 中的局部变量,因为它运行在父 shell 的子 shell 中,而直接运行的命令则不能(例如date
),因为它不在父 shell 的子 shell 中运行。我们可以使用跟踪的输出来解释差异吗?$ b=1 $ { echo $b; } & [1] 31214 1
这
b
是父 shell 的本地变量,而不是环境变量。子 shell由父 shell 的&
副本创建,因此与父 shell 的值相呼应。我创建了这个例子,来自b
b
1
另一个它使用命令替换而不是后台,两者都会创建一个子 shell 来运行命令。