Bash 的 no-fork 優化

咱們知道,Bash 在執行一個外部命令時,會先 fork() 一個子進程,而後在子進程裏面執行 execve() 去加載那個外部程序。fork 子進程是會耗性能的,因此 Bash 會在下面幾種狀況下不 fork 子進程,直接在當前進程執行 execve()。shell

bash -c 'command'

若是用了 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。

bash -c 'command1 && command2 || command3 ... && commandN'

在這種由若干個 && 和 || 把若干個簡單命令組成的的複合命令中,最右側的那個(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 以前沒有。

( command )

用顯示的子 shell 語法運行一個單獨的命令,好比 ( sleep 100 ),若是不進行優化的話,這裏應該先 fork 一個子 shell,而後這個子 shell 會再 fork 一個子子 shell 去運行 sleep,一共 fork 兩次,再極端點:( ( ( ( ( sleep 100 ) ) ) ) ),會產生一個 5 級的子 shell(( ( ( ( ( echo $BASH_SUBSHELL ) ) ) ) ) 的確會輸出 5),一共 fork 6次,然而 Bash 並不會這樣作,不管你嵌套了多少級,Bash 只會 fork 一次,只產生一個子進程,而後在這個進程裏執行 sleep 命令。

相關文章
相關標籤/搜索