c - O_NONBLOCK 是否被设置为文件描述符或基础文件的属性?

标签 c nonblocking file-descriptor 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;
}

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

最佳答案

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_dupfds[0] 都引用相同的打开文件描述(因为 dup()),所以fcntl() 操作影响了两个文件描述符。

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

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

关于c - O_NONBLOCK 是否被设置为文件描述符或基础文件的属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2889901/

相关文章:

从 C 中的命令行将字符转换为整数

java - 在 Play Controller-Action 中通过 #flatMap() 链接 Promise

c - select() 和 C 上带有动态缓冲区的非阻塞 recv

qt - 非阻塞循环 {while( QMutex::trylock() );} 如何工作?

c - unlinkat() C 中的目录

c - 等待来自文件描述符的输入

c++ - 是否可以使用 std::array 作为 POD 结构的数据容器?

c - 在 C 中的 EOF 之后执行

c - 通过更改 C 中的函数调用来节省代码空间

子 shell 中的 Bash 描述符