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

mic*_*has 11 linux bash file-descriptors socket

要从文件描述符 6 中读取,我可以使用<&6</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
Run Code Online (Sandbox Code Playgroud)

这里ls显示描述符确实存在。但是无法通过这种方式访问​​数据。如果我cat <&6改用一切正常。

两种访问文件描述符的方式有什么区别?

如果在变量中给出数字,是否有一种访问描述符的好方法?(</dev/fd/$fd会起作用,但<&$fd不会。)

(上述情况可以在 linux 上观察到,但在 OpenBSD 上不能观察到。- 似乎该文件描述符是那里的常规字符设备。)

TNW*_*TNW 6

之所以如此,是因为/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. Childbash运行并调用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
Run Code Online (Sandbox Code Playgroud)

如果您想知道为什么bash子进程可以访问 fd 6 - 文件描述符存活fork,并且如果它们没有被标记为关闭exec,它们也不会在那里被关闭。