Ser*_*nyy 9 command-line bash ksh
在浏览/proc/self
我的mksh
shell 的文件夹时,我发现了一个奇怪的事情:在/proc/self/fd/*
所有的标准文件描述符中(0 表示标准输入,1 表示标准输出,2 标准错误)一些文件描述符,还有一些额外的 - 24、25、3 . 从技术上讲,我可以在 shell 中使用 glob 列出它们:
$ for fd in /proc/self/fd/* ; do echo $fd ; done
/proc/self/fd/0
/proc/self/fd/1
/proc/self/fd/2
/proc/self/fd/24
/proc/self/fd/25
/proc/self/fd/3
Run Code Online (Sandbox Code Playgroud)
但是当我尝试stat
使用它们或使用find
它们时,它们被报告为不存在。
$ find /proc/self/fd/*
/proc/self/fd/0
/proc/self/fd/1
/proc/self/fd/2
find: ‘/proc/self/fd/24’: No such file or directory
find: ‘/proc/self/fd/25’: No such file or directory
/proc/self/fd/3
Run Code Online (Sandbox Code Playgroud)
同样发生在bash
,但只有一个辅助文件描述符。
$ for fd in /proc/self/fd/* ; do echo $fd; done
/proc/self/fd/0
/proc/self/fd/1
/proc/self/fd/2
/proc/self/fd/255
/proc/self/fd/3
$ find /proc/self/fd/*
/proc/self/fd/0
/proc/self/fd/1
/proc/self/fd/2
find: ‘/proc/self/fd/255’: No such file or directory
/proc/self/fd/3
Run Code Online (Sandbox Code Playgroud)
问题是:那些额外的文件描述符是什么?他们的目的是什么?
mur*_*uru 18
探测/proc/self
是一项棘手的工作,因为它随每个进程而变化。当您这样做时/proc/self/fd/*
,shell 会扩展通配符,因此它会列出自己的文件描述符。但是当这些被传递给另一个命令时,比如find
or ls
,路径现在将用于该进程' /proc/self
,并且它可能有也可能没有这些数字的 fds 。
更棘手的是,shell 可能会在通配符扩展期间打开文件描述符。
与/proc/$$/fd
可能有启发性的比较:
bash
:
$ ls -l /proc/self/fd /proc/$$/fd/* &
[1] 5172
$ lrwx------ 1 muru muru 64 Jan 1 20:16 /proc/4932/fd/0 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 1 20:16 /proc/4932/fd/1 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 1 20:16 /proc/4932/fd/2 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 1 20:16 /proc/4932/fd/255 -> /dev/pts/1
/proc/self/fd:
total 0
lrwx------ 1 muru muru 64 Jan 1 20:24 0 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 1 20:24 1 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 1 20:24 2 -> /dev/pts/1
lr-x------ 1 muru muru 64 Jan 1 20:24 3 -> /proc/5172/fd
[1]+ Done ls --color=auto -l /proc/self/fd /proc/$$/fd/*
Run Code Online (Sandbox Code Playgroud)
通过将其发送到后台,我让 bash 打印了 PID,您可以看到它/proc/self/fd/3
指向ls
' own /proc/<PID>/fd
,它已打开进行扫描。带有4932
, OTOH的条目用于 bash 的 fds,特殊的条目是 255。在此 SO 帖子中找到了解释:
打开的文件是 0 (stdin)、1 (stdout) 和 2 (stderr)。255 是 bash 用来在重定向时保留这些副本的一个小技巧。这是 bash 特有的。
资料来源:https : //books.google.com/books?id=wWjqCF9HLfYC&pg=PA231
与mksh
:
$ ls -l /proc/self/fd /proc/$$/fd/* &
[1] 5075
$ lrwx------ 1 muru muru 64 Jan 1 20:22 /proc/5074/fd/0 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 1 20:22 /proc/5074/fd/1 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 1 20:22 /proc/5074/fd/10 -> /dev/tty
lrwx------ 1 muru muru 64 Jan 1 20:22 /proc/5074/fd/2 -> /dev/pts/1
/proc/self/fd:
total 0
lrwx------ 1 muru muru 64 Jan 1 20:22 0 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 1 20:22 1 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 1 20:22 2 -> /dev/pts/1
lr-x------ 1 muru muru 64 Jan 1 20:22 3 -> /proc/5075/fd
[1] + Done ls -l /proc/self/fd /proc/$$/fd/*
Run Code Online (Sandbox Code Playgroud)
实际上是一样的,除了额外的 fd 是 10,我敢打赌它与 bash 的原因相同,因为源代码表明 shell 使用 fd 10 及以后的版本。
我没有得到两三个额外的 fd,但这可能是由于通配符扩展期间发生的任何事情,或者由于后台工作或其他一些不为人知的原因。
如果我运行你的for
循环,我会得到一个临时的 fd 3:
$ for fd in /proc/$$/fd/* ; do ls -l $fd ; done
lrwx------ 1 muru muru 64 Jan 2 17:39 /proc/6012/fd/0 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 17:39 /proc/6012/fd/1 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 17:39 /proc/6012/fd/2 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 17:39 /proc/6012/fd/255 -> /dev/pts/1
ls: cannot access '/proc/6012/fd/3': No such file or directory
Run Code Online (Sandbox Code Playgroud)
在这里,strace
用于跟踪执行:
strace -e open -o log bash -c 'for fd in /proc/$$/fd/* ; do : ; done'
Run Code Online (Sandbox Code Playgroud)
我们会看到第三个 fd 实际上是/proc/<PID>/fd
:
$ tail log
open("/usr/lib/libreadline.so.7", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libncursesw.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/dev/tty", O_RDWR|O_NONBLOCK) = 3
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/gconv/gconv-modules.cache", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/lib/gconv/gconv-modules", O_RDONLY|O_CLOEXEC) = 3
open("/proc/9975/fd/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
+++ exited with 0 +++
Run Code Online (Sandbox Code Playgroud)
现在的问题是,为什么这个 fd 没有出现在早期的ls
测试中?看起来背景与此有关:
$ ls -l /proc/$$/fd/* &
[1] 10091
$ lrwx------ 1 muru muru 64 Jan 2 17:46 /proc/10076/fd/0 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 17:46 /proc/10076/fd/1 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 17:46 /proc/10076/fd/2 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 17:46 /proc/10076/fd/255 -> /dev/pts/1
[1]+ Done ls --color=auto -l /proc/self/fd /proc/$$/fd/*
$ ls -l /proc/$$/fd/*
ls: cannot access '/proc/10076/fd/3': No such file or directory
lrwx------ 1 muru muru 64 Jan 2 17:46 /proc/10076/fd/0 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 17:46 /proc/10076/fd/1 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 17:46 /proc/10076/fd/2 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 17:46 /proc/10076/fd/255 -> /dev/pts/1
Run Code Online (Sandbox Code Playgroud)
前景ls
显示丢失的 fd。
现在,再次跟踪strace
:
strace -fe open,execve,fork -o log bash -ic 'ls -l /proc/self/fd /proc/$$/fd/* &'
Run Code Online (Sandbox Code Playgroud)
我们看:
10731 execve("/usr/bin/bash", ["bash", "-ic", "ls -l /proc/$$/fd/* &"], [/* 67 vars */]) = 0
10731 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
# snip
10734 open("/proc/10731/fd/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
10734 execve("/usr/bin/ls", ["ls", "--color=auto", "-l", "/proc/10731/fd/0", "/proc/10731/fd/1", "/proc/10731/fd/2", "/proc/10731/fd/255"], [/* 68 vars */]) = 0
Run Code Online (Sandbox Code Playgroud)
请注意 PID 的变化。通配符扩展似乎发生在分叉之后,但变量扩展发生在此之前。因此,fd 3 存在,但在不同的过程中。现在你用 iself
而不是$$
,你会看到 3 和 255:
$ strace -fe open,execve -o log bash -ic 'ls -l /proc/self/fd/* &'
[1] 10790
ls: cannot access '/proc/self/fd/255': No such file or directory
ls: cannot access '/proc/self/fd/3': No such file or directory
lrwx------ 1 muru muru 64 Jan 2 18:04 /proc/self/fd/0 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 18:04 /proc/self/fd/1 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 18:04 /proc/self/fd/2 -> /dev/pts/1
Run Code Online (Sandbox Code Playgroud)
Unix&Linux Stackexchange 站点上的相关答案引用了邮件列表中的回复:
Fd 255 在内部用作与 tty 的连接,因此它不会干扰使用 exec 重新定位 fds。出于同样的原因,Bash 在处理进程替换 `<(foo)' 时也会分配高 fds。
安德烈亚斯·施瓦布