要从文件描述符 6 读取,我可以使用<&6
or </dev/fd/6
(又名/proc/self/fd/6
)。通常两者都同样有效。然而,如果该文件描述符恰好是一个套接字,就会发生奇怪的事情。例如:
$ bash -c 'ls -l /dev/fd/6;cat /dev/fd/6' 6</dev/tcp/localhost/12345
lrwx------ 1 michas michas 64 Jan 10 19:50 /dev/fd/6 -> socket:[315010]
cat: /dev/fd/6: No such device or address
这里ls
显示描述符确实存在。但通过这种方式访问数据是不可能的。如果我使用cat <&6
它,一切都会恢复正常。
两种访问文件描述符的方式有什么区别?
如果在变量中给出数字,是否有访问描述符的好方法? (</dev/fd/$fd
可以,但<&$fd
不行。)
(上述情况可以在 Linux 上观察到,但在 OpenBSD 上则不然。- 看起来文件描述符是那里的常规字符设备。)
答案1
之所以如此,是因为/dev/fd/
Linux 上没有实现从代表套接字的条目中读取数据。你可以在这里找到一篇关于推理的很好的文章。因此您可以调用stat
该链接,这就是为什么您会看到它带有ls
,但故意禁止访问。
现在来说第二部分——为什么有效bash -c 'ls -l /dev/fd/6; cat <&6' 6</dev/tcp/localhost/12345
?这是因为套接字是使用套接字/文件 API 读取的,而不是/proc
文件系统。这是我观察到的情况:
bash
在终端中运行的实例使用 fd 6 创建套接字。- 子进程
bash
运行并调用dup2(6, 0)
,以便将您的套接字连接为cat
'sstdin
。 - 如果
dup2
调用没有失败, cat 会读取stdin
.
您可以通过以下方式重现并观察它:
netcat -lp 12345 # in another terminal session (GNU netcat)
strace -f -e trace=open,read,write,dup2 bash -c 'ls -l /dev/fd/6; cat <&6' \
6</dev/tcp/localhost/12345
如果您想知道为什么bash
子进程可以访问 fd 6 - 文件描述符仍然存在fork
,并且如果它们没有被标记为关闭exec
,那么它们也不会在那里关闭。
答案2
回答你的直接问题,“有什么不同?”:
当您从 重定向时<&6
,shell 使用dup2()
系统调用来复制文件描述符。当您(尝试)从 重定向时</dev/fd/6
,它将使用open()
.
内核不支持;open()
中的套接字/dev/fd
它们出现在目录中仅用于装饰信息。