Если Linux-система предоставляет как NPTL, так и LinuxThreads, иногда возникает необходимость в непосредственном выборе реализации многопоточности. Наиболее распространенным примером этого может служить ситуация, когда у нас есть старая программа, работа которой зависит от неких (вероятно, нестандартных) аспектов библиотеки LinuxThreads, поэтому мы хотим заставить ее работать именно с этой реализаций вместо NPTL.
Для этой задачи можно воспользоваться специальной переменной среды, которую учитывает динамический компоновщик: LD_ASSUME_KERNEL. Как видно по ее имени, она делает так, чтобы компоновщик выполнялся в режиме совместимости с определенным ядром Linux. Если указать версию ядра, которая не поддерживает NPTL (например, 2.2.5), это гарантированно приведет к задействованию LinuxThreads. Таким образом, мы можем запустить многопоточную программу на основе LinuxThreads с помощью следующей команды:
$ LD_ASSUME_KERNEL=2.2.5./prog
Совместив установку этой переменной среды с командой для вывода текущей реализации многопоточности, описанной выше, мы получим следующий результат:
$ export LD_ASSUME_KERNEL=2.2.5
$ $(ldd /bin/ls | grep libc.so | awk '{print $3}') | egrep — i 'threads|nptl'
linuxthreads-0.10 by Xavier Leroy
Диапазон версий ядра, которые можно указать переменной LD_ASSUME_KERNEL, имеет определенные ограничения. В некоторых популярных дистрибутивах, предоставляющих как NPTL, так и LinuxThreads, для выбора последней достаточно указать номер версии 2.2.5. Больше информации о применении этой переменной среды можно найти по адресу people.redhat.com/drepper/assumekernel.html.
Среди продвинутых возможностей программного интерфейса Pthreads можно выделить следующие.
•
•
•
Подробные сведения обо всех этих возможностях можно найти в книге [Butenhof, 1996].
Потоки плохо сочетаются с асинхронными сигналами; при проектировании многопоточных приложений использования сигналов следует избегать любыми путями. Но если это сделать не удается, наиболее безопасным подходом является блокирование сигналов во всех потоках и выделение отдельного потока специально для приема сигналов с помощью вызова sigwait() (или аналогичного). Этот поток впоследствии сможет безопасно выполнять такие действия как изменение разделяемых переменных (с применением мьютексов) и вызова функций, не рассчитанных на работу с асинхронными сигналами.
Наиболее распространенными реализациями многопоточности в Linux являются библиотеки LinuxThreads и NPTL. Первая была создана много лет назад, но из-за ряда расхождений с требованиями стандарта SUSv3 считается устаревшей. Современная реализация, NPTL, более строго следует стандарту SUSv3 и демонстрирует намного лучшую производительность; именно она предоставляется по умолчанию в современных дистрибутивах Linux.
Ознакомьтесь с источниками, приведенными в разделе 29.10.
Автор LinuxThreads разместил документацию к своей библиотеке на веб-странице pauillac.inria.fr/~xleroy/linuxthreads/. Реализация NPTL описана ее разработчиками в (уже несколько устаревшем) документе, электронная версия которого доступна по адресу people.redhat.com/drepper/nptl-design.pdf.
33.1. Напишите программу для демонстрации того, что разные потоки в одном и том же процессе могут иметь разные наборы ожидающих сигналов, которые возвращаются вызовом sigpending(). Вы можете воспользоваться функцией pthread_kill(), чтобы отправить разные сигналы двум разным потокам; эти потоки их заблокируют, и затем каждый из них вызовет sigpending() и выведет информацию об ожидающих сигналах (вам могут пригодиться функции из листинга 20.4).
33.2. Представьте, что поток создает потомка с помощью вызова fork(). Гарантируется ли доставка итогового сигнала SIGCHLD потоку, вызвавшему fork() (об остальных потоках внутри процесса речь не идет), если потомок завершится?