【问题标题】:What does POSIX mean when it says stderr is expected to be open for reading and writing?当 POSIX 说 stderr 应该对读写开放时,它是什么意思?
【发布时间】:2020-09-15 01:44:04
【问题描述】:

POSIX 在stderr, stdin, stdout - standard I/O streams 上的页面是这样说的:

stderr 流应该是开放读写的。

“预期”有多强?是否违反了未定义的行为?又是谁的责任,系统的还是应用的?

考虑这个程序:

#include <stdio.h>

int main(void) {
    printf("feof is %d and ferror is %d\n", feof(stderr), ferror(stderr));
    printf("fgetc is %d\n", fgetc(stderr));
    printf("feof is %d and ferror is %d\n", feof(stderr), ferror(stderr));
}

当我在不重定向 stderr 的情况下运行它(所以它像 stdin 一样指向我的终端),它会立即输出它而不等待任何输入:

feof is 0 and ferror is 0
fgetc is -1
feof is 0 and ferror is 1

这是否意味着我的系统不符合 POSIX?

另外,如果这是我的责任,那么假设我有一个权限为620 的文件,并且我在组中但不是所有者。这是否意味着 someprogram 2&gt;saidfile 是未定义的行为,因为无论如何您都无法从 stderr 读取?

【问题讨论】:

  • 传统上,login 命令(或在login 之前运行的getty 程序)将打开tty 设备进行读写作为文件描述符0(标准输入),并使用@987654329 @ 创建文件描述符 1(标准输出)和 2(标准错误)。因此,login 启动的 shell 可能具有所有三个可读写的标准 I/O 描述符(但 I/O 流可能不那么灵活)。我不知道 POSIX 要求;它在第 6 期(POSIX 2004)中正式添加。系统(shell)将负责提供可读的stderr
  • @JonathanLeffler 但是即使 FD 2 可以读写,尝试从stderr 流中读取也会失败(如果我想让它工作,我必须直接使用 FD)。这似乎正是 POSIX 所说的不应该发生的事情。
  • 还有一点很奇怪的是“此参考页面上描述的功能与 ISO C 标准一致”的注释,但 ISO C 不包含这样的要求。
  • 我正在扩展我认为需求的来源;我无法解释它,我对stderr(不同于“标准错误”)可读可写的规范感到困惑。当然,对于文件流,您应该在读取和写入之间或在写入和读取之间使用定位操作(例如fseek(stderr, 0L, SEEK_CUR))。我注意到 POSIX 没有说明 stdinstdout 是否需要既可读又可写。要求在 POSIX 扩展部分:[CX] ⌦ ... ⌫ — 它是对标准 C 的纯扩展。
  • 如果这里有不一致的地方,这似乎是 C 库的错,即使 fd 2 已打开以供读取,也拒绝接受 stderr 上的读取。我的系统(Ubuntu 19.1 上的 glibc)也是这样做的。但无论如何,这不是外壳的错。

标签: c file language-lawyer posix stderr


【解决方案1】:

Austin Group(维护 POSIX 标准的联合工作组)在 2020-12-07 和 2020-12-10 电话会议期间讨论了 defect that was reported about the "expected to be" wording,并同意 POSIX 第 7 期(2018 版)中的措辞是有问题的。下一个版本的 POSIX 标准中的措辞将更改如下(复制自bug comment):

在第 65 页第 1912 行之前(XBD“交互式外壳”)插入一个新定义并对剩余部分重新编号:

互动装置

一个终端设备。

注意:此定义旨在与 ISO C 标准对“交互式设备”的使用保持一致。

第 496 页第 17224-17228 行(XSH 2.5 标准 I/O 流)和第 2017 页第 64723-64727 行(XSH 标准错误、标准输入、标准输出说明)更改:

在程序启动时,应预定义三个流,无需显式打开:标准输入(用于读取常规输入),标准输出(用于编写常规输入)输出)和标准错误(用于写入诊断输出)。打开时,标准错误流没有完全缓冲;当且仅当可以确定流不引用交互式设备时,标准输入和标准输出流才会被完全缓冲。

