咱們知道,Bash 在執行一個外部命令時,會先 fork() 一個子進程,而後在子進程裏面執行 execve() 去加載那個外部程序。fork 子進程是會耗性能的,因此 Bash 會在下面幾種狀況下不 fork 子進程,直接在當前進程執行 execve()。shell
若是用了 bash -c 的形式啓動 Bash,同時 -c 選項的參數裏只包含一個命令,好比 bash -c 'sleep 666',這時 Bash 不會 fork 子進程去運行 sleep 命令,它會讓 sleep 直接佔用本身現有的進程:bash
$ bash -c 'sleep 666' &性能 $ pstree -ap優化 ...進程 | `-bash,3117table | |-pstree,3119 -ap語法 | `-sleep,3118 666程序 ...command |
3117 是我當前敲入命令的交互 Shell,3118 就是 bash -c 啓動的那個進程,而後直接被替換成了 sleep,pid 仍是 3118。d3
咱們能夠看一下 Bash 沒法優化的狀況下,進程樹是什麼樣的:
$ bash -c 'sleep 666;ls' & $ pstree -ap ... | `-bash,3117 | |-bash,3120 -c sleep\040666;ls | | `-sleep,3121 666 | `-pstree,3122 -ap ... |
此次咱們給 -c 的參數包含了兩個命令,sleep 和 ls,因此 Bash 不能讓 sleep 佔用它的進程,由於執行完 sleep 它還得去執行 ls。
在這種由若干個 && 和 || 把若干個簡單命令組成的的複合命令中,最右側的那個(commandN)命令執行時(若是執行到的話)會進行 no-fork 優化:
$ bash -c 'sleep 1 || sleep 2 && sleep 666' & $ pstree -ap # 等 3 秒鐘後再執行這條 ... | `-bash,3117 | |-pstree,3126 -ap | `-sleep,3123 666 ... |
在 bash -c 'sleep 1 || sleep 2 && sleep 666' 這條命令中,一共產生過 3 個進程,bash -c 首先產生了一個進程 3123,而後 3123 又分別 fork 出兩個子進程 3124 和 3125 來分別執行 sleep 1 和 sleep 2,sleep 666 沒有產生新的進程,它和 bash -c 用了同一個進程,也就是 3123。這個優化在 Bash 4.4 以前沒有。
用顯示的子 shell 語法運行一個單獨的命令,好比 ( sleep 100 ),若是不進行優化的話,這裏應該先 fork 一個子 shell,而後這個子 shell 會再 fork 一個子子 shell 去運行 sleep,一共 fork 兩次,再極端點:( ( ( ( ( sleep 100 ) ) ) ) ),會產生一個 5 級的子 shell(( ( ( ( ( echo $BASH_SUBSHELL ) ) ) ) ) 的確會輸出 5),一共 fork 6次,然而 Bash 並不會這樣作,不管你嵌套了多少級,Bash 只會 fork 一次,只產生一個子進程,而後在這個進程裏執行 sleep 命令。