/proc/self/environ
在某些 shell 中的奇怪行為;到底是怎麼回事?
我在 Debian GNU/Linux 9 上。我知道
/proc
是特別的,我知道是什麼/proc/self
。這個命令
sh -c '/bin/cat /proc/self/comm - </proc/self/comm'
產量
cat sh
如果我使用
dash
而不是sh
. 但是bash
,ksh
或者zsh
結果是cat cat
取而代之
/proc/self/stat
的是,/proc/self/comm
我可以確認這兩個cat
-s 實際上是同一個過程。顯然外殼在引擎蓋下有所不同,沒關係。現在讓我們採取sh -c '/bin/cat /proc/self/environ - </proc/self/environ'
看了以上,
sh
還是dash
期待先看看cat
shell的環境,再看看shell的環境。它似乎有效(兩種環境很可能是相同的,所以很難判斷一切是否按預期工作,但我的觀點是:兩者都不environ
是空的)。使用
bash
,ksh
或者zsh
我期望看到cat
兩次的環境,但它只列印了一次。分成兩種不同的情況:
bash -c '/bin/cat - </proc/self/environ'
什麼也不列印,好像environ
是空的;bash -c '/bin/cat /proc/self/environ'
按預期列印一些東西。到底是怎麼回事?這不是
comm
or的情況stat
。為什麼不environ
一樣?$ uname -a Linux barbaz 4.9.0-6-amd64 #1 SMP Debian 4.9.88-1 (2018-04-29) x86_64 GNU/Linux
shell 之間的差異是由於程序設置的差異造成的。
dash
在分叉之前設置重定向,所以/proc/self
指向外殼;bash
並zsh
在分叉後設置它們,因此/proc/self
指向新程序。您可以通過以下方式看到這種情況strace -f
:
strace -f dash -c '/bin/cat /proc/self/comm - </proc/self/comm'
表演(除其他外)open("/proc/self/comm", O_RDONLY) = 3 fcntl(0, F_DUPFD, 10) = 10 close(0) = 0 fcntl(10, F_SETFD, FD_CLOEXEC) = 0 dup2(3, 0) = 0 close(3) = 0 clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f12581299d0) = 7743 strace: Process 7743 attached [pid 7742] wait4(-1, <unfinished ...> [pid 7743] execve("/bin/cat", ["/bin/cat", "/proc/self/comm", "-"], [/* 43 vars */]) = 0
(
/proc/self/comm
在系統呼叫之前打開clone
,這是程序分叉的地方);
strace -f bash -c '/bin/cat /proc/self/comm - </proc/self/comm'
節目clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fb506bdee10) = 8106 strace: Process 8106 attached [... snip a ton of signal-handling setup ...] [pid 8106] open("/proc/self/comm", O_RDONLY) = 3 [pid 8106] dup2(3, 0) = 0 [pid 8106] close(3) = 0 [pid 8106] execve("/bin/cat", ["/bin/cat", "/proc/self/comm", "-"], [/* 43 vars */]) = 0
(
/proc/self/comm
在呼叫後打開clone
,在子程序中,8106)。了解為什麼
environ
顯示為空需要更多解釋。當/proc/<pid>/environ
打開時,核心保存一個指向任務的指針的副本mm_struct
,其中包含指向環境的指針。但是execve
,用於啟動cat
程序的 ,會為程序創建一個新mm_struct
的。因此,重定向最終指向過時的資訊,當cat
讀取其輸入時,它看不到它的真實環境。它確實看到的環境應該是其父環境的副本,但所涉及的 shell 在分叉和設置新環境(由 設置execve
)之前對其進行清理。