Почему select в именованном канале без записи блокируется бесконечно?


Я вызываю select с одним именованным каналом fd в read_fds. Этот именованный канал не имеет записи и был открыт только в неблокирующем режиме только для чтения. Я ожидал бы, что select возвращается с именованным каналом fd, помеченным как готовый к чтению, и что попытка чтения из канала возвращает 0:

Из manpage на Читать:

При попытке чтения из пустой трубы или FIFO:

  • Если ни один процесс не имеет открытой трубы для записи, read () возвращает 0 в > indicate конец файла.

Однако, выберите только блоки на неопределенное время. Почему так происходит?

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

#include <stdexcept>
#include <thread>
#include <iostream>

int main()
{
    char buf[4096];

    // Create a named pipe
    auto err = mkfifo("/tmp/whatever",0666);
    if(err) {
        throw std::runtime_error(
                    std::string("Failed to create fifo ")+
                    strerror(errno));
    }

    std::thread reader_thread(
                [&](){
        auto fd = open("/tmp/whatever",O_RDONLY|O_NONBLOCK);
        if(fd < 0) {
            throw std::runtime_error("Failed to open fifo");
        }

        fd_set fds;
        while(1) {
            FD_ZERO(&fds);
            FD_SET(fd,&fds);
            std::cerr << "calling select" << std::endl;
            auto retval = select(fd+1,&fds,nullptr,nullptr,nullptr);
            if(retval < 0) {
                std::runtime_error("Failed to call select");
            }

            if(FD_ISSET(fd,&fds)) {
                auto read_bytes = read(fd,buf,4096);
                std::cerr << "read " << read_bytes << std::endl;
                if(read_bytes==0) {
                    break;
                }
            }
        }

        close(fd);
    });

    reader_thread.join();

    return 0;
}
1 3

1 ответ:

Из документации POSIX fo select:

Дескриптор считается готовым к считыванию, когда вызов функции ввода с o_nonblock clear не блокируется, независимо от того, будет ли функция успешно передавать данные. (Функция может возвращать данные, указание конца файла или ошибку, отличную от той, которая указывает на то, что он заблокирован, и в каждом из этих случаев дескриптор считается готовым для чтения.

...

Если ни один из выбранные дескрипторы готовы к запрошенной операции, функция pselect() или select() блокируется до тех пор, пока не будет готова хотя бы одна из запрошенных операций, до тех пор, пока не наступит тайм-аут или пока не будет прерван сигнал.

Из manpage pipe(7) (который является базовым объектом FIFO):

Если все файловые дескрипторы, ссылающиеся на конец записи канала , были закрыты , то при попытке чтения(2) из канала будет виден конец файла (read(2) вернет 0).

Обратите внимание на использование настоящего совершенного времени! Это означает, что FIFO должен быть открыт с обеих сторон fiorst , закрытый со стороны записи (для вашего приложения), чтобы генерировать условие EOF.

Итак, если ФИФО в конечном счете не закрывается автором, почему select должен возвращаться? Значение параметра для самого файла (fifo-)не имеет значения по уважительной причине: он вводит условие гонки между открытием с обеих сторон при использовании наиболее эффективный метод для чтения более одного байта за раз. Это обычный способ, например, для командного канала: запустить процесс чтения и затем записать (что, как правило, совершенно не связано с программой при использовании именованного канала).

Если вы хотите, чтобы select возвращалось раньше, используйте Аргумент timeout. Но обычно используется отдельный поток, который может быть прерван сигналом (смотрите man-страницу select для получения дополнительной информации).

В качестве примечания: одна хорошая вещь в Linux/POSIX-это то, что он делает на самом деле не имеет значения, используете ли вы FIFO, файл или драйвер микрофона.