Ответ 1
Попытка изо всех сил ответить на все вопросы; Прошу прощения, что некоторые из них более смутные, чем это в идеале должно быть:
Если есть возможность использования кода библиотеки Зарегистрированные обработчики pthread_atfork которые не являются безопасными для асинхронного сигнала, это отрицает безопасность вилки?
Да. В документации fork указано следующее:
When the application calls fork() from a signal handler and any of the
fork handlers registered by pthread_atfork() calls a function that is
not asynch-signal-safe, the behavior is undefined.
Конечно, это означает, что вы не можете использовать pthread_atfork()
для своей цели, чтобы сделать многопотоковые библиотеки прозрачными для процессов, которые считают, что они однопоточные, потому что ни одна из функций синхронизации pthread не является async-signal- безопасно; это отмечено как дефект в спецификации, см. http://www.opengroup.org/austin/aardvark/latest/xshbug3.txt (поиск по "L16723" ).
Есть ли ответ зависит от того, поток, в котором обработчик сигнала бег может быть посредине используя ресурс, который нужны обработчики? Или сказал другой путь, если обработчики atfork используют ресурсов синхронизации (мьютексы, и т.д.), но fork вызывается из обработчик сигнала, который выполнен в нить, которая никогда не обращается к этим ресурсов, соответствует ли программа?
Строго говоря, ответ отрицательный, потому что согласно спецификации, функции являются либо безопасными для асинхронных сигналов, либо они не являются; нет понятия "безопасно при определенных обстоятельствах". На практике вы вполне можете уйти от него, но вы были бы уязвимы для неуклюжей, но правильной реализации, которая не разбивала свои ресурсы так, как вы ожидали.
Основываясь на этом вопросе, если "Нитебезопасное" форсирование реализовано внутренне в системной библиотеке, используя идиомы, предложенные pthread_atfork (получить все блокировки в преддверии обработчика и освободить все блокировки в обоих родительский и дочерний пост хэндлеров), то это вилка, когда-либо безопасная для использование от обработчиков сигналов в резьбовом программа? Разве не возможно, чтобы обработка потока может быть в в середине вызова malloc или fopen/fclose и проведение глобального блокировка, приводящая к вилка?
Если бы это было реализовано таким образом, тогда вы правы, fork()
из обработчика сигнала никогда не будет в безопасности, потому что попытка получить блокировку может быть заблокирована, если вызывающий поток уже удерживал ее. Но это означает, что реализация с использованием такого метода не будет соответствовать.
Глядя на glibc как на один пример, он этого не делает - скорее, он принимает два подхода: во-первых, блокировки, которые он получает, являются рекурсивными (поэтому, если текущий поток уже имеет их, их количество блокировки будет просто вырос); кроме того, в дочернем процессе он просто в одностороннем порядке перезаписывает все блокировки - см. этот фрагмент из nptl/sysdeps/unix/sysv/linux/fork.c
:
/* Reset the file list. These are recursive mutexes. */
fresetlockfiles ();
/* Reset locks in the I/O code. */
_IO_list_resetlock ();
/* Reset the lock the dynamic loader uses to protect its data. */
__rtld_lock_initialize (GL(dl_load_lock));
где каждая из функций resetlock
и lock_initialize
в конечном счете вызывает внутренний эквивалент glibc pthread_mutex_init()
, эффективно перезагружая мьютекс, независимо от каких-либо официантов.
Я думаю, что теория состоит в том, что, получив (рекурсивную) блокировку, она гарантировала, что никакие другие потоки не будут касаться структур данных (по крайней мере, таким образом, чтобы вызвать крушение), а затем сброс отдельных блокировок обеспечивает ресурсы не блокируются навсегда. (Сброс текущей блокировки потока является безопасным, так как теперь нет других потоков, чтобы бороться за структуру данных, и действительно не будет, пока не вернется какая-либо функция, использующая блокировку).
Я не уверен на 100%, что это охватывает все возможные события (не в последнюю очередь потому, что если/когда обработчик сигнала возвращается, функция, которая только что украла его замок, попытается разблокировать его, а функция внутренней рекурсивной разблокировки не будет защищайте от разблокировки слишком много раз!) - но кажется, что эффективная схема может быть построена поверх рекурсивных блокировок, устойчивых к асинхронному сигналу.
Наконец, даже если fork безопасен в обработчиков сигналов, безопасно ли вилка в обработчик сигнала, а затем вернуться из обработчик сигнала или вызов вилка в обработчике сигнала всегда требуется следующий вызов _exit или одно из семейств функций exec перед возвратом обработчика сигнала?
Я предполагаю, что вы говорите о дочернем процессе? (Если fork()
, являющийся безопасным для асинхронного сигнала, означает что-нибудь, тогда его можно будет вернуть в родительском!)
Не обнаружив ничего в спецификации, которая заявляет иначе (хотя я, возможно, пропустил ее), я считаю, что это должно быть безопасно - по крайней мере, "безопасно" в том смысле, что возврат из обработчика сигнала в ребенке не означает undefined сам по себе, хотя тот факт, что многопоточный процесс только разветвлен, может означать, что exec*()
или _exit()
, вероятно, самый безопасный способ действий.