&6 和 /dev/fd/6 有什么区别?

&6 和 /dev/fd/6 有什么区别?

要从文件描述符 6 读取,我可以使用<&6or </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文件系统。这是我观察到的情况:

  1. bash在终端中运行的实例使用 fd 6 创建套接字。
  2. 子进程bash运行并调用dup2(6, 0),以便将您的套接字连接为cat's stdin
  3. 如果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它们出现在目录中仅用于装饰信息。

相关内容