Является ли вилка (предположительно) безопасной от обработчиков сигналов в многопоточной программе?

Я действительно не уверен в требованиях POSIX к безопасности fork в присутствии streamов и сигналов. fork указана в качестве одной из функций безопасности, связанных с асинхронным сигналом, но если есть вероятность, что в библиотечном коде зарегистрировались обработчики pthread_atfork которые не являются безопасными для асинхронных сигналов, это лишает безопасность fork ? Ответ зависит от того, может ли stream, в котором работает обработчик сигнала, находиться в середине использования ресурса, который требуется обработчикам atfork? Или иначе, если обработчики atfork используют ресурсы синхронизации (мьютексы и т. Д.), Но fork вызывается из обработчика сигнала, который выполняется в streamе, который никогда не обращается к этим ресурсам, соответствует ли программа?

Основываясь на этом вопросе, если «streamобезопасное» forking реализовано внутри системной библиотеки с использованием идиом, предложенных pthread_atfork (получить все блокировки в обработчике предпродажа и освободить все блокировки как в родительских, так и дочерних обработчиках postfork), то это fork когда-либо безопасный для использования с обработчиками сигналов в поточной программе? Разве не возможно, что stream, обрабатывающий сигнал, может находиться посреди вызова malloc или fopen / fclose и удерживания глобальной блокировки, что приводит к тупиковой ситуации во время fork ?

