Kam*_*ski 9 shell io-redirection environment-variables proc
我使用的是 Debian GNU/Linux 9。我知道它/proc很特别,我知道什么/proc/self是.
这个命令
sh -c '/bin/cat /proc/self/comm - </proc/self/comm'
Run Code Online (Sandbox Code Playgroud)
产量
cat
sh
Run Code Online (Sandbox Code Playgroud)
如果我使用dash而不是sh. 但是用bash,ksh或者zsh结果是
cat
cat
Run Code Online (Sandbox Code Playgroud)
以/proc/self/stat代替/proc/self/comm我可以证实这两个cat-s其实都是同一个过程。显然,引擎盖下的外壳不同,没关系。现在让我们采取
sh -c '/bin/cat /proc/self/environ - </proc/self/environ'
Run Code Online (Sandbox Code Playgroud)
看了上面的,sh还是dash期待看到cat第一个的环境,后面的shell的环境。它似乎有效(无论如何,两种环境很可能是相同的,因此很难判断一切是否按预期工作,但我的观点是:两者都不environ是空的)。
用bash,ksh或者zsh我希望看到cat两次的环境,但它只打印一次。分为两种不同的情况:
bash -c '/bin/cat - </proc/self/environ'什么都不打印,好像environ是空的;bash -c '/bin/cat /proc/self/environ' 打印出预期的东西。到底是怎么回事?comm或不是这种情况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
Run Code Online (Sandbox Code Playgroud)
外壳之间的差异是由于流程设置的差异造成的。dash在 fork 之前设置重定向,所以/proc/self指向 shell;bash并zsh在fork后设置它们,所以/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
Run Code Online (Sandbox Code Playgroud)
(/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
Run Code Online (Sandbox Code Playgroud)
(调用/proc/self/comm后打开clone,在子进程中,8106)。
理解为什么environ显示为空需要更多的解释。当/proc/<pid>/environ被打开时,内核会保存一份指向任务的 指针的副本mm_struct,其中包含指向环境的指针。但是execve,用于启动cat进程的 ,为进程创建了一个新mm_struct的。因此,重定向最终指向过时的信息,并且在cat读取其输入时,它看不到其真实环境。它确实看到的环境应该是其父级的副本,但所涉及的 shell 在分叉和设置新环境(由 设置execve)之前对其进行清理。