為什麼 sudo 不總是產生一個 shell 子程序?
我試圖理解為什麼某些 shell 在使用sudo呼叫時似乎得到了特殊處理。例如,似乎有兩種可能的行為:
“隱式”組(pstree是sudo的直接子代,中間沒有外殼):
$ sudo pstree -s $$ systemd───login───bash───sudo───pstree $ sudo bash -c 'pstree -s $$' systemd───login───bash───sudo───pstree $ sudo zsh -c 'pstree -s $$' systemd───login───bash───sudo───pstree $ sudo dash -c 'pstree -s $$' systemd───login───bash───sudo───pstree
“顯式”組(shell 是sudo的直接子代):
$ sudo ksh -c 'pstree -s $$' systemd───login───bash───sudo───ksh───pstree $ sudo tcsh -c 'pstree -s $$' systemd───login───bash───sudo───tcsh───pstree $ sudo fish -c 'pstree -s $fish_pid' systemd───login───bash───sudo───fish───pstree
顯然sudo和一些 shell之間似乎發生了某種集成,但我找不到關於它的文件。我還 grepped 了sudo和bash的原始碼,但也找不到任何線索。
另一個問題似乎相關:為什麼 (…) 在後台執行時不會產生新的子程序?
我的sudo和bash版本是:
$ sudo --version Sudo version 1.8.29 ... $ bash --version GNU bash, version 5.1.16(1)-release (x86_64-pc-linux-gnu) ...
不,這不是你的 shell 和 sudo 之間的互動;就是負責執行的 shell 將自己替換為您執行的命令!
您可以從整個事情中刪除 sudo 以獲得相同的結果;例如,我在 alacritty 終端中執行 zsh:
$> bash -c 'pstree -s $$' systemd───alacritty───zsh───pstree
這裡沒有bash!
我們可以通過執行來驗證發生了什麼
$> strace -o /tmp/bash-pstree.strace bash -c 'pstree -s $$' systemd───alacritty───zsh───pstree $> bat /tmp/bash-pstree.strace # or just less /tmp/.... ; bat is just a nice code highlighter
在那裡我們看到
$> grep execve /tmp/bash-pstree.strace execve("/usr/bin/bash", ["bash", "-c", "pstree -s $$"], 0x7ffc02c52d30 /* 102 vars */) = 0 execve("/usr/bin/pstree", ["pstree", "-s", "35735"], 0x55b90ec78d00 /* 102 vars */) =
所以,第一個 execve 是 bash 被呼叫,第二個是 bash 用 pstree 替換自己 - 之後實際上沒有 bash 存在!兩者之間不會發生分叉/複製。
當然,這只適用於命令鏈中的*最後一個命令。*如果我們將自己替換為之前要執行的命令,之後我們將無法執行任何操作。我們實際上可以很容易地驗證這一點:
$> bash -c 'pstree -p -s $$; pstree -p -s $$' systemd(1)───alacritty(34604)───zsh(34609)───bash(39257)───pstree(39258) systemd(1)───alacritty(34604)───zsh(34609)───pstree(39257)
看,這裡第一個 pstree 實際上是在 bash 創建的程序中執行的,第二個由同一程序執行,替換 bash。
用它執行的程序替換 shell 當然是好的,在資源方面:我們在早期就產生了所有的文件句柄記憶體、鎖等。
我不知道為什麼有些 shell 會這樣做,而其他 shell 則不會(他們可能使用
fork
或clone
複制自己的程序,然後再execve
執行指定的命令)。可能,這是開發人員從未想到過的優化(如果你有一個fish
像,或者引入特殊情況在軟體架構上很尷尬,或者這是不可能的,因為外殼對啟動的程序保持一些控制,因此仍然需要存在才能接收信號或進行 IPC。當然,有些 shell 非常陳舊,因此可能有點簡單(上一個ksh
版本是 10 年前的!)。