У меня возникают проблемы с тем, как правильно обрабатывать создание дочернего процесса из многопоточной программы, которая использует Boost Asio многопоточным способом.
Если я правильно понимаю, способ запуска дочернего процесса в мире Unix - вызвать fork(), а затем exec*(). Кроме того, если я правильно понимаю, вызов fork() будет дублировать все дескрипторы файлов и т.д., И они должны быть закрыты в дочернем процессе, если они не помечены как FD_CLOEXEC (и тем самым атомарно закрываются при вызове exec*()).
Boost Asio требует уведомления, когда fork() вызывается для правильной работы, вызывая notify_fork(). Однако в многопоточной программе это создает несколько проблем:
-
Сокеты по умолчанию унаследованы дочерними процессами, если я правильно понимаю. Они могут быть установлены в
SOCK_CLOEXEC, но не непосредственно при создании *, что приводит к временному окну, если дочерний процесс создается из другого потока. -
notify_fork()требует, чтобы ни один другой поток не вызывал какую-либо другую функциюio_service, ни любую функцию на любом другом объекте ввода-вывода, связанного сio_service. Это на самом деле не представляется возможным - ведь вся программа многопоточна по какой-то причине. -
Если я правильно понимаю, любой вызов функции между
fork()иexec*()должен быть безопасным для асинхронного сигнала (см.fork()documentation), Документация о том, что вызовnotify_fork()является безопасным для асинхронного сигнала, отсутствует. На самом деле, если я смотрю на исходный код Boost Asio (по крайней мере, в версии 1.54), могут быть вызовы pthread_mutex_lock, которые не являются безопасными для асинхронного сигнала если я правильно понимаю (см. Концепции сигналов, есть и другие вызовы, которые не входят в белый список).
Проблема №1 Возможно, я обойдусь, отделив создание дочерних процессов и сокетов + файлов, чтобы я мог гарантировать, что в окне между создаваемым сокетом и настройкой SOCK_CLOEXEC не создается дочерний процесс. Проблема №2 сложнее, мне, вероятно, нужно будет убедиться, что все потоки обработчика asio остановлены, разворачивают вилку, а затем снова воссоздают их, что в лучшем случае является отличным, и действительно очень плохо в худшем случае (как насчет моих ожидающих таймеров?). Проблема № 3, похоже, делает это совершенно невозможным для правильного использования.
Как правильно использовать Boost Asio в многопоточной программе вместе с fork() + exec*()?
... или я "раздвоен"?
Пожалуйста, дайте мне знать, если я неправильно понял какие-либо фундаментальные концепции (я поднимаюсь на программирование Windows, а не на * nix...).
Изменить:
* - На самом деле можно создать сокеты с SOCK_CLOEXEC, установленными непосредственно в Linux, доступными с версии 2.6.27 (см. socket() documentation). В Windows соответствующий флаг WSA_FLAG_NO_HANDLE_INHERIT доступен с Windows 7 SP 1/Windows Server 2008 R2 SP 1 (см. WSASocket() документация). OS X, похоже, не поддерживает это.