创建两个文件描述符对应同一个文件是否有明确定义?

11

POSIX规定了一个fdopen函数,用于为文件描述符创建FILE。 POSIX还规定了一个fileno函数,用于返回FILE的文件描述符。这两个函数可以一起使用,以创建访问现有文件相同底层文件描述符的第二个FILE

FILE *secondfile(FILE *f, const char *mode)
{
    int fd = fileno(f);

    return fd >= 0 ? fdopen(fd, mode) : NULL;
}

在POSIX下,这是一个明确定义的操作吗?当我访问原始FILE和我为同一文件描述符创建的第二个FILE 时会发生什么?交互是否已指定?如果有,怎么办?

历史上,Unix使用了一个固定的FILE结构表用于可打开的20个文件。在文件描述符已经与FILE相关联的情况下调用fdopen()将破坏现有文件并产生未定义的行为。我不确定此类stdio实现是否仍然被POSIX允许,这就是我提出这个问题的原因。


1
我无法想象这会起作用。 - Thomas Padron-McCarthy
我会怀疑它不是定义良好的,因为IO操作的结果将取决于FILE结构中的数据与底层文件描述符的(共享)偏移量的交互。 - Andrew Henle
@ThomasPadron-McCarthy 我也不知道,但我在 POSIX 中没有找到关于这种情况的语言,而其他语言似乎表明它是明确定义的,这就是我为什么要问的原因。 - fuz
虽然文档将fileno的结果称为“文件描述符”,但它更像是一个句柄。但是,由于FILE结构体可能包括libc的缓冲等内容,如果您使用两个不同的FILE来处理相同的句柄,则可能会出现问题。该句柄可能更适用于通过直接系统调用进行低级别访问,绕过stdlib函数。无论如何,不清楚您实际上为什么要这样做。如果您需要对文件进行多个访问位置,则最好将它们映射到内存中或在stdlib之上编写自己的层。 - too honest for this site
它由POSIX定义(§2.5.1 文件描述符和标准I/O流的交互),但是将它们一起使用需要满足特定的要求。 - Michael Foukarakis
显示剩余2条评论
2个回答

2
POSIX明确允许多个“句柄”同时与同一底层的“打开文件描述符”相关联,其中句柄可以是文件描述符或流。虽然它没有明确解决通过fdopen()在同一文件描述符上打开的多个流的问题,但我认为这些流通常不会受到更多或不同的要求,就像任何两个与同一打开文件描述符相关联的流一样。
POSIX定义了如何使用同一打开文件描述符上的两个句柄以避免未定义行为的限制。这里需要注意的是,对于文件描述符而言,这些限制非常少;几乎所有限制都适用于流,并且主要围绕与缓冲相关的条件组织。例外与定位有关。
如果您按照这些限制的方式使用流,主要是确保在切换到使用另一个流时不会将输出缓冲未写入,那么您可以期望流 I/O 函数的行为与文档描述的一致。否则,行为将被明确定义为未定义。

1

给定一个典型的Unix实现,其中包含一个文件描述符以从中读取的FILE数据结构和一个缓冲区用于缓冲,如果您知道缓冲区的大小和填充它的策略(需要数据时而不是在缓冲区为空时立即填充),我会说您可以确定会发生什么。但我不知道POSIX标准说了什么,我确信这将是程序中难以使用的事情。(“好吧,所以我已经从磁盘文件中读取了前4096个字节到这个 FILE中,接下来的4096个字节读取到那个 FILE中,并且第三个4096字节块将被读取到首先到达其缓冲区末尾并需要读取更多内容的FILE中...”)

(我从未刻意做过这样的事情,但我似乎记得从调试混合文件和文件描述符的代码中出现这样的症状。)

我的猜测是,POSIX没有很好地指定这一点,不能保证其工作。例如,POSIX是否指定FILE中的缓冲区何时会通过从文件描述符读取来填充?当为空时,当为空且需要更多数据时,还是其中任何一个,取决于什么?根据选择,来自文件描述符的数据将显示在不同的FILE中。

谢谢您的回答,但我更希望得到基于标准而非典型UNIX系统的答案。有一些非典型的UNIX系统(例如Windows),看似显而易见的假设在这些系统上可能并不成立。 - fuz
@FUZxxl:是的,需要更了解POSIX标准的人给出更权威的答案。 - Thomas Padron-McCarthy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接