為什麼後台控制操作符(&)創建的子shell不顯示在pstree下
我知道當我執行
exit
它時它會終止我目前的 shell,因為exit
命令在同一個 shell 中執行。我也明白,當我執行時,exit &
原始 shell 不會終止,因為&
確保命令在子 shell 中執行,結果exit
將終止這個子 shell 並返回到原始 shell。但我不明白的是為什麼有和沒有的命令在 下&
看起來完全一樣pstree
,在這種情況下sleep 10
和sleep 10 &
。4669 是 bash 的 PID,在該 PID 下首先發出sleep 10
然後sleep 10 &
發出,並且在此期間從另一個 shell 實例獲得以下輸出:# version without & $ pstree 4669 bash(4669)───sleep(6345) # version with & $ pstree 4669 bash(4669)───sleep(6364)
版本不應該
&
包含更多生成的子外殼(例如,在這種情況下,PID 為 5555),就像這個?bash(4669)───bash(5555)───sleep(6364)
pstree
PS:為了更好的可讀性,從開頭的輸出中省略了以下程式碼:systemd(1)───slim(1009)───ck-launch-sessi(1370)───openbox(1551)───/usr/bin/termin(4510)───bash(4518)───screen(4667)───screen(4668)───
在我開始回答這個問題之前,我還沒有意識到使用
&
控制操作符在後台執行*作業會啟動一個子shell。*當命令包含在括號中或構成管道的一部分時,將創建子外殼(管道中的每個命令都在其自己的子外殼中執行)。Bash 手冊的命令列表部分(感謝 jimmij)指出:
如果命令被控制操作符“&”終止,shell 將在子 shell 中非同步執行命令。這稱為在後台執行命令。shell 不等待命令完成,返回狀態為 0(真)。
據我了解,當您執行
sleep 10 &
shell fork創建一個新的子程序(自身的副本),然後立即execsleep
用外部命令( )中的程式碼替換該子程序時。這類似於正常執行命令(在前台)時發生的情況。有關此機制的簡短概述,請參閱Fork-exec Wikipedia 文章。我不明白為什麼 Bash 會在子 shell 中執行後台命令,但如果您還希望能夠執行 shell 內置命令,例如
exit
或echo
在後台執行(不僅僅是外部命令),這是有道理的。當它是在後台執行的內置 shell 時,
fork
會發生(導致子 shell)而無需exec
呼叫以用外部命令替換自身。執行以下命令表明,當echo
命令用花括號括起來並在後台執行(帶有&
)時,確實創建了一個子shell:$ { echo $BASH_SUBSHELL $BASHPID; } 0 21516 $ { echo $BASH_SUBSHELL $BASHPID; } & [1] 22064 $ 1 22064
在上面的範例中,我將
echo
命令用大括號括起來以避免BASH_SUBSHELL
被目前 shell 擴展;花括號用於在不使用子shell 的情況下將命令組合在一起。該命令的第二個版本(以&
控制運算符結尾)清楚地表明,用 & 號終止命令會導致創建一個子 shell(帶有新的 PID)來執行echo
內置命令。(我可能在這裡簡化了 shell 的行為。請參閱 mikeserv 的評論。)如果我沒有閱讀您的問題,我永遠不會想到執行
exit &
,我會期望目前的 shell 退出。現在知道這些命令是在子shell中執行的,您對退出的是子shell的解釋是有道理的。“為什麼後台控制操作符(&)創建的子shell不顯示在pstree下”
如上所述,當您執行時
sleep 10 &
,Bash 會自行創建子 shell,但由於sleep
是外部命令,因此它會呼叫系統呼叫,該exec()
系統呼叫會立即將子程序中的 Bash 程式碼和數據替換為程序的執行副本sleep
。執行pstree
時,exec
呼叫已經完成,子程序現在將具有名稱“ sleep ”。在遠離我的電腦時,我試圖想辦法讓子shell 執行足夠長的時間,以使子shell 顯示為
pstree
. 我想我們可以通過time
內置命令執行命令:$ time sleep 11 & [2] 4502 $ pstree -p 26793 bash(26793)─┬─bash(4502)───sleep(4503) └─pstree(4504)
在這裡,Bash shell (26793) 派生出一個子 shell (4502),以便在後台執行命令。這個子shell執行它自己的
time
內置命令,反過來,fork(創建一個PID為4503的新程序)和execs來執行外部sleep
命令。使用命名管道,jimmij想出了一個聰明的方法來保持創建的子shell 執行
exit
足夠長的時間以使其顯示pstree
:$ mkfifo file $ exit <file & [2] 6413 $ pstree -p 26793 bash(26793)─┬─bash(6413) └─pstree(6414) $ echo > file $ jobs [2]- Done exit < file
從命名管道重定向
stdin
很聰明,因為它會導致子shell 阻塞,直到它接收到來自命名管道的輸入。稍後,重定向echo
(不帶任何參數)的輸出會將換行符寫入命名管道,從而解除對子shell 程序的阻塞,而子shell 程序又執行exit
內置命令。同樣,對於
sleep
命令:$ mkfifo named_pipe $ sleep 11 < named_pipe & [1] 6600 $ pstree -p 26793 bash(26793)─┬─bash(6600) └─pstree(6603)
在這裡,我們看到為在後台執行命令而創建的子 shell 的 PID 為
6600
. 接下來,我們通過向管道寫入換行符來解除程序阻塞:$ echo > named_pipe
然後子shell
exec
s 執行sleep
命令。$ pstree -p 26793 bash(26793)─┬─pstree(6607) └─sleep(6600)
呼叫後
exec()
,我們可以看到子程序 (6600
) 現在正在執行sleep
程序。