Linux proc/pid/fd 的 stdout 是 11 吗?

发布于 2024-11-10 16:53:38 字数 527 浏览 8 评论 0原文

使用重定向到文件的 stdout 执行脚本。所以 /proc/$$/fd/1 应该指向该文件(因为 stdout fileno 是 1)。然而,该文件的实际 fd 是 11。请解释为什么。

这是会话:

$ cat hello.sh
#!/bin/sh -e
ls -l /proc/$$/fd >&2

$ ./hello.sh > /tmp/1
total 0
lrwx------ 1 nga users 64 May 28 22:05 0 -> /dev/pts/0
lrwx------ 1 nga users 64 May 28 22:05 1 -> /dev/pts/0
lr-x------ 1 nga users 64 May 28 22:05 10 -> /home/me/hello.sh
l-wx------ 1 nga users 64 May 28 22:05 11 -> /tmp/1
lrwx------ 1 nga users 64 May 28 22:05 2 -> /dev/pts/0

Executing a script with stdout redirected to a file. So /proc/$$/fd/1 should point to that file (since stdout fileno is 1). However, actual fd of the file is 11. Please, explain, why.

Here is session:

$ cat hello.sh
#!/bin/sh -e
ls -l /proc/$/fd >&2

$ ./hello.sh > /tmp/1
total 0
lrwx------ 1 nga users 64 May 28 22:05 0 -> /dev/pts/0
lrwx------ 1 nga users 64 May 28 22:05 1 -> /dev/pts/0
lr-x------ 1 nga users 64 May 28 22:05 10 -> /home/me/hello.sh
l-wx------ 1 nga users 64 May 28 22:05 11 -> /tmp/1
lrwx------ 1 nga users 64 May 28 22:05 2 -> /dev/pts/0

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

絕版丫頭 2024-11-17 16:53:38

我有一个怀疑,但这很大程度上取决于您的 shell 的行为方式。您看到的文件描述符是:

  • 0:标准输入
  • 1:标准输出
  • 2:标准错误
  • 10:正在运行的脚本
  • 符的备份副本

11:脚本的正常标准输出描述 10 和 11 在 exec 上接近,因此不会出现在 ls 进程中。然而,0-2 是在分叉之前为 ls 做好准备的。我在 dash (Debian Almquist shell) 中看到了这种行为,但在 bash (Bourne Again shell) 中没有看到这种行为。 Bash 在分叉后进行文件描述符操作,并且顺便在脚本中使用 255 而不是 10。在分叉后进行更改意味着它不必恢复父级中的描述符,因此它没有用于 dup2 的备用副本。

I have a suspicion, but this is highly dependent on how your shell behaves. The file descriptors you see are:

  • 0: standard input
  • 1: standard output
  • 2: standard error
  • 10: the running script
  • 11: a backup copy of the script's normal standard out

Descriptors 10 and 11 are close on exec, so won't be present in the ls process. 0-2 are, however, prepared for ls before forking. I see this behaviour in dash (Debian Almquist shell), but not in bash (Bourne again shell). Bash instead does the file descriptor manipulations after forking, and incidentally uses 255 rather than 10 for the script. Doing the change after forking means it won't have to restore the descriptors in the parent, so it doesn't have the spare copy to dup2 from.

囍孤女 2024-11-17 16:53:38

strace 的输出在这里会很有帮助。

相关部分是

fcntl64(1, F_DUPFD, 10)                 = 11
close(1)                                = 0
fcntl64(11, F_SETFD, FD_CLOEXEC)        = 0
dup2(2, 1)                              = 1
stat64("/home/random/bin/ls", 0xbf94d5e0) = -1 ENOENT (No such file or
+++++++>directory)
stat64("/usr/local/bin/ls", 0xbf94d5e0) = -1 ENOENT (No such file or directory)
stat64("/usr/bin/ls", 0xbf94d5e0)       = -1 ENOENT (No such file or directory)
stat64("/bin/ls", {st_mode=S_IFREG|0755, st_size=96400, ...}) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD,
+++++++>child_tidptr=0xb75a8938) = 22748
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 22748
--- SIGCHLD (Child exited) @ 0 (0) ---
dup2(11, 1)                             = 1

因此,shell 将现有的 stdout 移动到 10 以上的可用文件描述符(即 11),然后将现有的 stderr 移动到它自己的 stdout 上(由于 >&2重定向),然后当 ls 命令完成时将 11 恢复到它自己的标准输出。

The output of strace can be helpful here.

The relevant section is

fcntl64(1, F_DUPFD, 10)                 = 11
close(1)                                = 0
fcntl64(11, F_SETFD, FD_CLOEXEC)        = 0
dup2(2, 1)                              = 1
stat64("/home/random/bin/ls", 0xbf94d5e0) = -1 ENOENT (No such file or
+++++++>directory)
stat64("/usr/local/bin/ls", 0xbf94d5e0) = -1 ENOENT (No such file or directory)
stat64("/usr/bin/ls", 0xbf94d5e0)       = -1 ENOENT (No such file or directory)
stat64("/bin/ls", {st_mode=S_IFREG|0755, st_size=96400, ...}) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD,
+++++++>child_tidptr=0xb75a8938) = 22748
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 22748
--- SIGCHLD (Child exited) @ 0 (0) ---
dup2(11, 1)                             = 1

So, the shell moves the existing stdout to an available file descriptor above 10 (namely, 11), then moves the existing stderr onto its own stdout (due to the >&2 redirect), then restores 11 to its own stdout when the ls command is finished.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文