Наконец, даже если fork безопасен в обработчиках сигналов, безопасно ли оно fork в обработчике сигнала, а затем возвращаться из обработчика сигнала или вызов в fork в обработчике сигналов всегда требует последующего вызова _exit или одного из exec семейство функций перед возвратом обработчика сигнала?

    Попытка изо всех сил ответить на все вопросы; Я прошу прощения, что некоторые из них более смутные, чем это в идеале должно быть:

    Если есть вероятность, что в библиотечном коде зарегистрировались обработчики pthread_atfork, которые не являются безопасными для асинхронного сигнала, это ли это отрицает безопасность вилки?

    Да. В документации на вилку указано следующее:

      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() для своей цели, чтобы сделать многоstreamовые библиотеки прозрачными для процессов, которые считают, что они однопоточные, потому что ни одна из функций синхронизации pthread не безопасна для асинхронных сигналов; это отмечается как дефект спецификации, см. http://www.opengroup.org/austin/aardvark/latest/xshbug3.txt (поиск по «L16723»).

    Ответ зависит от того, может ли stream, в котором работает обработчик сигнала, находиться в середине использования ресурса, который требуется обработчикам atfork? Или иначе, если обработчики atfork используют ресурсы синхронизации (мьютексы и т. Д.), Но fork вызывается из обработчика сигнала, который выполняется в streamе, который никогда не обращается к этим ресурсам, соответствует ли программа?

    Строго говоря, ответ отрицательный, потому что согласно спецификации, функции являются либо безопасными для асинхронного сигнала, либо они не являются; нет понятия «безопасно при определенных обстоятельствах». На практике вам вполне может это избежать, но вы были бы уязвимы для неуклюжей, но правильной реализации, которая не разбивала свои ресурсы так, как вы ожидали.

    Основываясь на этом вопросе, если «streamобезопасное» forking реализовано внутри системной библиотеки с использованием идиом, предложенных pthread_atfork (получить все блокировки в обработчике предпродажа и освободить все блокировки как в родительских, так и дочерних обработчиках postfork), то это fork когда-либо безопасный для использования с обработчиками сигналов в поточной программе? Разве не возможно, что stream, обрабатывающий сигнал, может находиться посреди вызова malloc или fopen / fclose и удерживания глобальной блокировки, что приводит к тупиковой ситуации во время fork?

    Если бы это было реализовано таким образом, тогда вы правы, fork() из обработчика сигнала никогда не будет в безопасности, потому что попытка получить блокировку может быть заблокирована, если вызывающий stream уже удерживал ее. Но это означает, что реализация с использованием такого метода не будет соответствовать.

    Глядя на glibc как на один пример, он этого не делает – скорее, он принимает два подхода: во-первых, блокировки, которые он получает, являются рекурсивными (поэтому, если текущий stream уже имеет их, их количество блокировок просто будет увеличено); Кроме того, в дочернем процессе он просто в одностороннем порядке перезаписывает все блокировки – см. этот nptl/sysdeps/unix/sysv/linux/fork.c из 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() , эффективно сбросив мьютекс, независимо от каких-либо официантов.

    Я думаю, что теория состоит в том, что, получив (рекурсивную) блокировку, гарантируется, что никакие другие streamи не будут касаться структур данных (по крайней мере, таким образом, чтобы вызвать крушение), а затем сброс отдельных блокировок гарантирует, t постоянно заблокирован. (Сброс блокировки текущего streamа является безопасным, так как теперь нет других streamов, которые могут конкурировать за структуру данных, и действительно не будет, пока не вернется функция, использующая блокировку).

    Я не уверен на 100%, что это охватывает все возможные события (не в последнюю очередь потому, что если / когда обработчик сигнала возвращается, функция, которая только что украла его замок, попытается разблокировать его, а функция внутренней рекурсивной разблокировки не защитит от разблокировки слишком много раз!) – но кажется, что эффективная схема может быть построена поверх рекурсивных блокировок, устойчивых к асинхронному сигналу.

    Наконец, даже если fork безопасен в обработчиках сигналов, безопасно ли оно вилка в обработчике сигнала, а затем возвращаться из обработчика сигнала или вызов в fork в обработчике сигналов всегда требует последующего вызова _exit или одного из exec семейство функций перед возвратом обработчика сигнала?

    Предполагаю, вы говорите о детском процессе? (Если fork() является безопасным для асинхронного сигнала, значит, что-нибудь, тогда его можно будет вернуть в родительском!)

    Не обнаружив ничего в спецификации, которая заявляет иначе (хотя я, возможно, пропустил ее), я считаю, что это должно быть безопасно – по крайней мере, «безопасно» в том смысле, что возrotation из обработчика сигнала в ребёнке не означает неопределенного поведения в и сам по себе, хотя тот факт, что многопоточный процесс просто разветвлен, может означать, что exec*() или _exit() , вероятно, является самым безопасным способом.

    Я добавляю этот ответ, потому что похоже, что fork() скорее всего не считается асинхронным. По крайней мере, это похоже на glibc но, возможно, поддержка больше не существует в POSIX. Ответ, который в настоящее время отмечен как «принятый», кажется, приходит к выводу, что он безопасен, но, по крайней мере, в glibc это, скорее всего, не так.

    Основываясь на этом вопросе, если «streamобезопасное» forking реализовано внутри системной библиотеки с использованием идиом, предложенных pthread_atfork (получить все блокировки в обработчике предпродажа и освободить все блокировки как в родительских, так и дочерних обработчиках postfork), то это fork когда-либо безопасный для использования с обработчиками сигналов в поточной программе? Разве не возможно, что stream, обрабатывающий сигнал, может находиться посреди вызова malloc или fopen / fclose и удерживания глобальной блокировки, что приводит к тупиковой ситуации во время fork?

    В самом деле! Похоже, что Open Group решила удалить его из списка именно по этой причине .

    Запрос на интерпретацию IEEE 1003.1c-1995 № 37 касается pthread_atfork .

    Комитет по интерпретации считает, что … должны быть сделаны следующие пояснительные дополнения: Pg 78 line 864 «Кроме того, для обработки асинхронных вызовов требуются вызовы обработчиков вил, установленных pthread_atfork от вилки, вызываемой из обработчика сигналов».

    glibc Bug 4737 определяет разрешение, которое fork() вызывается из списка функций асинхронных функций, а posix_spawn() используется для заполнения его места. К сожалению, это было разрешено как WONTFIX поэтому даже manpages не были обновлены.

    Использование fork () в обработчике сигнала должно быть прекрасным.

    pthread_atfork звучит как плохая идея когда-либо использовать.

    Чтобы ответить на ваш первоначальный вопрос, pthread не может гарантировать безопасность вызова какой-либо функции pthread_atfork, это безопасно для асинхронного сигнала, потому что реализация сигналов ядра делает невозможным.

    О, и если вы используете fork в обработчике сигнала, не позволяйте ребенку возвращаться из обработчика сигнала. Это не определено.

    Как я понимаю из источника fork в glibc, он использует критический раздел состояния сигнала, чтобы убедиться, что процедура наложения не прерывается сигналом.

      ss = _hurd_self_sigstate (); __spin_lock (&ss->critical_section_lock); 

    А так как обработчики pthread_atfork выполняются после блокировки критической секции – они становятся автоматически сигнальными.

    Может быть, я ошибаюсь в этом, я буду признателен за исправления.

    Здесь https://www.securecoding.cert.org/confluence/display/seccode/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers

    вилка указана как безопасная для Async-сигнала, поэтому ее можно использовать.

    POSIX

    Следующая таблица из базовых спецификаций Open Group [Open Group 2004] определяет набор функций, которые являются асинхронно-сигнальными. Приложения могут вызывать эти функции без ограничений из обработчика сигнала.

    Асинхронные сигнальные функции

    вилка ()