Появилась проблема при использовании boost::interprocess::message_queue(boost 1.42) —
это именованная глобальная очередь сообщений, в которую можно писать/читать из разных процессов
В системе, которую мы разработываем очередь используется для межпроцессного взаимодействия.
Система разработывается под Linux.
До тех пор, пока все процессы стартуют, завершаются в штатном режиме очередь сообщений работает корректно.
Если один из процессов аварийно завершается (ловит сигфолт) а в это время один из его потоков
выполенял операции try_send/try_receive в это же самое время, то очередь переходит в "заблокированное состояни".
После такого проишествия ни один из процессов не может более выполнить операции с очередью.
Вызов функций try_send/try_receive блокируют поток, вызваший их.
Запустил под отладчиком процесс после очередного фатального завершения процесса и оказалось, что поток блокируется
на функции message_queue::do_send(block_t block, ...) на инструкции:
line 448: scoped_lock<interprocess_mutex> lock(p_hdr->m_mutex);
Как я понимаю фатально завершившийся поток выполнил lock для межпроцессорного мьютекса p_hdr->m_mutex, а соответствующий
unlock выполнить не успел.
Я так понимаю проблема состоит в том, что межпроцессорный мьютекс никак не связан с процессом, который его использует,
поэтому падение процесса не приводит к его разблокировке.
Существует ли возможность определить, что очередь оказалась в такой "нештатной" блокировке, и вернуть очередь в рабочее состояние?
--
Дмитрий
Re: boost::interprocess::message_queue сбой в работе очереди
V>Как я понимаю фатально завершившийся поток выполнил lock для межпроцессорного мьютекса p_hdr->m_mutex, а соответствующий V>unlock выполнить не успел.
V>Я так понимаю проблема состоит в том, что межпроцессорный мьютекс никак не связан с процессом, который его использует, V>поэтому падение процесса не приводит к его разблокировке.
V>Существует ли возможность определить, что очередь оказалась в такой "нештатной" блокировке, и вернуть очередь в рабочее состояние?
Если это именно мьютекс, то ОС освободит его при смерти процесса. Скорее всего там используется не мьютекс, а семафор или что-то самопальное.
В целом такая возможность есть, такая синхронизация называется robust IPC. В Windows мьютексы всегда robust, при смерти владеющего процесса WaitForSingleObject() вернёт WAIT_ABANDONED, это значит, что процессу надо "восстановить" состояние после умершего процесса до разблокировки мьютекса. В POSIX мьютекс надо явно инициализировать как robust с помощью pthread_mutexattr_setrobust(), тогда процесс будет получать EOWNERDEAD после смерти владеющего процесса, и после восстановления надо явно пометить данные как консистентные с помощью pthread_mutex_consistent().
Только учти, что что бы "восстанавливать" данные, вся работа под мьютексом должна быть написана в стиле lock-free (с помощью упорядоченных атомарных операций), т.к. данные после обычного C/C++ кода восстановить не удастся.
Плюс есть такие моменты, как например нельзя отдельно выделить память под сообщение, а потом отдельно поместить сообщение в очередь — при таком подходе начнутся утечки памяти. Выделение памяти и помещение в очередь должны быть так или иначе связаны, что бы их можно было целиком восстановить (в частности освободить память, если сообщение так и не попало в очередь).
Здравствуйте, varga, Вы писали:
V>Привет, всем!
V>Если один из процессов аварийно завершается (ловит сигфолт) а в это время один из его потоков V>выполенял операции try_send/try_receive в это же самое время, то очередь переходит в "заблокированное состояни".
... V>Как я понимаю фатально завершившийся поток выполнил lock для межпроцессорного мьютекса p_hdr->m_mutex, а соответствующий V>unlock выполнить не успел.
V>Я так понимаю проблема состоит в том, что межпроцессорный мьютекс никак не связан с процессом, который его использует, V>поэтому падение процесса не приводит к его разблокировке.
V>Существует ли возможность определить, что очередь оказалась в такой "нештатной" блокировке, и вернуть очередь в рабочее состояние?
А можно вопрос.
Ты пробовал назначить обработчик сигнала на SIGSEGV и в нем проверить вот тот самый залоченный межпроцессный mutex и если он залочен, то его разлочить или дождаться пока он разлочится и там уже падать?
А так согласен, падение процесса не приводит к разблокировке залоченного IPC мьютекса.
Re[2]: boost::interprocess::message_queue сбой в работе очер
Здравствуйте, blackwater, Вы писали:
B>А можно вопрос.
B>Ты пробовал назначить обработчик сигнала на SIGSEGV и в нем проверить вот тот самый залоченный межпроцессный mutex и если он залочен, то его разлочить или дождаться пока он разлочится и там уже падать?
B>А так согласен, падение процесса не приводит к разблокировке залоченного IPC мьютекса.
Обработчик сигнала SIGSEGV есть, но он не "лезит" во внутренности message_queue. Даже если будет применено такое решение, то гарантии целостности очереди нет.
А если будет получен сигнал SIGKILL то обработчик и вовсе не сработает.
Re[2]: boost::interprocess::message_queue сбой в работе очер
Здравствуйте, remark, Вы писали:
V>>Существует ли возможность определить, что очередь оказалась в такой "нештатной" блокировке, и вернуть очередь в рабочее состояние?
R>Если это именно мьютекс, то ОС освободит его при смерти процесса. Скорее всего там используется не мьютекс, а семафор или что-то самопальное.
нет там внутри этой эмуляции очереди обыкновенный pthread_mutex_t созданный в шареной памяти с аттрибутом PTHREAD_PROCESS_SHARED
т.е. при смерти одного процесса еси не почистить эту саму память (что конечно же грязный хак) он останется там залоченым
фигня в том что из прикладного кода добраться до этого mutex'a топикстартеру буит аццки не просто (если вообще возможно... ну конечно всегда можно похакать буст %) ... так что второй совет в этом thread'e (повеситься на SIGSEGV и делать unlock) также реализуем с невероятным трудом
теперь об эмуляции: заглянув в исходнеки был разочарован что message_queue этот на сам деле ни какой не POSIX message queue (see mans: mq_open, mq_close, mq_send, mq_receive), а тупо шареная память и интерпроцессный mutex...
топикстартеру рекомендую ознакомиться с `man 7 mq_overview`. от себя добавлю что в POSIX message queue ни кааких локов не требуется вв юзерском коде пользоваться легко и удобно (с помощью простецких С++ных врапперов конечно же %) которые к слову говоря сильно проще чем в бусте накрученная эмуляция)... ну и кроме всего прочего нативные message queue умеют уведомлять процесс (каким скажешь сигналом) о пришедшем сообщении (не нада ничо полоть)
Re[3]: boost::interprocess::message_queue сбой в работе очер
Здравствуйте, varga, Вы писали:
V>Здравствуйте, blackwater, Вы писали:
B>>А можно вопрос.
B>>Ты пробовал назначить обработчик сигнала на SIGSEGV и в нем проверить вот тот самый залоченный межпроцессный mutex и если он залочен, то его разлочить или дождаться пока он разлочится и там уже падать?
V>Обработчик сигнала SIGSEGV есть, но он не "лезит" во внутренности message_queue. Даже если будет применено такое решение, то гарантии целостности очереди нет.
Так а почему не лезет. Я вот и спрашиваю про обоснование. Я знаю, что есть ограниченный набор функций libc которые можно вызывать в обработчике сигнала, другие не стоит вызывать. А у вас какая причина это не делать? Может пусть зайдет и разлочит? Или дождется пока другой поток его разлочит. Про гарантии целостности очереди я не понял. Что мешает достичь этих гарантий если ты напишешь такой обработчик сигнала?
V>А если будет получен сигнал SIGKILL то обработчик и вовсе не сработает.
Как раз это самый простой случай как мне кажется. SIGKILL посылается командой kill -9? Тогда пусть человек (или скрипт) также делают ipcrm. Ведь kill -9 они сами делали. Или запускают твое приложение с каким-нибудь дополнительным ключом, чтобы оно само удалило этот IPC мьютекс.
Re[3]: boost::interprocess::message_queue сбой в работе очер
Здравствуйте, zaufi, Вы писали:
Z>Здравствуйте, remark, Вы писали:
Z>нет там внутри этой эмуляции очереди обыкновенный pthread_mutex_t созданный в шареной памяти с аттрибутом PTHREAD_PROCESS_SHARED
Z>т.е. при смерти одного процесса еси не почистить эту саму память (что конечно же грязный хак) он останется там залоченым
Z>фигня в том что из прикладного кода добраться до этого mutex'a топикстартеру буит аццки не просто (если вообще возможно... ну конечно всегда можно похакать буст %) ... так что второй совет в этом thread'e (повеситься на SIGSEGV и делать unlock) также реализуем с невероятным трудом
Z>теперь об эмуляции: заглянув в исходнеки был разочарован что message_queue этот на сам деле ни какой не POSIX message queue (see mans: mq_open, mq_close, mq_send, mq_receive), а тупо шареная память и интерпроцессный mutex...
Boost видимо из-за своей кроссплатформенности не может использовать нативно POSIX message queue. Под windows этот вариант не прокатит.
Z>топикстартеру рекомендую ознакомиться с `man 7 mq_overview`. от себя добавлю что в POSIX message queue ни кааких локов не требуется вв юзерском коде пользоваться легко и удобно (с помощью простецких С++ных врапперов конечно же %) которые к слову говоря сильно проще чем в бусте накрученная эмуляция)... ну и кроме всего прочего нативные message queue умеют уведомлять процесс (каким скажешь сигналом) о пришедшем сообщении (не нада ничо полоть)
У нас проект в проекте не рекомендуется использовать чистый POSIX. Поэтому используется Boost.
Re[4]: boost::interprocess::message_queue сбой в работе очер
Здравствуйте, varga, Вы писали:
Z>>топикстартеру рекомендую ознакомиться с `man 7 mq_overview`. от себя добавлю что в POSIX message queue ни кааких локов не требуется вв юзерском коде пользоваться легко и удобно (с помощью простецких С++ных врапперов конечно же %) которые к слову говоря сильно проще чем в бусте накрученная эмуляция)... ну и кроме всего прочего нативные message queue умеют уведомлять процесс (каким скажешь сигналом) о пришедшем сообщении (не нада ничо полоть)
V>У нас проект в проекте не рекомендуется использовать чистый POSIX. Поэтому используется Boost.
А если Boost не содержит необходимых компонент?
Если нужна именно robust очередь (что б переживала крэши и насильные пребивания процессов; а зачем тогда вообще IPC, если этого нет?..), то варианта 2. Первый — тормозной, но простой — использовать mq, пайпы, сокеты и т.д. Второй — быстрый, но сложный — писать с нуля robust очередь.
Здравствуйте, blackwater, Вы писали:
B>Так а почему не лезет. Я вот и спрашиваю про обоснование. Я знаю, что есть ограниченный набор функций libc которые можно вызывать в обработчике сигнала, другие не стоит вызывать. А у вас какая причина это не делать? Может пусть зайдет и разлочит? Или дождется пока другой поток его разлочит. Про гарантии целостности очереди я не понял. Что мешает достичь этих гарантий если ты напишешь такой обработчик сигнала?
Мешает это сделать то, что это сделать нельзя. Данные после обычного С/C++ кода восстановить нельзя. Что бы это можно было сделать, очередь должна работать по какому-то специальному алгоритму, который предусматривает восстановление, и очень аккуратно реализована с помощью упорядоченных атомарных операций. Только в этом случае её можно будет восстановить.
V>>А если будет получен сигнал SIGKILL то обработчик и вовсе не сработает. B>Как раз это самый простой случай как мне кажется. SIGKILL посылается командой kill -9? Тогда пусть человек (или скрипт) также делают ipcrm. Ведь kill -9 они сами делали. Или запускают твое приложение с каким-нибудь дополнительным ключом, чтобы оно само удалило этот IPC мьютекс.
Разлочивание мьютекса — это меньшая (практически нулевая) часть проблемы. robust POSIX мьютексы и Win32 мьютексы вообще сами разлочиваются. И дальше начинается самое интересное.