Если 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.

33.6. Продвинутые возможности программного интерфейса Pthreads

Среди продвинутых возможностей программного интерфейса Pthreads можно выделить следующие.

• Планирование в режиме реального времени. Мы можем устанавливать политику и приоритеты планирования в режиме реального времени. Это аналогично системным вызовам для планирования процессов, описанным в разделе 35.3.

• Разделяемые мьютексы и условные переменные процесса. Стандарт SUSv3 предусматривает параметр, который позволяет сделать мьютексы и условные переменные общими для нескольких процессов (а не просто для разных потоков одного процесса). В этом случае условная переменная или мьютекс должны быть выделены на участке памяти, разделяемом процессами. Данная возможность поддерживается библиотекой NPTL.

• Дополнительные средства синхронизации потоков. Речь идет о барьерах, блокировках для чтения/записи и циклических блокировках.

Подробные сведения обо всех этих возможностях можно найти в книге [Butenhof, 1996].

33.7. Резюме

Потоки плохо сочетаются с асинхронными сигналами; при проектировании многопоточных приложений использования сигналов следует избегать любыми путями. Но если это сделать не удается, наиболее безопасным подходом является блокирование сигналов во всех потоках и выделение отдельного потока специально для приема сигналов с помощью вызова 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.8. Упражнения

33.1. Напишите программу для демонстрации того, что разные потоки в одном и том же процессе могут иметь разные наборы ожидающих сигналов, которые возвращаются вызовом sigpending(). Вы можете воспользоваться функцией pthread_kill(), чтобы отправить разные сигналы двум разным потокам; эти потоки их заблокируют, и затем каждый из них вызовет sigpending() и выведет информацию об ожидающих сигналах (вам могут пригодиться функции из листинга 20.4).

33.2. Представьте, что поток создает потомка с помощью вызова fork(). Гарантируется ли доставка итогового сигнала SIGCHLD потоку, вызвавшему fork() (об остальных потоках внутри процесса речь не идет), если потомок завершится?

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

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