将 stdout fd 传递给“读取”系统调用,但它仍然工作正常

将 stdout fd 传递给“读取”系统调用,但它仍然工作正常

我将1(stdout)/ 2(stderr) 传递给read系统调用,但它仍然工作正常。然后我将0(stdin)传递给write系统调用,发现它也有效!

int main(int argc, char** argv){
    char buf[1024] = "abcdefghi\n";

    write(0, buf, 10);

    char readbuf[1024] = {0};
    // read(1, readbuf, 10); works too
    read(2, readbuf, 10);
    write(2, readbuf, 10);

    return 0;
}

输出:

abcdefghi
hey stdin  <-- I input this
hey stdin

很困惑,我认为这应该是一个错误。

实验:

然后我尝试重定向 fd 2。

$ ./a.out 2>/dev/null

这次读取和第二次写入都不“可见”。输出是

abcdefgi

那么stderr可以用于读取吗?

然后我关闭 stdout 和 stderr 并制作两个 stdin 副本:

int main(int argc, char** argv){
    char buf[1024] = "abcdefghi\n";

    close(1);
    close(2);

    dup2(0, 1);
    dup2(0, 2);

    write(0, buf, 10);

    char redbuf[1024] = {0};
    read(2, redbuf, 10);
    write(2, redbuf, 10);

    return 0;
}

它又起作用了。

输出:

abcdefghi
hey stdin  <-- I input this
hey stdin

那么stdin可以用于写入吗?

我在这里需要一些解释。

问题

我想知道:

为什么stdout/stderr可以用来读取?

为什么stdin可以用来写?

是三流(标准输入、标准输出、标准错误)内部溪流 ?

如果不是,为什么我会得到这个结果?

答案1

唯一的约定是使用 fd 0/1/2 作为输入/输出/错误。如果您在没有重定向的情况下调用程序,则所有三个都引用您的 tty,并且您的 tty 已打开并具有读写访问权限。这意味着您可以根据需要读取或写入它们。您可以将它们称为相同的流,尽管表达式流通常用于更高级别的 I/O,例如FILE在 C 或streamC++ 中。

这就是为什么两个或不重定向的示例都只是回显您输入的文本的原因。

另一方面,如果进行重定向,shell 将以只读或只写访问权限打开文件。在您的示例中./a.out 2>/dev/null,写入0仍然连接到终端,因为它没有重定向,因此显示在屏幕上。读取2仅连接到写入/dev/null,因此应该失败,但您不会注意到与程序的差异。写入2成功,但写入到/dev/null。无效读取和有效写入/dev/null在您的终端上不可见。

答案2

只要您没有重定向 stdin/stdout/stderr,这些文件描述符就会被登录过程打开,并打开相关的tty读写文件作为第一个文件(导致文件描述符 #0),然后调用dup()2 次以获取stdout 和 stderr 的文件描述符。

正如中提到的“less”如何从标准输入获取数据,同时仍然能够读取用户的命令?在以前(之前/dev/tty已经介绍过UNIX),像 did 这样的程序在要求确认时more会读取。stderr

相关内容