我使用的是 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
- 实际上是同一个进程。显然外壳在引擎盖下有所不同,没关系。现在让我们采取
sh -c '/bin/cat /proc/self/environ - </proc/self/environ'
观察上述情况后,sh
或dash
我期望看到的环境cat
,稍后看到外壳的环境。它似乎有效(无论如何,两个环境很可能是相同的,所以很难判断是否一切按预期工作,但我的观点是:两者都不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
答案1
shell 之间的差异是由于进程设置的差异造成的。dash
在分叉之前设置重定向,因此/proc/self
指向 shell;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
读取其输入时,它看不到其真实环境。环境它做see 应该是其父环境的副本,但涉及的 shell 在分叉和设置新环境(由execve
)之前清理它。