到:

在程序启动时,应预定义三个流并且已经打开:stdin(标准输入,用于常规输入)用于读取,stdout(标准输出,用于常规输出)用于写入,stderr(标准错误,用于诊断输出)用于写入。打开时,stderr不应被完全缓冲; stdinstdout 当且仅当 [CX] 与流关联的文件描述符被确定为不与交互式设备关联时才应被完全缓冲。[/CX]

在 2017 页第 64733 行(XSH 标准输入说明)上,更改:

stderr 流应该是开放读写的。

到:

这些文件描述符通常都与具有访问模式 O_RDWR 的单个打开文件描述相关联(例如,在用于登录 shell 的终端设备的情况下)。但是,在这种情况下,在程序启动时不需要打开 stderrstdinstdout 流进行读取和写入。

在 2017 页第 64747 行(stdin SEE ALSO)添加 isatty()。

特别感谢 Rich Felker 报告该缺陷。

【讨论】:

    【解决方案2】:

    POSIX 在specification for execve 中对此进行了详细说明:

    如果文件描述符 0、1 或 2 在成功调用 exec 系列函数之一后将被关闭,则实现可能会为新进程映像中的文件描述符打开一个未指定的文件。如果执行标准实用程序或符合标准的应用程序时文件描述符 0 未打开以供读取或文件描述符 1 或 2 未打开以供写入,则执行实用程序或应用程序的环境应被视为不符合标准,因此实用程序或应用程序的行为可能与本标准中所述不同。

    对于您自己的应用程序,如果他们尝试在其中任何一个关闭的情况下执行,他们应该为实现打开新的 stdin/out/err 的可能性做好准备,并且可以设置自己的规则(包括将其视为合同)违规导致灾难性错误行为)如果标准文件描述符在启动时未打开,他们将如何处理。

    对于标准实用程序,上面的文字涵盖了它。

    document you cited 只是指定它们与那些文件描述符相关联。我同意这还不够清楚,但合理的解释是,如果相应的文件描述符在应用程序入口处未打开或未针对适当的模式打开,则结果是在相关功能下为该条件(通常是 EBADF)指定的结果.例如,fgetc 指定:

    [EBADF]

    [CX] [Option Start] 底层流的文件描述符不是打开以供读取的有效文件描述符。 [选项结束]

    关于“预期”的文字:

    stderr 流应该是开放读写的。

    我认为标准中的任何地方都没有定义“预期”。但是,这里使用了 stream 这个词,而不是文件/文件描述符,所以我认为stderr 流的FILE 模式(如fopen 模式)就是这样只要遵守有关在它们之间切换的规则,就不会在其上读取或写入函数产生未定义的行为。没有这个文本,例如在没有 POSIX 的情况下,fgetc(stderr) 可能会产生未定义的行为。

    【讨论】:

    • 这似乎是在谈论裸文件描述符,而不是流,并且根本没有提到从 stderr 读取。
    • 您引用的文档只是指定它们与那些文件描述符相关联。我同意这还不够清楚,但合理的解释是,如果相应的文件描述符未打开或未在应用程序入口时以适当的模式打开,则结果是为该条件指定的结果(通常为EBADF)。
    • 但它特别提到了标准错误的“阅读”。
    • EBADF 注释使情况变得更加奇怪,因为在我的测试中,文件描述符 2 已打开以供读取;你可以做read(2, buf, size),它成功了。然而,当您执行fgetc(stderr) 时,它会返回EOFerrno == EBADF。基本上,似乎 C 启动代码正在执行stderr = fdopen(2, "w"),而不考虑文件描述符 2 是否打开以供读取。
    • @NateEldredge:我已经向标准提交了解释/澄清请求:austingroupbugs.net/view.php?id=1347
    猜你喜欢
    • 2023-03-03
    • 1970-01-01
    • 2012-08-23
    • 1970-01-01
    • 2011-10-18
    • 2021-03-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多