O RDWR на именованных трубах с опросом()

Я прошел через различные реализации клиента/сервера с именем Linux, но большинство из них используют блокировку по умолчанию для чтения/записи.

поскольку я уже использую poll() для проверки других флагов, я, хотя было бы неплохо проверить входящие данные FIFO через poll ()...

после всех исследований я думаю, что открытие трубы в режиме O_RDWR-единственный способ предотвратить бесконечное количество событий EOF на трубе, когда ни один писатель не имеет открыть его.

этот путь оба конца трубы закрыт и другие клиенты могут раскрыть writable конец также. Чтобы ответить, я использовал бы отдельные трубы...

моя проблема в том, что, хотя я нашел несколько примеров, которые используют флаг O_RDWR, manpages open() описывают этот флаг как неопределенный при назначении FIFO. (http://linux.die.net/man/3/open)

но как бы вы использовали poll () на трубе без O_RDWR? Как вы думаете, "O_RDWR" - это законный способ открыть трубы???

2 ответов


во-первых, некоторые предварительные замечания:

используя O_NONBLOCK и poll() это обычная практика, а не наоборот. Для успешной работы необходимо обязательно обработать все poll() и read() возврат состояния правильно:

  • read() возвращаемое значение 0 означает EOF -- другая сторона закрыла свое соединение. Это соответствует (обычно, но не на всех ОС)poll() возвращение POLLHUP revent. Возможно, вы захотите проверить POLLHUP перед попытка read(), но это не совсем необходимо, так как read() гарантирует возврат 0 после того, как сторона записи закрылась.
  • если вы называете read() прежде чем писатель подключился, и у вас есть O_RDONLY | O_NONBLOCK, вы получите EOF (read() возвращение 0) неоднократно, как вы заметили. Однако, если вы используете poll() ждать POLLIN событие перед вызовом read(), он будет ждать, пока писатель подключится, а не производить доказательства.
  • read() возвращаемое значение -1 как правило, означает ошибку. Однако, если errno == EAGAIN, это просто означает, что нет больше данных, доступных прямо сейчас, и вы не блокируете, поэтому вы можете вернуться к poll() в случае, если другие устройства нуждаются в обработке. Если errno == EINTR, потом read() был прерван перед чтением любых данных, и вы можете либо вернуться к poll() или просто позвонить read() снова немедленно.

теперь, для Linux:

  • если вы открываете на стороне чтения с O_RDONLY, затем:
    • на open() будет блокировать, пока не откроется соответствующий писатель.
    • poll() даст POLLIN revent когда данные готовы быть прочитанным, или EOF происходит.
    • read() блокируется до тех пор, пока не будет прочитано запрошенное количество байтов, соединение не будет закрыто (возвращает 0), оно не будет прервано сигналом или не произойдет фатальная ошибка ввода-вывода. Этот блокирующий вид побеждает цель использования poll(), поэтому poll() почти всегда используется с O_NONBLOCK. Вы могли бы использовать alarm() чтобы проснуться из read() после тайм-аута, но это слишком сложно.
    • если писатель закроется, то читатель получит poll() POLLHUP revent и read() вернутся 0 затем на неопределенный срок. На этом этапе читатель должен закрыть свою файловую ручку и снова открыть ее.
  • если вы открываете на стороне чтения с O_RDONLY | O_NONBLOCK, тогда:
    • на open() не будет блок.
    • poll() даст POLLIN revent когда данные готовы быть прочитанным, или EOF происходит. poll() также блокируется до тех пор, пока не будет доступен писатель, если его нет.
    • после того, как все имеющиеся в настоящее время данные считываются, read() либо вернет -1 и set errno == EAGAIN если соединение все еще открыто, или он вернется 0 если соединение закрыто (EOF) или еще не открыт писателем. Когда errno == EAGAIN, это означает, что пришло время вернуться к poll(), так как соединение открыто, но нет больше данных. Когда errno == EINTR, read() еще не прочитал байтов и был прерван сигналом, поэтому его можно перезапустить.
    • если писатель закроется, то читатель получит poll() POLLHUP revent, и read() вернутся 0 затем на неопределенный срок. На этом этапе читатель должен закрыть свою файловую ручку и снова открыть ее.
  • (Linux-specific:) если вы открываете на стороне чтения с O_RDWR, тогда:
    • на open() не будет блокировать.
    • poll() даст POLLIN revent, когда данные готовы для чтения. Однако для именованных каналов EOF не вызовет POLLIN или POLLHUP revents.
    • read() блокируется до тех пор, пока не будет прочитано запрошенное количество байтов, оно не будет прервано сигналом или не произойдет другая фатальная ошибка ввода-вывода. Для именованных каналов он не вернется errno == EAGAIN, и он даже не вернется 0 на EOF. Он будет просто сидеть там, пока не будет прочитано точное количество запрошенных байтов или пока он не получит сигнал (в этом случае он вернет количество байтов, прочитанных до сих пор, или вернет -1 и установит errno == EINTR если байты не были прочитаны до сих пор).
    • если писатель закроется, читатель не потеряет возможность прочитать именованный канал позже, если другой писатель откроет именованный канал, но читатель также не получит никакого уведомления.
  • (Linux-specific:) если вы откройте на стороне чтения с O_RDWR | O_NONBLOCK, тогда:
    • на open() не будет блокировать.
    • poll() даст POLLIN revent, когда данные готовы для чтения. Однако EOF не вызовет POLLIN или POLLHUP revents на именованные каналы.
    • после того, как все имеющиеся в настоящее время данные считываются, read() вернутся -1 и set errno == EAGAIN. Пришло время вернуться в poll() ждать больше данных, возможно, из других потоков.
    • если писатель закрывается, читатель не потеряет возможность прочитать именованный канал позже, если другой писатель откроет именованный канал. Соединение является постоянным.

как вы справедливо обеспокоены, используя O_RDWR с трубами не стандартно, POSIX или в другом месте.

однако, поскольку этот вопрос, похоже, часто возникает, лучший способ в Linux сделать "устойчивые именованные трубы", которые остаются живыми, даже когда одна сторона закрывается, и которые не вызывают POLLHUP revents или return 0 на read() - использовать O_RDWR | O_NONBLOCK.

я вижу три основных способа обработки именованных каналов в Linux:

  1. (портативный.) Без poll(), и с одной трубой:

    • open(pipe, O_RDONLY);
    • основной цикл:
      • read() столько данных, сколько необходимо, возможно зацикливание на read() звонки.
        • если read() == -1 и errno == EINTR, read() все снова.
        • если read() == 0 соединение закрывается, и все данные были получены.

  2. (портативный.) С poll(), и с ожиданием, что трубы, даже именованные, открываются только один раз, и что после их закрытия они должны быть открыты как читателем, так и писателем, создавая новый трубопровод:

    • open(pipe, O_RDONLY | O_NONBLOCK);
    • Главная петля:
      • poll() на POLLIN события, возможно, на нескольких трубах одновременно. (Примечание: это предотвращает read() от получения нескольких доказательств до подключения писателя.)
      • read() столько данных, сколько необходимо, возможно зацикливание на read() звонки.
        • если read() == -1 и errno == EAGAIN, возвращаться в poll() шаг.
        • если read() == -1 и errno == EINTR, read() снова и снова.
        • если read() == 0, соединение закрыто, и вы должны завершите или закройте и снова откройте канал.

  3. (не портативный, специфичный для Linux.) С poll(), и с ожиданием, что именованные каналы никогда не заканчиваются и могут быть подключены и отключены несколько раз:

    • open(pipe, O_RDWR | O_NONBLOCK);
    • основной цикл:
      • poll() на POLLIN события, возможно, на нескольких трубах в однажды.
      • read() столько данных, сколько необходимо, возможно зацикливание на read() звонки.
        • если read() == -1 и errno == EAGAIN, возвращаться в poll() шаг.
        • если read() == -1 и errno == EINTR, read() снова и снова.
        • если read() == 0 что-то не так ... это не должно быть с O_RDWR на именованные каналы, но только с O_RDONLY или неназванные трубы; это указывает на закрытую трубу, которая должна быть закрыта и повторно открыта. Если вы смешиваете именованные и неназванные трубы в же poll() цикл обработки событий, этот случай все еще может потребоваться обработать.

по данным "открыть" (2) man page, вы можете пройти O_RDONLY|O_NONBLOCK или O_WRONLY|O_NONBLOCK избежать open syscall будет заблокирован (Вы получите errno == ENXIO в этом случае)

как я прокомментировал Читайте также fifo (7) и mkfifo(3) man страницы.