C语言 O_NONBLOCK 是否被设置为文件描述符或底层文件的属性?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2889901/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 05:29:51  来源:igfitidea点击:

Is O_NONBLOCK being set a property of the file descriptor or underlying file?

cnonblockingfile-descriptorfcntlposix-api

提问by Daniel Trebbien

From what I have been reading on The Open Group website on fcntl, open, read, and write, I get the impression that whether O_NONBLOCKis set on a file descriptor, and hence whether non-blocking I/O is used with the descriptor, should be a property of that file descriptor rather than the underlying file. Being a property of the file descriptor means, for example, that if I duplicate a file descriptor or open another descriptor to the same file, then I can use blocking I/O with one and non-blocking I/O with the other.

从我在 Open Group 网站上阅读的fcntl, open, read, 和 上write的内容来看,我的印象是是否O_NONBLOCK在文件描述符上设置,因此是否将非阻塞 I/O 与描述符一起使用,应该是该文件描述符而不是底层文件。作为文件描述符的一个属性意味着,例如,如果我复制一个文件描述符或打开另一个描述符到同一个文件,那么我可以对一个使用阻塞 I/O,对另一个使用非阻塞 I/O。

Experimenting with a FIFO, however, it appears that it is not possible to have a blocking I/O descriptor and non-blocking I/O descriptor to the FIFO simultaneously (so whether O_NONBLOCKis set is a property of the underlying file [the FIFO]):

然而,尝试使用 FIFO,似乎不可能同时对 FIFO 具有阻塞 I/O 描述符和非阻塞 I/O 描述符(因此是否O_NONBLOCK设置是底层文件 [FIFO] 的属性) ):

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    int fds[2];
    if (pipe(fds) == -1) {
        fprintf(stderr, "`pipe` failed.\n");
        return EXIT_FAILURE;
    }

    int fd0_dup = dup(fds[0]);
    if (fd0_dup <= STDERR_FILENO) {
        fprintf(stderr, "Failed to duplicate the read end\n");
        return EXIT_FAILURE;
    }

    if (fds[0] == fd0_dup) {
        fprintf(stderr, "`fds[0]` should not equal `fd0_dup`.\n");
        return EXIT_FAILURE;
    }

    if ((fcntl(fds[0], F_GETFL) & O_NONBLOCK)) {
        fprintf(stderr, "`fds[0]` should not have `O_NONBLOCK` set.\n");
        return EXIT_FAILURE;
    }

    if (fcntl(fd0_dup, F_SETFL, fcntl(fd0_dup, F_GETFL) | O_NONBLOCK) == -1) {
        fprintf(stderr, "Failed to set `O_NONBLOCK` on `fd0_dup`\n");
        return EXIT_FAILURE;
    }

    if ((fcntl(fds[0], F_GETFL) & O_NONBLOCK)) {
        fprintf(stderr, "`fds[0]` should still have `O_NONBLOCK` unset.\n");
        return EXIT_FAILURE; // RETURNS HERE
    }

    char buf[1];
    if (read(fd0_dup, buf, 1) != -1) {
        fprintf(stderr, "Expected `read` on `fd0_dup` to fail immediately\n");
        return EXIT_FAILURE;
    }
    else if (errno != EAGAIN) {
        fprintf(stderr, "Expected `errno` to be `EAGAIN`\n");
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

This leaves me thinking: is it ever possible to have a non-blocking I/O descriptor and blocking I/O descriptor to the same file and if so, does it depend on the type of file (regular file, FIFO, block special file, character special file, socket, etc.)?

这让我思考:是否有可能对同一个文件有一个非阻塞 I/O 描述符和阻塞 I/O 描述符,如果是这样,它是否取决于文件的类型(常规文件、FIFO、块特殊文件、字符特殊文件、套接字等)?

回答by Jonathan Leffler

O_NONBLOCK is a property of the open file description, not of the file descriptor, nor of the underlying file.

O_NONBLOCK 是打开文件描述的属性,而不是文件描述符的属性,也不是底层文件的属性。

Yes, you could have separate file descriptors open for the same file, one of which is blocking and the other of which is non-blocking.

是的,您可以为同一个文件打开单独的文件描述符,其中一个是阻塞的,另一个是非阻塞的。

You need to distinguish between a FIFO (created using mkfifo()) and a pipe (created using pipe()).

您需要区分 FIFO(使用 创建mkfifo())和管道(使用创建pipe())。

Note that the blocking status is a property of the 'open file description', but in the simplest cases, there is a one-to-one mapping between file descriptors and open file descriptions. The open()function call creates a new open file description and a new file descriptor that refers to the open file description.

请注意,阻塞状态是“打开文件描述”的一个属性,但在最简单的情况下,文件描述符和打开文件描述之间存在一对一的映射。该open()函数调用创建一个新打开的文件的描述,并且指的是打开文件描述了新的文件描述符。

When you use dup(), you have two file descriptors sharing one open file description, and the properties belong to the open file description. The description of fcntl()says that F_SETFL affects the open file description associated with the file descriptor. Note that lseek()adjusts the file position of the open file description associated with the file descriptor - so it affects other file descriptors duplicated from the original one.

当您使用 时dup(),您有两个文件描述符共享一个打开的文件描述,并且属性属于打开的文件描述。的描述fcntl()说 F_SETFL 影响与文件描述符关联的打开文件描述。请注意,lseek()调整与文件描述符关联的打开文件描述的文件位置 - 因此它会影响从原始文件描述符复制的其他文件描述符。

Removing the error handling from your code to reduce it, you have:

从您的代码中删除错误处理以减少它,您有:

int fds[2];
pipe(fds);
int fd0_dup = dup(fds[0]);
fcntl(fd0_dup, F_SETFL, fcntl(fd0_dup, F_GETFL) | O_NONBLOCK);

Now both fd0_dup and fds[0] refer to the same open file description (because of the dup()), so the fcntl()operation affected both file descriptors.

现在 fd0_dup 和 fds[0] 都引用了相同的打开文件描述(因为dup()),所以fcntl()操作影响了两个文件描述符。

if ((fcntl(fds[0], F_GETFL) & O_NONBLOCK)) { ... }

Hence the observed behaviour here is required by POSIX.

因此这里观察到的行为是 POSIX 所要求的。