18
Русскоязычную терминологию, пусть и не самую благозвучную, мы здесь заимствуем из [12].
В литературе неоднократно отмечалось, что ошибки многопоточных программ часто не детерминированы (могут возникать или нет в идентичных условиях исполнения), трудно воспроизводимы и могут быть крайне трудны для локализации.
Пользователь может изменять это поле, однако это лишено смысла и не влечет за собой никаких последствий, ведь значением текущего приоритета «управляет» ОС, например для осуществления наследования приоритетов. С другой стороны, иногда целесообразно считать значение именно этого поля, чтобы определить значение динамического приоритета потока, установленного, например, в результате того же наследования.
В документации неоднократно упоминается еще одна дисциплина — «адаптивная» (SCHED_ADAPTIVE), и даже детально описывается, «как она работает». Видимо, это можно отнести только к тому, что корректировка обширной документации отстает от развития самой системы. На конференции «QNX-Россия 2003» на вопрос по поводу ADAPTIVE-диспетчеризации представители QSSL отвечали так: «Этот вид диспетчеризации был в QNX 4.xx, а в QNX 6.x вместо него введена более продвинутая техника SPORADIC-диспетчеризации». Тем не менее более продвинутая спорадическая диспетчеризация не позволяет абсолютно точно выразить логику адаптивной.
Этот тест и его результаты для Linux подсказаны одним из участников (имя нам неизвестно) обсуждений на http://qnxclub.net.forum.
Согласно стандарту POSIX установки состояния и типа завершаемости могут быть сделаны только из уже выполняющегося кода потока (при старте потока эти параметры установлены в значения по умолчанию). QNX делает расширение, позволяя установить соответствующие флаги в атрибутной записи еще до создания потока. Подробнее об этом говорилось при обсуждении создания потока.
Разница выражается в том, что макрос pthread_cleanup_push() расширяется в фрагмент кода, открывающийся скобкой «{» без соответствующей скобки «}», аналогично pthread_cleanup_pop() закрывается «}», не имея открывающей скобки. Эти вызовы могут располагаться только парами, в противном случае возникнет лексическая ошибка, обнаруживаемая компилятором.
Мы умышленно делаем акцент на этом месте, так как существует «красивая народная легенда» (и это утверждение встречается порой и в литературе), что потоки на периоде выполнения намного эффективнее (в смысле переключения контекста), чем процессы.
Здесь применена только простейшая форма «активного объекта»: гораздо сложнее, к примеру, определить деструктор такого объекта. Но именно в своем простейшем виде это многообещающая техника активных объектов.
Именно поэтому, наверное, стандарту POSIX так сложно органично согласовать их с нововведениями, например с парадигмой потоков.
Наличие круглых скобок после имени сигнала — признак того, что для этих сигналов реакция по умолчанию — принудительное завершение процесса; наличие (+) означает, что для этих сигналов предусмотрено создание дампа завершения, а наличие (-) — для этих сигналов дамп не создается. В квадратных скобках после имени сигнала указано численное значение, соответствующее данному сигналу, как оно определено в QNX.
Все это и делает механизм обработки более надежным по сравнению с более ранним механизмом, который описывался выше.
Спецификация XSI требует, чтобы процесс использовал либо поле sa_handler, либо поле sa_sigaction, но не оба поля одновременно (в случае «классической» структуры sigaction, см. выше). Реализация QNX за счет объединения двух обработчиков под одним union обеспечивает это требование автоматически, хотя определения при этом становятся несколько более громоздкими.
Модель очереди сигналов введена главным образом для обеспечения сигналов реального времени и будет рассмотрена ниже.
Инициализации, используемые в примерах вида sigaction act = { &catchint, 0, (sigset_t)0};, будут зависимыми от системы из-за описанных ранее различий определения struct sigaction в разных ОС UNIX.
Повторить приложение У. Стивенса в QNX в чистом виде не удастся — оно аварийно завершится по сигналу. Тонкий анализ этого факта интересен сам по себе, но он выходит за рамки нашего рассмотрения. Мы обращаем внимание на это обстоятельство, чтобы лишний раз сделать акцент на достаточно ощутимых отличиях реализаций QNX от схем POSIX (или того, как эти схемы понимаются в других ОС).
Функция SignalProcmask() имеет свой реентерабельный (безопасный в потоках) эквивалент: SignalProcmask_r().
И здесь вопрос не в плагиате. Известно, что Р. Кертен долгое время сотрудничал с QSSL именно по части написания документации, поэтому она во многом следует его манере изложения, хотя более поздняя книга Р. Кертена [1] изложена заметно доходчивее.
Функции SyncMutexRevive() и SyncMutexRevive_r(), из которых вторая является потокобезопасной формой первой, как это описывалось выше, отличаются между собой только способом уведомления об ошибке: первая форма возвращает -1 в случае ошибки и устанавливает errno, а вторая непосредственно возвращает код ошибки.
Забавно! Речь идет об исчерпании количества рекурсивных захватов для внутреннего мьютекса. Здесь есть некоторое несоответствие с другими объектами синхронизации.
Естественно, поток, один из потоков процесса, но оригинальные описания логики обмена сообщениями сформулированы в терминологии процессов, и мы не станем отходить от этой традиции. Это обусловлено, скорее, преемственностью изложений с предыдущими реализациями ОС QNX — 4.X и более ранними, т.к. логика функционирования обмена сообщениями остается практически неизменной на протяжении более 20 лет развития линии QNX.
Как и функции spawn*() и exec*(), API обмена сообщениями предоставляет целые группы вызовов, различающихся суффиксами имен и форматами входных параметров.
Вы спросите, почему указатель, возвращаемый thread_pool_create(), имеет тип thread_pool_t*, а получающий его параметр thread_pool_start() определен как void*? Это или неаккуратность разработчиков QNX, или глубокая сермяжная правда, которую мы пока не понимаем.
Эта техника возникла не «сразу» и не случайно: ее идеология практически сложилась за почти 20 лет развития системы QNX, но не была представлена в виде формальных механизмов. В QNX 6.X оставалось только придать ей формальный вид и обеспечить ее поддержание написанием специальных библиотек.
В Интернете доступен прекрасный перевод этого документа, выполненный Владимиром Зайцевым и отредактированный к публикации Сергеем Малышевым: http://qnxclub.net/files/articles/resmgr/resmgr.pdf.gz.
Строго говоря, в однопоточных процессах QNX 4 обмен тоже организовывался между потоками, просто там разделение понятий «процесс» и «поток» не имело смысла и поэтому всюду использовался только термин «процесс».