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 и seterrno == 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
и 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 страницы.