изо всех сил стараюсь ответить на все подвопросы; Я прошу прощения, что некоторые из них более неопределенные, чем в идеале должно быть:
Если есть
вероятность того, что код библиотеки имеет
зарегистрированные обработчики 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 не является асинхронной-сигнальной. безопасный; это указано как дефект в спецификации, см. http://www.opengroup.org/austin/aardvark/latest/xshbug3.txt (поиск "L16723").
ли
Ответ зависит от того, является ли
поток, в котором обработчик сигнала
бег может быть в середине
используя ресурс, который Atfork
обработчики нужны? Или сказал другой
Кстати, если обработчики Atfork используют
ресурсов синхронизации (мьютексы,
и т.д.) но вилка вызывается из
обработчик сигнала, который выполняется в
нить, которая никогда не получает доступ к этим
ресурсы, соответствует ли программа?
Строго говоря, ответ - нет, потому что согласно спецификации, функции либо безопасны по асинхронному сигналу, либо нет; нет понятия «безопасно при определенных обстоятельствах». На практике вам это может сойти с рук, но вы будете уязвимы для неуклюжей, но правильной реализации, которая не делит свои ресурсы так, как вы ожидаете.
Опираясь на этот вопрос, если
реализовано "потокобезопасное" разветвление
внутренне в системной библиотеке с помощью
идиомы, предложенные 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%, что это покрывает все возможные ситуации (не в последнюю очередь потому, что если / когда обработчик сигнала вернется, функция, у которой только что была украдена блокировка, попытается разблокировать ее, а внутренняя функция рекурсивной разблокировки не защищать от разблокировки слишком много раз!) - но кажется, что работоспособная схема может быть построена поверх рекурсивных блокировок, защищенных от асинхронных сигналов.
Наконец, даже если вилка безопасна в
обработчики сигналов, безопасно ли подключаться
обработчик сигнала, а затем вернуться изобработчик сигнала, или делает вызов
форк в обработчике сигнала всегда
необходимо последующий вызов _exit
или одна из функций семейства exec
до того, как обработчик сигнала вернется?
Я полагаю, вы говорите о дочернем процессе? (Если fork()
безопасность асинхронного сигнала означает что-либо, тогда должна быть возможность вернуться в родительский объект!)
Не найдя в спецификации ничего, что гласит иначе (хотя я, возможно, пропустил это), я считаю, что должно быть безопасным - по крайней мере, "безопасным" в том смысле, что возвращение из обработчика сигнала ребенок не подразумевает неопределенное поведение само по себе, хотя тот факт, что многопоточный процесс только что разветвился, может означать, что exec*()
или _exit()
, вероятно, самый безопасный способ действий.