• Вызов getpid() возвращает уникальное значение для каждого потока в процессе. Эта функция показывает, что все потоки, кроме главного, создаются управляющим потоком процесса (то есть getppid() возвращает идентификатор управляющего потока). Если вызвать getppid() в других потоках, она вернет то же значение, которое можно было бы получить в главном потоке.

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

• Стандарт SUSv3 требует, чтобы при вызове потоком exec() все остальные потоки завершали свою работу. Но если вызов exec() — из любого потока, кроме главного, итоговый процесс будет иметь такой же идентификатор, как и вызывающий поток, — то есть идентификатор этого процесса будет отличаться от аналогичного идентификатора главного потока. Согласно стандарту SUSv3 они должны совпадать.

• Потоки не разделяют учетные данные (идентификаторы пользователя и группы). Когда многопоточный процесс выполняет программу, которая устанавливает идентификатор пользователя, может случиться так, что потоки утратят возможность обмениваться сигналами с помощью вызова pthread_kill(); причина может быть в том, что учетные данные потоков были изменены, в результате чего один поток больше не имеет привилегий для отправки сигнала другому потоку (см. рис. 20.2). Кроме того, библиотека LinuxThreads использует сигналы для внутренней работы, поэтому при изменении учетных данных потока различные операции программного интерфейса Pthreads могут завершиться неудачей или зависнуть.

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

• Сигнал, отправленный процессу с помощью вызовов kill() или sigqueue(), должен быть доставлен произвольному потоку в этом процессе, который обработает его неблокирующим способом. Однако в библиотеке LinuxThreads потоки имеют разные идентификаторы процессов, поэтому сигнал можно направить только какому-то определенному потоку. И даже если сигнал блокируется только одним этим потоком, он все равно остается в режиме ожидания.

• LinuxThreads не поддерживает сигналы, которые ожидают весь процесс целиком; ожидающие сигналы доступны только на уровне отдельных потоков.

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

• Атрибуты альтернативного стека сигналов (которые устанавливаются с помощью sigaltstack()) относятся к отдельным потокам. Но, поскольку новый поток ошибочно наследует эти параметры от потока, вызвавшего pthread_create(), альтернативный стек сигналов становится общим для них обоих. Стандарт SUSv3 требует, чтобы новый поток по умолчанию не имел альтернативного стека сигналов. Это несоответствие, допущенное библиотекой LinuxThreads, может привести к непредвиденным результатам (например, может произойти сбой в программе), если оба потока одновременно станут обрабатывать разные сигналы в общем альтернативном стеке. Часто эта проблема плохо поддается воспроизведению и отладке, так как ее появление зависит от редкого события, когда два сигнала обрабатываются в один и тот же момент.

В программе, которая использует LinuxThreads, новый поток может сделать вызов sigaltstack(), чтобы проверить, отличается ли его альтернативный стек сигналов от стека его создателя (как вариант, стек может и вовсе отсутствовать). Однако переносимые приложения (и библиотечные функции для создания потоков) не будут знать о таком подходе, поскольку в других системах это не является обязательным требованием. Но даже если мы воспользуемся данной методикой, это не избавит нас от возможности возникновения состояния гонки: новый поток может получить и обработать сигнал из альтернативного стека до того, как у него появится возможность вызвать sigaltstack().

• Потоки не разделяют общие идентификаторы сессии и группы процессов. Вызовы setsid() и setpgid() не могут использоваться для изменения сессии или принадлежности к группе процессов в многопоточном приложении.

• Блокировки записей, устанавливаемые вызовом fcntl(), не разделяются. Пересекающиеся запросы блокировки того же типа не объединяются.

• Потоки не разделяют ограничения на ресурсы. Стандарт SUSv3 гласит, что ограничения на ресурсы являются атрибутами уровня процесса.

• Процессорное время и сведения о потреблении ресурсов, возвращаемые вызовами times() и getrusage() соответственно, относятся к отдельным потокам. Итоговые значения, возвращаемые этими системными вызовами, должны относиться ко всему процессу целиком.

Перейти на страницу:

Похожие книги