O_NONBLOCK是否设置了文件描述符或基础文件的属性?

Dan*_*ien 19 c file-descriptor nonblocking fcntl posix-api

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

然而,尝试使用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;
}
Run Code Online (Sandbox Code Playgroud)

这让我想到:是否有可能拥有一个非阻塞I/O描述符并阻止I/O描述符到同一个文件,如果是这样,它是否依赖于文件类型(常规文件,FIFO,块特殊文件) ,字符特殊文件,套接字等)?

Jon*_*ler 28

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

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

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

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

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

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

int fds[2];
pipe(fds);
int fd0_dup = dup(fds[0]);
fcntl(fd0_dup, F_SETFL, fcntl(fd0_dup, F_GETFL) | O_NONBLOCK);
Run Code Online (Sandbox Code Playgroud)

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

if ((fcntl(fds[0], F_GETFL) & O_NONBLOCK)) { ... }
Run Code Online (Sandbox Code Playgroud)

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

  • 哦好的.所以"文件描述符"和"文件描述"之间存在差异.两个文件描述符可以共享相同的文件描述,并且标志是描述的属性.再看一下打开的文档,它说函数"应该创建一个打开的文件描述".我想我现在明白了. (3认同)
  • @DanielTrebbien:一些标志属于文件描述符,例如`FD_CLOEXEC`. (2认同)
  • @TomE:有“打开文件描述符”和“打开文件描述”,两者是分开的。不过,第一句话有一个错误,我即将修复。该错误是由 2021 年 1 月 8 日在[修订版 6](https://stackoverflow.com/revisions/2889920/6) 中进行的编辑引入的,此后我已回滚该编辑,因为它完全颠覆了我所说的内容。我不知道我是如何错过当时正在进行的编辑的;我可能已经收到通知了。又重新修复了。 (2认同)
  • 您可以在答案中的链接中找到有关“打开文件描述符”和“打开文件描述”的信息,特别是“open()”的 POSIX 规范的链接。 (2认同)