O_NONBLOCK устанавливает свойство дескриптора файла или базового файла?

Из того, что я читал на сайте Open Group на fcntl, open, read, а также write Складывается впечатление что ли O_NONBLOCK устанавливается в дескрипторе файла, и, следовательно, используется ли неблокирующий ввод-вывод с дескриптором, должно быть свойством этого дескриптора файла, а не лежащим в основе файла. Быть свойством дескриптора файла означает, например, что если я дублирую файловый дескриптор или открою другой дескриптор для того же файла, то я могу использовать блокирующий ввод-вывод с одним и неблокирующий ввод-вывод с другим.

Однако, экспериментируя с FIFO, кажется, что невозможно иметь блокирующий дескриптор ввода-вывода и неблокирующий дескриптор ввода-вывода для 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;
}

Это заставляет меня задуматься: возможно ли когда-нибудь иметь неблокирующий дескриптор ввода-вывода и блокировать дескриптор ввода-вывода для одного и того же файла, и если это так, зависит ли это от типа файла (обычный файл, FIFO, блокировать специальный файл, символьный специальный файл, сокет и т. д.)?

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.

Другие вопросы по тегам