attributes(7) представления безопасности POSIX

ОПИСАНИЕ

Замечание: Текст данной справочной страницы основан на материале раздела «POSIX Safety Concepts» руководства по библиотеки GNU C. Дополнительную информацию по темам, описанным здесь, можно найти в этом руководстве.

В справочных страницах по функциями есть раздел АТРИБУТЫ, в котором описано безопасен ли вызов функции в разных ситуациях. В этом разделе для функций описываются следующие отметки безопасности:

MT-Safe
Функции с MT-Safe или Thread-Safe безопасно вызывать при наличии других нитей. MT в MT-Safe означает Multi Thread.

Отметка MT-Safe не подразумевает атомарность работы функции или то, что в ней используется какой-либо механизм синхронизации POSIX, предоставляемый пользователям. Возможно, что последовательный вызов нескольких MT-Safe функций не приведёт к безопасной комбинации MT-Safe. Например, если нить вызывает две MT-Safe функции друг за другом, то это не гарантирует поведения, эквивалентного атомарному выполнению комбинации обеих функций, так как одновременные вызовы в других нитях могут всё испортить.

Из-за оптимизации программы в целом, могут быть добавлены встраиваемые (inline) функции из библиотечного интерфейса, что может привести к небезопасному переупорядочиванию, и поэтому выполнение встраивания через интерфейс GNU C Library не рекомендуется. Задокументированное состояние MT-Safety не гарантируется при оптимизации программы в целом. Однако функции, определённые в видимых пользователю заголовках, разрабатывались как безопасные для встраивания.

MT-Unsafe Функции, помеченные MT-Unsafe, небезопасно использовать в многонитевых программах.

Другие ключевые слова, встречающиеся в комментариях по безопасности, описаны в следующих разделах.

Условно безопасные свойства

Некоторые свойства функции делают их вызовы небезопасными в определённых ситуациях, но существуют пути обхода помимо отказа от вызова функций вообще. Ключевые слова, описывающие подобные свойства, и каждое их определение показывает как требуется ограничить программу в целом, чтобы решить проблему с безопасностью, описываемую ключевым словом. Только, когда все причины, которые делают функцию небезопасной, найдены и сняты, применив задокументированные ограничения, функция становится безопасной для вызова в описанном контексте.
init
MT-Unsafe функции, помеченные init, выполняют MT-Unsafe инициализацию при первом их вызове.

Вызов такой функции не менее одного раза в однонитевом режиме удаляет эту причину для функции, которая считается MT-Unsafe. Если других причин нет, то функция может безопасно вызываться после запуска других нитей.

race
MT-Safety функции, помеченные race, работают с объектами способами, которые могут привести к состязательности за данные или к подобному разрушающему параллельному выполнению. Это могут быть объекты, передаваемые в функции пользователями, возвращаемые функциями пользователю как значения или вообще не предоставляемые пользователям.

const
MT-Safety функции, помеченные const, не атомарно изменяют внутренние объекты, которые считаются константами, потому что существенная часть библиотеки GNU C обращается к ним без синхронизации. В отличие от MT-Unsafe функций с race, которые и читают, и изменяют внутренние объекты, const помечаются функции, производящие только запись. Вызов писателя остаётся MT-Unsafe, но тогда обязательная неизменяемость объектов, которые он изменил, позволяет читателям считаться MT-Safe (на время, пока не возникло других причин считаться небезопасными), так как отсутствие синхронизации не является проблемой, когда объекты в действительности константы.

Идентификатор после const является комментарием по безопасности в читателях. В программах, в которых нужно обойти эту проблему безопасного вызова писателей, могут использовать нерекурсивную блокировку чтения-записи, связанную с идентификатором, и защитить все вызовы функций, помеченных const с идентификатором блокировкой на запись, а все вызовы функций, помеченных самим идентификатором, блокировкой на чтение.

sig
MT-Safety функции, помеченные sig, могут временно устанавливать обработчик сигнала для внутренних нужд, что может влиять на другие использования сигнала (указан после двоеточия).

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

term
MT-Safety функции, помеченные term, могут изменять настройки терминала рекомендуемым способом, а именно: вызывать tcgetattr(3), изменять некоторые флаги и затем вызывать tcsetattr(3), это создаёт интервал, в котором изменения, сделанные в других нитях, теряются. То есть функции, помеченные term, считаются MT-Unsafe.

