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()возвращениеPOLLHUPrevent. Возможно, вы захотите проверить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()дастPOLLINrevent когда данные готовы быть прочитанным, или EOF происходит. -
read()блокируется до тех пор, пока не будет прочитано запрошенное количество байтов, соединение не будет закрыто (возвращает 0), оно не будет прервано сигналом или не произойдет фатальная ошибка ввода-вывода. Этот блокирующий вид побеждает цель использованияpoll(), поэтомуpoll()почти всегда используется сO_NONBLOCK. Вы могли бы использоватьalarm()чтобы проснуться изread()после тайм-аута, но это слишком сложно. - если писатель закроется, то читатель получит
poll()POLLHUPrevent иread()вернутся0затем на неопределенный срок. На этом этапе читатель должен закрыть свою файловую ручку и снова открыть ее.
- на
- если вы открываете на стороне чтения с
O_RDONLY | O_NONBLOCK, тогда:- на
open()не будет блок. -
poll()дастPOLLINrevent когда данные готовы быть прочитанным, или EOF происходит.poll()также блокируется до тех пор, пока не будет доступен писатель, если его нет. - после того, как все имеющиеся в настоящее время данные считываются,
read()либо вернет -1 и seterrno == EAGAINесли соединение все еще открыто, или он вернется0если соединение закрыто (EOF) или еще не открыт писателем. Когдаerrno == EAGAIN, это означает, что пришло время вернуться кpoll(), так как соединение открыто, но нет больше данных. Когдаerrno == EINTR,read()еще не прочитал байтов и был прерван сигналом, поэтому его можно перезапустить. - если писатель закроется, то читатель получит
poll()POLLHUPrevent, иread()вернутся0затем на неопределенный срок. На этом этапе читатель должен закрыть свою файловую ручку и снова открыть ее.
- на
- (Linux-specific:) если вы открываете на стороне чтения с
O_RDWR, тогда:- на
open()не будет блокировать. -
poll()дастPOLLINrevent, когда данные готовы для чтения. Однако для именованных каналов EOF не вызоветPOLLINилиPOLLHUPrevents. -
read()блокируется до тех пор, пока не будет прочитано запрошенное количество байтов, оно не будет прервано сигналом или не произойдет другая фатальная ошибка ввода-вывода. Для именованных каналов он не вернетсяerrno == EAGAIN, и он даже не вернется0на EOF. Он будет просто сидеть там, пока не будет прочитано точное количество запрошенных байтов или пока он не получит сигнал (в этом случае он вернет количество байтов, прочитанных до сих пор, или вернет -1 и установитerrno == EINTRесли байты не были прочитаны до сих пор). - если писатель закроется, читатель не потеряет возможность прочитать именованный канал позже, если другой писатель откроет именованный канал, но читатель также не получит никакого уведомления.
- на
- (Linux-specific:) если вы откройте на стороне чтения с
O_RDWR | O_NONBLOCK, тогда:- на
open()не будет блокировать. -
poll()дастPOLLINrevent, когда данные готовы для чтения. Однако EOF не вызоветPOLLINилиPOLLHUPrevents на именованные каналы. - после того, как все имеющиеся в настоящее время данные считываются,
read()вернутся-1и seterrno == EAGAIN. Пришло время вернуться вpoll()ждать больше данных, возможно, из других потоков. - если писатель закрывается, читатель не потеряет возможность прочитать именованный канал позже, если другой писатель откроет именованный канал. Соединение является постоянным.
- на
как вы справедливо обеспокоены, используя O_RDWR с трубами не стандартно, POSIX или в другом месте.
однако, поскольку этот вопрос, похоже, часто возникает, лучший способ в Linux сделать "устойчивые именованные трубы", которые остаются живыми, даже когда одна сторона закрывается, и которые не вызывают POLLHUP revents или return 0 на read() - использовать O_RDWR | O_NONBLOCK.
я вижу три основных способа обработки именованных каналов в Linux:
-
(портативный.) Без
poll(), и с одной трубой:open(pipe, O_RDONLY);- основной цикл:
-
read()столько данных, сколько необходимо, возможно зацикливание наread()звонки.- если
read() == -1иerrno == EINTR,read()все снова. - если
read() == 0соединение закрывается, и все данные были получены.
- если
-
-
(портативный.) С
poll(), и с ожиданием, что трубы, даже именованные, открываются только один раз, и что после их закрытия они должны быть открыты как читателем, так и писателем, создавая новый трубопровод:open(pipe, O_RDONLY | O_NONBLOCK);- Главная петля:
-
poll()наPOLLINсобытия, возможно, на нескольких трубах одновременно. (Примечание: это предотвращаетread()от получения нескольких доказательств до подключения писателя.) -
read()столько данных, сколько необходимо, возможно зацикливание наread()звонки.- если
read() == -1иerrno == EAGAIN, возвращаться вpoll()шаг. - если
read() == -1иerrno == EINTR,read()снова и снова. - если
read() == 0, соединение закрыто, и вы должны завершите или закройте и снова откройте канал.
- если
-
-
(не портативный, специфичный для 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 страницы.