Олег Цилюрик - QNX/UNIX: Анатомия параллелизма
- Название:QNX/UNIX: Анатомия параллелизма
- Автор:
- Жанр:
- Издательство:Символ-Плюс
- Год:2006
- Город:Санкт-Петербург
- ISBN:5-93286-088-Х
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Олег Цилюрик - QNX/UNIX: Анатомия параллелизма краткое содержание
Книга адресована программистам, работающим в самых разнообразных ОС UNIX. Авторы предлагают шире взглянуть на возможности параллельной организации вычислительного процесса в традиционном программировании. Особый акцент делается на потоках (threads), а именно на тех возможностях и сложностях, которые были привнесены в технику параллельных вычислений этой относительно новой парадигмой программирования. На примерах реальных кодов показываются приемы и преимущества параллельной организации вычислительного процесса. Некоторые из результатов испытаний тестовых примеров будут большим сюрпризом даже для самых бывалых программистов. Тем не менее излагаемые техники вполне доступны и начинающим программистам: для изучения материала требуется базовое знание языка программирования C/C++ и некоторое понимание «устройства» современных многозадачных ОС UNIX.
В качестве «испытательной площадки» для тестовых фрагментов выбрана ОСРВ QNX, что позволило с единой точки зрения взглянуть как на специфические механизмы микроядерной архитектуры QNX, так и на универсальные механизмы POSIX. В этом качестве книга может быть интересна и тем, кто не использует (и не планирует никогда использовать) ОС QNX: программистам в Linux, FreeBSD, NetBSD, Solaris и других традиционных ОС UNIX.
QNX/UNIX: Анатомия параллелизма - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Ниже представлены отличия нашей реализации от простого цикла с задержкой, обсуждавшейся выше (помимо исправлений очевидных недостатков):
• Для каждого синхронизирующего таймера установлен свой приоритет «пробуждения», и он может быть достаточно высоким, для того чтобы предотвратить вытеснение этого синхронизирующего потока.
• После «пробуждения» по таймеру запускается целевая функция, но выполняется это отдельным потоком, причем потоком «отсоединенным». Другими словами, процесс выполнения целевой функции никак не влияет на общую схему синхронизации.
• Перед запуском целевой функции выполняющему ее потоку восстанавливается приоритет породившего потока (но не потока обслуживания таймера!), ведь нам не нужно, чтобы целевая функция, тем более, возможно и не очень значимая, как в нашем примере, могла влиять вытеснением на процессы синхронизации.
Запустим наше тестовое приложение:
# t3
+10+10*10+10+10.10*10+10+10*10+10+10.10*10+10+10+10*10+10.10+10*10+10+10*10+10.10+10*10+10+10*10+10.10+10+10*10+10+10+10.10+10+10*10+10+10.10*10+10+10+10*10+10.10+10*10+10+10*10+10+10.10*10+10+10*10+10+10.10+10*10+10+10*10+10.10+10*10+10+10*10+10.10+10+10*10+10+10*10^C
Monitoring finalisation!
0 32 => 316.919 [316.867...317.895] ~0.178511 (0.056327%)
1 59 => 170.955 [168.583...173.296] ~0.92472 (0.540914%)
2 132 => 76.9796 [76.942...77.9524] ~0.085977 (0.111688%)
Первое, что мы должны отметить, — это очень приличную точность выдержки периода синхронизации (последняя колонка вывода). Для того чтобы убедиться в том, что целевая функция при этом выполняется под приоритетом породившего ее потока, закомментируем строки, выделенные жирным шрифтом в коде программы:
# t3
+25+25*5+25+25.15*5+25+25*5+25+25.15*5+25+25+25*5+25.15+25*5+25+25*5+25.15+25*5+25+25*5*5+25.15+25+25*5+25+25*5.15+25+25*5+25+25.15*5+25+25+25*5+25.15+25*5+25+25*5+25+25.15*5+25+25*5+25+25^C
Monitoring finalisation!
0 32 => 316.919 [316.797...317.915] ~0.185331 (0.0584792%)
1 60 => 170.955 [168.964...173.925] ~0.47915 (0.280279%)
2 34 => 76.9796 [76.8895...77.9694] ~0.0937379 (0.12177%)
В этом варианте (и диагностический вывод это подтверждает) мы искусственно ликвидировали наследование приоритета по цепочке порождения: сработавший таймер — функция потока — целевая функция объекта. Это не совсем соответствует цели, намеченной в начале этого раздела, но все же этот вариант иллюстрирует, что именно наш предыдущий вариант удовлетворял всем поставленным целям.
3. Сигналы
Сигналы инициируются некоторыми событиями в системе и посылаются процессу для его уведомления о том, что произошло нечто неординарное, требующее определенной реакции. Порождающее сигнал событие может быть действием пользователя или может быть вызвано другим процессом или ядром операционной системы. Сигналы являются одним из самых старых и традиционных механизмов UNIX. [27] Именно поэтому, наверное, стандарту POSIX так сложно органично согласовать их с нововведениями, например с парадигмой потоков.
Уже из этого краткого описания можно заключить, что:
• действия, вызываемые для обработки сигнала, являются принципиально асинхронными;
• сигналы могут быть использованы как простейшее, но мощное средство межпроцессного взаимодействия.
Все сигналы определяются целочисленными константами, но для программиста это в принципе не так важно, поскольку каждому сигналу приписано символическое наименование вида SIG*
. Все относящиеся к сигналам определения находятся в заголовочном файле , который должен быть включен в любой код, работающий с сигналами. В последующих примерах мы не будем показывать эту включающую директиву, чтобы не загромождать текст.
Посланный сигнал обрабатывается получившим его потоком или процессом (как уже обсуждалось ранее, собственно процесс ничего не может обрабатывать: это пассивная субстанция; понятно, что речь в таком контексте идет об обработке сигнала главным потоком процесса). Поток может отреагировать на полученный сигнал следующими способами (иногда действие, установленное для конкретного сигнала, называют диспозициейсигнала):
• Стандартное действие— выполнение действия, предписанного для обработки этого сигнала по умолчанию. Для многих сигналов действием по умолчанию является завершение, но это необязательно; есть сигналы, которые по умолчанию игнорируются. Для большей части сигналов, чьим действием по умолчанию является принудительное завершение процесса, предписано действие создания дампа памяти при завершении (core dump), но для некоторых, например SYSINT
(завершение по [Ctrl+C]), определено такой дамп при завершении не создавать.
• Игнорирование сигнала— сигнал не оказывает никакого воздействия на ход выполнения потока получателя.
• Вызов обработчика— по поступлению сигнала вызывается функция реакции, определенная пользователем. Если для сигнала устанавливается функция-обработчик, то говорят, что сигнал перехватывается (относительно стандартного действия).
Для различных сигналов программа может установить различные механизмы обработки. Более того, в ходе выполнения программа может динамически переопределять реакции, установленные для того или иного сигнала.
Для большинства сигналов их воздействие можно перехватить из программного кода или игнорировать, но не для всех. Например, сигналы SIGKILL
и SIGSTOP
не могут быть ни перехвачены, ни проигнорированы. Другой пример — описываемые далее специальные сигналы QNX, начиная с SIGSELECT
и далее.
В QNX определены 64 сигнала в трех диапазонах:
• 1…40 — 40 POSIX-сигналов общего назначения;
• 41…56 — 16 POSIX-сигналов реального времени, введенных в стандарт позже (от SIGRTMIN
до SIGRTMAX);
• 57…64 — 8 сигналов, используемых в QNX Neutrino для специальных целей.
Начнем со специальных сигналов. Эти сигналы не могут быть проигнорированы или перехвачены: попытка вызвать signal()
или sigaction()
(или вызов ядра SignalAction()
native API) завершится для них с ошибкой EINVAL
. Более того, эти сигналы всегда блокированы в пользовательском приложении, и для них установлено разрешение очереди обслуживания (очереди сигналов будут подробно рассмотрены далее). Попытка разблокировать эти сигналы, используя sigprocmask()
( SignalProcmask()
— native API), будет проигнорирована. Эти сигнальные механизмы используются, в частности, графической системой Photon для ожидания событий GUI и функцией select()
для ожидания ввода/вывода на множестве дескрипторов (один из фундаментальных механизмов UNIX). Вот определения некоторых из этих сигналов:
#define SIGSELECT (SIGRTMAX + 1)
#define SIGPHOTON (SIGRTMAX + 2)
В силу своей недоступности эта группа сигналов представляет небольшой интерес для разработчика приложений.
Далее перейдем к POSIX-сигналам общего назначения (1…40), из которых назовем только самые употребляемые [28] Наличие круглых скобок после имени сигнала — признак того, что для этих сигналов реакция по умолчанию — принудительное завершение процесса; наличие (+) означает, что для этих сигналов предусмотрено создание дампа завершения, а наличие (-) — для этих сигналов дамп не создается. В квадратных скобках после имени сигнала указано численное значение, соответствующее данному сигналу, как оно определено в QNX.
(пока мы описываем все сигналы в классической UNIX-нотации, не углубляясь в нюансы, например такие, как особенности реакции на них в многопоточном окружении):
Интервал:
Закладка: