stdin 和 stdout 实际上是同一个文件吗?

Are stdin and stdout actually the same file?

我完全糊涂了,有没有可能stdin、stdout和stderr在内部指向同一个文件描述符? 因为如果我使用 stdin 作为输入或 stdout,如果我想从控制台读取字符串,这在 C 中没有区别。

read(1, buf, 200) 作为 read(0, buf, 200) 这怎么可能?

(0 == STDIN_FILENO == fileno(stdin),
1 == STDOUT_FILENO == fileno(stdout))

当输入来自控制台,而输出转到控制台时,这三者确实碰巧引用了同一个文件。 (但控制台设备的读写实现方式完全不同。)

无论如何,您应该仅将 stdin/stdout/stderr 用于其预期目的;否则,像下面这样的重定向将不起作用:

<inputfile myprogram >outputfile

(这里,stdinstdout指的是两个不同的文件,stderr指的是控制台。)

is it possible that stdin, stdout, and stderr point to the same filedescriptor internally?

A file descriptor is an index into the file descriptor table of your process (see also credentials(7)...). By definition STDIN_FILENO is 0, STDOUT_FILENO is 1, annd STDERR_FILENO is 2. Read about proc(5) 查询有关某些进程的信息(例如,在您的交互式 shell 中尝试 ls -l /proc/$$/fd)。

程序(通常,但不总是,一些 shell) which has execve(2)-d your executable might have called dup2(2) 共享(即复制)一些文件描述符。

另见 fork(2), intro(2) and read some Linux programming book, such as the old ALP

注意 read(2) from STDOUT_FILENO could fail (e.g. with errno(3) being EBADF) in the (common) case where stdout is not readable (e.g. after redirection by the shell). If reading from the console, it could be readable. Read also the Tty Demystified.

没有禁止在内核中引用同一事物的任意数量的文件句柄。

终端程序的默认设置是 STDINSTDOUTSTDERR 指的是同一个终端。

因此,看起来您使用哪个并不重要,但如果调用者执行任何句柄重定向,一切都会出错,这很常见。
最常见的是将一个程序的输出通过管道传输到下一个程序的输入,但将标准输出排除在外。

shell的示例:

source | filter | sink

有些人似乎忽略了一件事情:read 是低级系统调用。它的第一个参数是一个 Unix 文件描述符,而不是像 stdinstdoutstderr 这样的 FILE*。您应该收到有关此的编译器警告:

    warning: passing argument 1 of ‘read’ makes integer from pointer without a cast [-Wint-conversion]
   int r = read(stdout, buf, 200);
                ^~~~~~

在我的系统上,它不适用于 stdinstdoutread总是returns-1,而errno设置为EBADF,即"Bad file descriptor"。在我看来,那些确切的行在您的系统上似乎不太可能工作:指针必须指向内存地址 0、1 或 2,这在典型的机器上不会发生。

要使用read,您需要将其传递STDIN_FILENOSTDOUT_FILENOSTDERR_FILENO

要使用 FILE*,例如 stdinstdoutstderr,您需要使用 fread

loginxterm 等程序通常会在创建 once 时打开 tty 设备 新终端会话,复制 文件描述符两到三次,安排文件描述符 0、1 和 2 链接到 打开文件描述 打开的 tty 设备。它们通常在执行 shell 之前关闭所有其他文件描述符。因此,如果 shell 或其子进程没有进行进一步的重定向,文件描述符 0、1 和 2 将保持链接到同一个文件。因为底层 tty 设备是以读写模式打开的,所有三个文件描述符都具有读写访问权限。