Идентификаторы потоков, которые здесь обсуждаются, не являются тождественными тем идентификаторам, которые используются в POSIX-потоках (и имеют тип pthread_t). Последние генерируются и обслуживаются внутренним POSIX-механизмом, реализованным в пользовательском пространстве.
Все потоки в группе имеют один и тот же идентификатор родительского процесса — тот, что принадлежит лидеру группы. Родительский процесс получает сигнал SIGCHLD (или сигнал завершения) то после того, как завершатся все потоки в группе. Такое поведение соответствует требованиям, предъявляемым к POSIX-потокам.
Когда поток с флагом CLONE_THREAD завершает работу, поток, который создал его с помощью вызова clone(), не получает никакого сигнала. Соответственно, вызов wait() (и аналогичные ему) не подходит для ожидания потоков, созданных таким образом. Это отвечает требованиям стандарта POSIX. POSIX-поток не тождественен процессу; его нельзя отследить с помощью wait(). Вместо этого его следует присоединить, применив вызов pthread_join(). Для определения того, что поток, созданный с использованием флага CLONE_THREAD, завершил работу, применяется специальное средство синхронизации под названием
Если какой-либо поток выполнит exec(), все потоки в его группе, кроме лидера, завершатся (это поведение соответствует требованием стандарта POSIX), а новая программа будет выполнена в потоке-лидере. Иными словами, вызов gettid() в новой программе вернет идентификатор потока, являющегося лидером группы. Во время выполнения вызова exec() сигнал о завершении работы, который этот процесс должен послать своему родителю, сбрасывается к SIGCHLD.
Потомок, созданный в потоке с помощью вызовов fork() или vfork(), может быть отслежен любым участником группы этого потока с использованием вызова wait() или аналогичного.
Начиная с Linux 2.6, при использовании CLONE_THREAD в аргументе flags необходимо также указывать флаг CLONE_SIGHAND. Это отвечает требованиям стандарта POSIX; больше подробностей можно найти в описании взаимодействия POSIX-потоков и сигналов в разделе 33.2 (способ обработки ядром сигналов для групп потоков, созданных с помощью флага CLONE_THREAD, отражает требования стандарта POSIX, касающиеся того, как потоки внутри процесса должны реагировать на сигналы).
Поддержка библиотек для работы с потоками: CLONE_PARENT_SETTID, CLONE_CHILD_SETTID и CLONE_CHILD_CLEARTID
Флаги CLONE_PARENT_SETTID, CLONE_CHILD_SETTID и CLONE_CHILD_CLEARTID были добавлены в Linux 2.6 для поддержки реализации POSIX-потоков. Они влияют на то, как вызов clone() обращается со своими аргументами ptid и ctid. Флаги CLONE_PARENT_SETTID и CLONE_CHILD_CLEARTID также используются в реализации NPTL-потоков.
Если указать флаг CLONE_PARENT_SETTID, то ядро запишет идентификатор дочернего потока в место, на которое указывает ptid. Этот идентификатор копируется в ptid перед дублированием памяти родителя. Это означает, что даже при отсутствии флага CLONE_VM значение с этим местоположением будет доступно как родителю, так и потомку (как уже отмечалось выше, флаг CLONE_VM указывается при создании POSIX-потоков).
Флаг CLONE_PARENT_SETTID нужен для того, чтобы поточная библиотека имела надежный способ получения идентификатора нового потока. Стоит отметить, что использование для этого значения, возвращаемого вызовом clone() (как показано ниже) не является достаточным:
tid = clone(…);
Дело в том, что данный код может привести к различным видам состояния гонки, поскольку присваивание выполняется только после возвращения вызова clone(). Представим, к примеру, что новый поток завершается и что его обработчик сигнала завершения вызывается до того, как переменной tid присвоится соответствующее значение. В этом случае обработчик не может получить доступ к tid эффективным способом (в рамках поточной библиотеки tid может быть всего лишь элементом глобальной структуры, с помощью которой отслеживается статус всех потоков). Часто программы, вызывающие clone() напрямую, способны обойти это состояние гонки. Однако поточная библиотека не может контролировать действия вызывающей ее программы. Этой проблемы можно избежать, если до возврата вызова clone() гарантировать, что идентификатор нового потока сохранен по адресу, на который указывает ptid; и именно этого позволяет добиться флаг CLONE_PARENT_SETTID.
Если указать флаг CLONE_CHILD_SETTID, вызов clone() запишет идентификатор дочернего потока в место, на которое указывает ctid. Это происходит только в памяти потомка, но родитель тоже может быть затронут, если дополнительно указать флаг CLONE_VM. И хотя CLONE_CHILD_SETTID не предусмотрен в библиотеке NPTL, с его помощью можно повысить гибкость альтернативных реализаций.
Если указать флаг CLONE_CHILD_CLEARTID, во время завершения потомка вызов clone() обнулит участок памяти, на который указывает ctid.