LINUX.ORG.RU

[FIFO] Запись в канал, из которого никто не читает

 


0

0

Для получения сообщений от CGI, выполняющемся на сервере, я использую FIFO, содержимое которого периодически считывается другим CGI из xmlhttprequest'а. FIFO в основном CGI открывается так:

int file = open(FIFO_OUT, O_RDWR|O_NONBLOCK);
outfile = fdopen(file, "w+");
затем все сообщения fprintf'ом записываются в это FIFO.

Считывающий сообщения CGI открывает FIFO так:

f = open(FIFO, O_RDONLY|O_NONBLOCK);
и считывает данные с использованием select'а.

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

Вопрос: как можно сделать нормальную буферизацию FIFO?

P.S. до этого я пытался сделать обмен информацией при помощи очередей сообщений, но здесь возникает проблема в ограничении общего объема сообщений и фиксированной их длине: если делаем сообщения достаточной длины, их получается около десятка; если же увеличить их количество, длина сообщений становится слишком маленькой.

☆☆☆☆☆

При записи надо проверять код возврата. Когда буфер переполняется, write возвращает ошибку.

mv ★★★★★
()
Ответ на: комментарий от mv

fprintf возвращает всегда положительное значение (и когда файл открыт на считывание, и когда никто из него не читает). Кроме того, я обрабатываю все сигналы - никаких SIGPIPE не поступает. Данные просто «теряются».

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от ananas

Проверял: «читателя» точно нет. И специально запускал пишущую программу без читающей - все равно данные пропадают. Плюс ко всему, теряется все содержимое буфера FIFO, если пишущая программа завершается, а читающая еще не запустилась.

Можно, конечно, выделить кусок разделяемой памяти килобайт на 500, и вместо FIFO использовать его. Но в этом случае придется писать свои printf'ы...

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

Кроме того, я обрабатываю все сигналы - никаких SIGPIPE не поступает. Данные просто «теряются».

Раз у тебя программа не падает, то я предположил, что SIGPIPE игнорируется.

mv ★★★★★
()
Ответ на: комментарий от Eddy_Em

Проверял: «читателя» точно нет. И специально запускал пишущую программу без читающей - все равно данные пропадают.

Это как ты проверял, что они пропадают?

Плюс ко всему, теряется все содержимое буфера FIFO, если пишущая программа завершается, а читающая еще не запустилась.

Естественно. Буфер пайпа - это in-kernel структура, при исчезновении всех её пользователей она прибивается.

mv ★★★★★
()
Ответ на: комментарий от mv

SIGPIPE игнорируется.

Нет, все 256 сигналов «заворачиваются» в начале на обработчик:

for( _=0; _<255; _++) signal(_, stop);
А он пишет в лог-файл. Кстати, падать-то программа не должна (т.к. FIFO открыт в неблокирующем режиме).

Это как ты проверял, что они пропадают?

Запускаю «писалку», потом «читалку» - часть данных, записанных «писалкой» в начале, а также все данные, записанные ей до выхода, теряются.

Есть ли другие решения? Мне нужно, чтобы сообщения основной программы в асинхронном режиме посредством другого CGI могли бы отправляться клиенту. Причем отправка сообщений не должна блокировать основной процесс. Ну и считывающий процесс не должен блокироваться, чтобы не выскакивали ошибки xml-запроса.

Эх, когда уже firefox будет поддерживать веб-сокеты... :(

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от mv

Что-то пока не очень-то получается... ;)

Так я надеялся, что кто-нибудь подскажет простое решение.

А так - придется, наверное, завести «демона», который будет считывать все сообщения из FIFO и писать их в обычный файл, используя блокировки. Ну, а «читалка» будет каким-нибудь SIGUSR1 сообщать «демону», что файл считан, и нужно сделать lseek(fd, 0, SEEK_SET). Правда, очень уж не хочется плодить процессы...

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

> Да проще свой велосипед написать, чем этим пользоваться.

Когда-нибудь ты поймешь, что всякие фреймворки придуманы не зря. Да и учебники по программированию тоже придумали не зря.

Могу предложить сделать выделенный буферизующий процесс - по-моему, как раз в духе остальной системы.

tailgunner ★★★★★
()
Ответ на: комментарий от ananas

а использовать msgsnd()/msqrcv() не думал?

Выше я уже писал: и в очередях сообщений system V, и в очередях POSIX, если сообщений много, буфера не хватает. У меня сообщения переменной длины, поэтому я не могу сказать

echo 1024 > /proc/sys/fs/mqueue/msg_max
т.к. это приведет к уменьшению длины сообщений. Хотя, можно, конечно, попробовать (в другой веб-оболочке, попроще, я и использовал очереди - но там частенько возникало переполнение буфера).

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от tailgunner

Да и учебники по программированию тоже придумали не зря.

Да я уже проштудировал «взаимодействие процессов» Стивенса от корки до корки :)

Могу предложить сделать выделенный буферизующий процесс

Попробую, все-таки, как-нибудь выкрутиться с очередями; если не получится - буду делать отдельный буферизующий процесс.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

> Да я уже проштудировал «взаимодействие процессов» Стивенса от корки до корки :)

Насколько я понял из начального поста, ты пишешь в FIFO без select? Обычным fprintf, без всякого протокола?

tailgunner ★★★★★
()

напросай минимальный тест, приводящей к ошибке - так будет проще понять где сбоит

x905 ★★★★★
()
Ответ на: комментарий от x905

набросай минимальный тест, приводящей к ошибке - так будет проще понять где сбоит

Да и так понятно - выше я уже писал.

В общем, плюнул я на FIFO и сделал через очереди сообщений. Выделил 256 сообщений по 512 байт, надеюсь, хватит.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от tailgunner

Насколько я понял из начального поста, ты пишешь в FIFO без select? Обычным fprintf, без всякого протокола?

Да. А по-другому и нельзя: пишущий процесс не может ждать, пока появится читающий.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

>> Насколько я понял из начального поста, ты пишешь в FIFO без select? Обычным fprintf, без всякого протокола?

Да.

То есть ты сам заранее запрограммировал потерю данных и знаешь об этом %)

А по-другому и нельзя: пишущий процесс не может ждать, пока появится читающий.

Ну вообще это фундаментальная проблема систем с очередями - если в очереди кончается место, данные теряются :D

tailgunner ★★★★★
()
Ответ на: комментарий от Eddy_Em

Для SysV есть /proc/sys/kernel/msgmnb или размер очереди можно задавать через msgctl(). Тогда туда влазит много.

mky ★★★★★
()
Ответ на: комментарий от mky

Спасибо, знаю. Единственно, нужно сначала от рута увеличить предельное количество сообщений и/или их предельный объем, иначе msgctl завершится с ошибкой.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

А так - придется, наверное, завести «демона», который будет считывать все сообщения из FIFO и писать их в обычный файл, используя блокировки. Ну, а «читалка» будет каким-нибудь SIGUSR1 сообщать «демону», что файл считан, и нужно сделать lseek(fd, 0, SEEK_SET). Правда, очень уж не хочется плодить процессы...

Если двигаться в таком направлении, то намного проще сделать аналог tail -f. Пишем в файл в режиме APPEND, а читатель переодически следит за изменениями и дочитывает новые блоки. Плюс ротация бинарных логов.

mikki
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.