Обработка сигналов Boost.asio и UNIX - PullRequest
15 голосов
/ 21 сентября 2011

Предисловие

У меня есть многопоточное приложение, работающее через Boost.Asio.Существует только один boost::asio::io_service для всего приложения, и все вещи внутри него выполняются группой потоков.Иногда необходимо порождать дочерние процессы, используя fork и exec.Когда ребенок заканчивается, мне нужно сделать waitpid, чтобы проверить код выхода и собрать зомби.Я использовал недавно добавленный boost::asio::signal_set, но столкнулся с проблемой в старых системах с ядрами linux-2.4. * (Которые, к сожалению, до сих пор используются некоторыми клиентами).В старых ядрах Linux потоки на самом деле являются особым случаем процессов, и поэтому, если дочерний процесс был порожден одним потоком, другой поток не сможет ожидать его, используя семейство системных вызовов waitpid.Asio signal_set отправляет обработчик сигнала на io_service, и любой поток, выполняющий эту службу, может запустить этот обработчик, что не подходит для моего случая.Поэтому я решил обработать сигналы старым добрым способом «сигнал / сигнал» - все потоки имеют один и тот же обработчик, который вызывает waitpid.Итак, есть еще одна проблема:

Проблема

Когда обработчик получает сигнал и процесс успешно ожидает, как я могу "отправить" это на мой io_service из обработчика?Как мне кажется, очевидный метод io_service::post() невозможен, потому что он может заблокировать io_service внутренние мьютексы, если сигнал поступает в неправильное время.Единственное, что мне пришло в голову, - это использовать какую-нибудь трубу или пару сокетов для записи туда уведомлений и async_wait на другом конце, как это иногда делается для обработки сигналов в poll() циклах событий.

Есть ли какие-либолучшие решения?

Ответы [ 2 ]

2 голосов
/ 02 октября 2011

Я не имел дело с boost :: asio, но я решил похожую проблему. Я считаю, что мое решение работает как для LinuxThreads, так и для новых потоков NPTL.

Я предполагаю, что причина, по которой вы хотите "публиковать" сигналы на вашем * io_service *, заключается в том, чтобы прервать системный вызов, чтобы поток / программа завершился корректно. Это правильно? Если нет, может быть, вы можете лучше описать свою конечную цель.

Я перепробовал множество различных решений, в том числе некоторые, которые требовали определения, какой тип потоков использовался. В конце концов мне помогло решить эту проблему: секция Прерывание системных вызовов и библиотечных функций обработчиками сигналов сигнала человека (7).

Ключ должен использовать sigaction () в потоке обработки сигналов без SA_RESTART , чтобы создать обработчики для всех сигналов, которые вы хотите перехватить, снимите маску с этих pthread_sigmask (SIG_UNBLOCK, sig_set, 0) в потоке обработки сигналов и маскировать тот же набор сигналов во всех других потоках. Обработчик не должен ничего делать. Просто наличие обработчика меняет поведение, а не установка SA_RESTART позволяет прерывать системные вызовы (например, write ()). Принимая во внимание, что если вы используете sigwait () , системные вызовы в других потоках не прерываются.

Чтобы легко маскировать сигналы во всех других потоках. Я запускаю поток обработки сигналов. Затем замаскируйте все сигналы, которые хотите обработать в главном потоке, прежде чем запускать любые другие потоки. Затем при запуске других потоков они копируют маску сигналов основного потока.

Смысл в том, что если вы сделаете это, вам может не потребоваться отправлять сигналы на ваш * io_service *, потому что вы можете просто проверить системные вызовы на наличие кодов возврата прерываний. Я не знаю, как это работает с boost :: asio.

Таким образом, конечным результатом всего этого является то, что я могу поймать нужные мне сигналы, такие как SIGINT, SIGTERM, SIGHUO и SIGQUIT, чтобы выполнить чистое завершение работы, но мои другие потоки по-прежнему прерывают свои системные вызовы и также могут выйти чисто с помощью исключить любую связь между сигнальным потоком и остальной частью системы, не делая ничего опасного в обработчике сигналов, а одна реализация работает как в LinuxThreads, так и в NPTL.

Возможно, это был не тот ответ, который вы искали, но я надеюсь, что это поможет.

ПРИМЕЧАНИЕ. Если вы хотите выяснить, работает ли в системе LinuxThreads, вы можете сделать это, создав поток и сравнив его PID с PID основного потока. Если они отличаются, это LinuxThreads. Затем вы можете выбрать лучшее решение для типа нити.

0 голосов
/ 02 октября 2011

Если вы уже опрашиваете свой IO, другое возможное решение, которое очень просто, - просто использовать логическое значение для сигнализации других потоков. Логическое значение всегда либо ноль, либо нет, поэтому нет возможности частичного обновления и состояния гонки. Затем вы можете просто установить этот логический флаг без мьютексов, которые читают другие потоки. Инструменты типа valgrind не нравятся, но на практике это работает.

Если вы хотите быть еще более корректным, вы можете использовать gcc's atomics , но это зависит от компилятора.

...