Таким образом, в приложениях, использующих терминал, желательно избегать параллельных и реентерабельных взаимодействий с ним, то есть не использовать его в сигнальных обработчиках или сигналах блокирования, которые могли бы использовать его, и удерживать блокировку при вызове этих функций и взаимодействующих с терминалом. Эта блокировка также должна использоваться для взаимного исключения функций, помеченных race:tcattr(fd), где fd — файловый дескриптор управляющего терминала. Для простоты вызывающий может использовать одиночный мьютекс или один мьютекс на каждый терминал, даже если на него ссылаются разные файловые дескрипторы.

Другие отметки о безопасности

Функции могут помечаться другими ключевыми словами, показывающими возможности, которые не делают функции небезопасными для вызова, но может потребоваться учитывать это в определённых классах программ:
locale
MT-Safety функции, помеченные locale, выполняют чтение из объекта локали без установки синхронизации. Вызов функций, помеченных locale, одновременно с изменением локали может использовать ещё не полностью готовые данные локали, что приводит к непредсказуемому поведению.

Мы не помечаем эти функции как MT-Unsafe, однако, так как функции, которые изменяют объект локали, помечаются как const:locale и следовательно небезопасны. Считаясь небезопасными, последние не должны вызываться при выполнении нескольких нитей или включённых асинхронных сигналах, и поэтому локаль может считаться постоянной в этих контекстах, что делает первые функции безопасными.

env
MT-Safety функции, помеченные env, используют окружение через getenv(3) и подобные функции без какой-либо защиты от одновременного изменения.

Мы не помечаем эти функции как MT-Unsafe, однако, так как функции, которые изменяют окружение, помечаются как const:env и следовательно небезопасны. Считаясь небезопасными, последние не должны вызываться при выполнении нескольких нитей или включённых асинхронных сигналах, и поэтому окружение может считаться постоянным в этих контекстах, что делает первые функции безопасными.

hostid
MT-Safety функции, помеченные hostid, читают из системных структур данных, которые содержат «ID узла» машины. Эти структуры данных, обычно, нельзя изменить атомарно. Так как ожидается, что значение «ID узла», обычно не меняется, функция, читающая его (gethostid(3)), считается безопасной, но функция, изменяющая его (sethostid(3)), помечается const:hostid, что указывает на возможность специальной обращения при вызове. Этот конкретный случай специального обращения требует координации в масштабе всей системы (а не просто внутри процесса).
sigintr
MT-Safety функции, помеченные sigintr, обращаются к внутренней структуре данных _sigintr библиотеки GNU C без какой-либо защиты от одновременного изменения.

Мы не помечаем эти функции как MT-Unsafe, однако, так как функции, которые изменяют эту структуру данных, помечаются как const:sigintr и следовательно небезопасны. Считаясь небезопасными, последние не должны вызываться при выполнении нескольких нитей или включённых асинхронных сигналах, и поэтому структура данных может считаться постоянной в этих контекстах, что делает первые функции безопасными.

cwd
MT-Safety функции, помеченные cwd, могут временно изменять текущий рабочий каталог на время выполнения, что может привести к непредсказуемому значению при определении относительных путей в других нитях, внутри асинхронного сигнала или обработчиков отмены (cancellation handlers).

Этого недостаточно для пометки таких функций как MT-Unsafe, но когда такое поведение необязательно (например, nftw(3) с FTW_CHDIR), используйте полные пути или системные вызовы с файловыми дескрипторами (например, openat(2)).

:идентификатор
Иногда маркировки могут указываться после идентификаторов, они предназначены для некоторых групп функций, которые, например, обращаются к структурам данных небезопасным способом, как у race и const, или для предоставления более узкой информации, например имени сигнала, если функция помечена sig. В будущем предполагается применять это также к lock и corrupt.

В большинстве случаев идентификатор именуется по набору функций, но это может быть и имена глобальных объектов, аргументов функций, отличительные свойства или логические компоненты, связанные с ними, что выглядит как, например :buf(arg) — буфер, связанный с аргументом arg, или :tcattr(fd) — атрибуты терминала файлового дескриптора fd.

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

/условие
Некоторые описания безопасности могут быть условными, то есть они применяются только если логическое выражение, состоящее из вовлечённых аргументов, глобальных переменных или даже используемого ядра считается истинным. Например, /!ps и /one_per_line указывают, что впереди стоящий маркер применим только когда аргумент ps равен NULL или глобальная переменная one_per_line не равна нулю.

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