文章出處

我們知道,Bash 在執行一個外部命令時,會先 fork() 一個子進程,然后在子進程里面執行 execve() 去加載那個外部程序。fork 子進程是會耗性能的,所以 Bash 會在下面幾種情況下不 fork 子進程,直接在當前進程執行 execve()。

bash -c 'command'

如果用了 bash -c 的形式啟動 Bash,同時 -c 選項的參數里只包含一個命令,比如 bash -c 'sleep 666',這時 Bash 不會 fork 子進程去運行 sleep 命令,它會讓 sleep 直接占用自己現有的進程:

$ bash -c 'sleep 666' &

$ pstree -ap

...

|  `-bash,3117

|      |-pstree,3119 -ap

|      `-sleep,3118 666

...

3117 是我當前敲入命令的交互 Shell,3118 就是 bash -c 啟動的那個進程,然后直接被替換成了 sleep,pid 還是 3118。

我們可以看一下 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 命令。


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()