某些 shell 中 `/proc/self/environ` 的奇怪行为;到底是怎么回事?

某些 shell 中 `/proc/self/environ` 的奇怪行为;到底是怎么回事?

我使用的是 Debian GNU/Linux 9。我知道/proc很特别, 我知道什么/proc/self

这个命令

sh -c '/bin/cat /proc/self/comm - </proc/self/comm'

产量

cat
sh

dash如果我使用而不是,该模式将类似sh。但与bash,kshzsh结果是

cat
cat

代替/proc/self/stat/proc/self/comm可以确认这两个cat- 实际上是同一个进程。显然外壳在引擎盖下有所不同,没关系。现在让我们采取

sh -c '/bin/cat /proc/self/environ - </proc/self/environ'

观察上述情况后,shdash我期望看到的环境cat,稍后看到外壳的环境。它似乎有效(无论如何,两个环境很可能是相同的,所以很难判断是否一切按预期工作,但我的观点是:两者都不environ为空)。

bashksh或者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;bashzsh在分叉后设置它们,因此/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)之前清理它。

相关内容