Почему poll () немедленно возвращает обычные файлы и блокирует fifo? - PullRequest
0 голосов
/ 31 марта 2020

Я проверял этот код несколько раз и не могу понять, почему poll () немедленно возвращается?

Здесь файл открыт для чтения и должен ждать события. Как заставить его ждать ввода?

#include <iostream>

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


using namespace std;

ssize_t read_out_to_the_end(int fd){
   char chunk[1024];
   ssize_t ret = 0, n;
   while((n = ::read(fd, chunk, sizeof chunk)) > 0){
      ret += n;
      cerr << "read chunk: " << n << " | ";
      cerr.write(chunk, n);
      cerr << endl;
   }
   if (n < 0) {
       cerr << "err in read" << endl;
   }
   else if (ret == 0){
      cerr << "nothing to read" << endl;
   }
   return ret;
}

int main() {
   int bininfd = open("bin-in", O_RDONLY | O_CREAT);//, 0644/*S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH*/);
   if (bininfd < 0) {
      perror("err in open(binin)");
      return -1;
   }

   struct pollfd pollfds[] = {
         {bininfd, POLLIN, 0},
   };
   auto&[pfd] = pollfds;

   while (1) {
      pfd.revents = 0;  // cleanup, shouldn't it be redundant
      int pollret = poll(pollfds, 1, -1);
      if (pollret > 0) {
         if (pfd.revents & POLLIN) {
            cerr << "(pfd.revents & POLLIN)" << endl;
            read_out_to_the_end(pfd.fd);
         }
      } else if (pollret == 0) {
         cerr << "poll timed out" << endl;
         continue;
      } else {
         cerr << "check for error" << endl;
         continue;
      }
   }
}

вывод

(pfd.revents & POLLIN)
nothing to read
(pfd.revents & POLLIN)
nothing to read
(pfd.revents & POLLIN)
nothing to read
(pfd.revents & POLLIN)
nothing to read
(pfd.revents & POLLIN)
............... etc ....................

живой пример

ОБНОВЛЕНИЕ:

  1. read_out_to_the_end () исправлено. Благодаря @ RemyLebeau
  2. он работает (блокируется) на пятерках, как я ожидаю, но не на обычных файлах. Почему?

Ответы [ 2 ]

3 голосов
/ 01 апреля 2020

poll() или select() никогда не блокируют обычные файлы. Они всегда возвращают обычный файл как «готовый». Если вы хотите использовать poll(), чтобы сделать то, что делает tail -f, вы на неправильном пути.

Цитирование из стандарта SUSv4 :

Функция poll() должна поддерживать обычные файлы, терминальные и псевдо-терминальные устройства, FIFO, каналы, сокеты и файлы на основе [OB XSR] STREAMS. Поведение poll() для элементов fds, которые ссылаются на другие типы файлов, не определено.

Обычные файлы всегда должны опрашивать TRUE для чтения и записи .

Поскольку использование poll() или select() для обычных файлов практически бесполезно, новые интерфейсы попытались это исправить. На BSD вы можете использовать kqueue(2) с EVFILT_READ и на Linux inotify(2) с IN_MODIFY. Более новый интерфейс epoll(7) на Linux просто выдаст ошибку EPERM, если вы попытаетесь просмотреть обычный файл.

К сожалению, ни один из них не является стандартным.

1 голос
/ 31 марта 2020

read_out_to_the_end() имеет несколько проблем:

  • ret не инициализировано.

  • while l oop увеличивается n когда это должно быть назначено вместо этого. Но тогда, если while l oop попадет в EOF, if( n == 0) будет истинным, даже если данные действительно были прочитаны до нажатия EOF.

  • chunk может быть нулевым Завершено, но может также принимать значения NULL, в зависимости от входных данных. Поэтому не следует записывать в cerr (почему бы не cout?), Используя operator<<, вместо этого используйте cerr.write(), чтобы вы могли передать ему фактическое число прочитанных байтов.

Попробуйте вместо этого:

ssize_t read_out_to_the_end(int fd){
   char chunk[1024];
   ssize_t ret = 0, n;
   while((n = ::read(fd, chunk, sizeof chunk)) > 0){
      ret += n;
      cerr << "read chunk: " << n << " | ";
      cerr.write(chunk, n);
      cerr << endl;
   }
   if (n < 0) {
       cerr << "err in read" << endl;
   }
   else if (ret == 0){
      cerr << "nothing to read" << endl;
   }
   return ret;
}

int main() {
   int bininfd = open("bin-in", O_RDONLY | O_CREAT);//, 0644/*S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH*/);
   if (bininfd < 0) {
      perror("err in open(binin)");
      return -1;
   }

   pollfd pfd = {};
   pfd.fd = bininfd;
   pfd.events = POLLIN;

   while (true) {
      pfd.revents = 0;  // cleanup, shouldn't it be redundant
      int pollret = poll(&pfd, 1, -1);
      if (pollret > 0) {
         if (pfd.revents & POLLIN) {
            cerr << "(pfd.revents & POLLIN)" << endl;
            read_out_to_the_end(pfd.fd);
         }
      } else if (pollret == 0) {
         cerr << "poll timed out" << endl;
         continue;
      } else {
         cerr << "poll error " << errno << endl;
         break;
      }
   }
}

Кроме того, в примечании open() документация гласит:

Аргумент mode указывает биты режима файла, применяемые при создании нового файла. Этот аргумент должен быть предоставлен, когда O_CREAT или O_TMPFILE указаны в flags; если ни O_CREAT, ни O_TMPFILE не указаны, mode игнорируется. Эффективный режим изменяется с помощью umask процесса обычным способом: при отсутствии ACL по умолчанию режим создаваемого файла (mode & ~umask). Обратите внимание, что этот режим применяется только к будущему доступу к вновь созданному файлу; open() вызов, который создает файл только для чтения, вполне может вернуть дескриптор файла для чтения / записи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...