Оказалось, что в отличие от моего комментария к первому вопросу выше, существует прямое решение.
Данное решение предполагает, что все читатели и все авторы одного и того же FIFO совместно используют буферы ядра. Это должен быть самый логичный и простой способ реализации FIFO (учитывая их поведение), поэтому я ожидаю, что все системы, обеспечивающие FIFO, будут вести себя таким образом. Однако это просто мое предположение, а не какая-либо гарантия. Я не нашел ничего в соответствующих стандартах POSIX, чтобы поддержать или противоречить этому. Пожалуйста, сделайте трубку, если найдете другое.
Процедура тривиальна:
Когда клиент обращается в нуль неожиданно, писатель снова открывает FIFO, не закрывая исходный дескриптор первого. Это open()
будет блокироваться, пока не появится новый читатель, но поскольку исходный файловый дескриптор все еще открыт, данные, уже буферизированные в FIFO, будут доступны новому читателю. Если open()
преуспевает, писатель просто закрывает исходный дескриптор и вместо этого переключается на использование нового дескриптора.
Если структуры ядра совместно используются, состояние буфера FIFO разделяется между дескрипторами писателя, и новый читатель сможет прочитать, что предыдущий читатель оставил непрочитанным.
(Обратите внимание, что автор не знает количество данных, буферизованных между изменениями клиента, и, следовательно, не знают о точке в потоке данных, где переключатель происходит.)
Я проверил эту тривиальная стратегия работает на Linux 3.8.0-35-generic ядер на x86_64 в Ubuntu, а также 2.6.9-104.ELsmp на x86_64.
Однако я до сих пор полностью согласен с либо принять потерю данных, или изменения протокола, как это было предложено Basile Starynkevitch в комментарии к первоначальному вопросу.
Лично я нашел сокеты домена Unix (привязанные к имени пути, скажем /var/run/yourservice/unix
), чтобы быть намного лучшим вариантом, поскольку он позволяет нескольким одновременным клиентам без повреждения данных (в отличие от FIFO) и гораздо более здравого протокола.
Я предпочитаю сокеты с дейтаграммой Unix с порядковым номером и длиной дейтаграммы в начале каждой дейтаграммы.(Длина помогает клиенту проверить его, прочитав всю дейтаграмму, я действительно не ожидаю, что какая-либо ОС обрезает дейтаграммы Unix.)
Обычно писатель отправляет несколько дейтаграмм каждому клиенту и ждет подтверждения, прежде чем отправлять новые из них. После обработки дейтаграммы клиент подтверждает дейтаграмму, отправив порядковый номер автору. (Помните, что это сокеты, поэтому связь двунаправленная.) Это позволяет писателю сохранять несколько дейтаграмм в полете на каждого клиента, а клиент обрабатывать дейтаграммы в правильном порядке (порядковый номер) или вне порядка , используя несколько потоков.
Важным моментом является то, что каждая датаграмма подтверждается только после ее обработки, а не сразу после ее получения.
(В дополнение к читателю-писателю «acks» (подтверждения) я также поддерживал бы ответы «пожалуйста, повторно отправьте», если клиент использовал слишком маленький буфер для получения дейтаграммы или опустил его на пол из-за по некоторым другим причинам. А может быть, даже «гадость» для дейтаграмм клиент не знает, что делать.)
Если клиент обращается в нуль, автор знает, что все не-признал дейтаграммы не были обработаны клиентом тем не менее, и может повторно отправить их другому подключенному клиенту или будущему клиенту.
Вопросы?
Практично ли закрывать и открывать трубу? – rici
Вы пытались [опрос (2)] (http://man7.org/linux/man-pages/man2/poll.2.html)? –
@BasileStarynkevitch: Даже с 'poll()', канал может быть готов для записи, а затем, как раз перед тем, как вы напишете, читатель умрет: вы потеряли данные! – rodrigo