比较直接运行命令和在后台运行命令

比较直接运行命令和在后台运行命令

在 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)

问题:

  1. 就运行命令而言date,直接运行它和在后台运行它似乎做同样的事情:

    直接运行命令时,原始 bash shell 6913 clone()本身,然后是其克隆 12918execve()命令。

    当在后台运行该命令时,原始 bash shell 6913 clone()本身,然后是其克隆 12931execve()该命令。

    两者clone()都只调用一次。它们之间有什么区别?

  2. 命令是外部的还是内置的并不重要,例如,运行date&和运行echo $b &都调用clone()一次。为什么会有这样的差异呢?

  3. 比较跑步date &{ date; } &. Bash 手册说大括号不会创建子 shell。为什么他们的跟踪输出不同:

    • 在 的跟踪输出中date&,只有一个克隆(6913clone()创建 12931)和克隆 12931 execve() date
    • 在 的跟踪输出中{ date; }&,有两个克隆(6913clone()创建 31294,31294clone()创建 31295)和最后一个克隆 31295execve() date
  4. 据说在后台运行的命令(例如date&)可以访问父 shell 中的局部变量,因为它运行在父 shell 的子 shell 中,而直接运行的命令则不能(例如date),因为它不在父 shell 的子 shell 中运行。我们可以使用跟踪的输出来解释差异吗?

    $ b=1
    $ { echo $b; } &
    [1] 31214
    1
    

    b是父 shell 的本地变量,而不是环境变量。子 shell由父 shell 的&副本创建,因此与父 shell 的值相呼应。我创建了这个例子,来自bb1另一个它使用命令替换而不是后台,两者都会创建一个子 shell 来运行命令。

相关内容