【问题标题】:Is O_NONBLOCK being set a property of the file descriptor or underlying file?O_NONBLOCK 是否被设置为文件描述符或基础文件的属性?
【发布时间】:2011-02-22 19:28:23
【问题描述】:

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

但是,在使用 FIFO 进行实验时,似乎不可能同时将阻塞 I/O 描述符和非阻塞 I/O 描述符添加到 FIFO(因此是否设置了 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;
}

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

【问题讨论】:

  • 我想知道这一点,因为如果设置的 O_NONBLOCK 是底层文件的属性,那么调用打开带有 O_NONBLOCK not 在 oflags 中设置的文件的调用可能 仍然返回带有 O_NONBLOCK 标志的文件描述符。

标签: c nonblocking file-descriptor fcntl posix-api


【解决方案1】:

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);

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

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

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

【讨论】:

  • 哦,好的。所以“文件描述符”和“文件描述”之间是有区别的。两个文件描述符可以共享同一个文件描述,标志是描述的一个属性。再次查看打开的文档,它说该功能“应创建一个打开的文件描述”。我想我现在明白了。
  • @DanielTrebbien:一些标志属于文件描述符,例如FD_CLOEXEC。
  • "O_NONBLOCK 是打开文件描述符的属性,不是文件描述符的属性,也不是基础文件的属性。"不明白后面提到的“打开文件描述符”、“文件描述符”和“打开文件描述”的区别。都是唯一的和独立的吗?
  • @TomE:有“打开文件描述符”和“打开文件描述”,两者是分开的。但是,第一句话中有一个错误,我将要修复它。该错误是由 2021 年 1 月 8 日在 Revision 6 中进行的编辑引入的,我已经回滚了,因为它完全颠覆了我的意思。我不确定我是如何错过当时正在进行的编辑的;我大概是被通知了。它再次被修复。
  • 您可以在答案中的链接中找到有关“打开文件描述符”和“打开文件描述”的信息,尤其是指向open() 的 POSIX 规范的链接。
猜你喜欢
  • 1970-01-01
  • 2014-04-10
  • 2012-03-27
  • 2017-09-05
  • 2020-03-26
  • 2011-05-15
  • 1970-01-01
  • 2014-04-17
  • 1970-01-01
相关资源
最近更新 更多