
Когда происходит планирование потоков
Микроядро QNX Neutrino не работает постоянно, а получает управление только в случае системных вызовов, исключений и прерываний. Также микроядро во время своей работы выполняет планирование потоков. Отсюда можно сделать верный вывод, что операция планирования потоков происходит не сама по себе, а по какому-то событию. На самом деле таких событий немного:
- Вытеснение. Если поток с более высоким приоритетом, чем выполняющийся в настоящее время, перешёл в состояние готовности (READY), то микроядро остановит поток, выполняющийся в данный момент, переключит контексты и запустит на выполнение поток с более высоким приоритетом. Поток, который выполнялся до этого останется первым в очереди на выполнение.
- Блокирование. Поток во время своего выполнения (т.е. во время работы) может вызвать функцию, которая приведёт к его блокированию. Например, попытается захватить семафор или передаст сообщение при помощи системной функции
MsgSend()
(прямо или опосредованно). В этом случае ядро удалит такой процесс из очереди на выполнение и передаст управление другому потоку. - Уступка (передача управления, yield). Поток может добровольно передать управление, если вызовет функцию
sched_yield()
. В этом случае поток ставится последним в очереди на выполнение и микроядро передаст управление другому потоку (может быть и тому, который только что отдал управление).
Параметры потока, влияющие на планирование
Одна из основных функций микроядра QNX Neutrino (и, пожалуй, самая важная после обмена сообщениями) — это планирование потоков. Именно микроядро переключает контексты и выбирает, какой поток будет выполняться в следующий момент времени. Микроядро делает всё это не просто так и не по желанию своей левой пятки, а основываясь на следующих параметрах потоков:
- Приоритет потока (уровень приоритета потока). Каждый поток в ОСРВ QNX6 выполняется на каком-то определённом приоритете. Чем выше приоритет, тем больше шансов у потока получить процессор в первую очередь. При наличии в системе двух и более потоков в состоянии READY (готовых к выполнению), микроядро передаст управление тому потоку, чей приоритет выше.
- Дисциплина планирования. Каждый поток в системе выполняется с определённой дисциплиной планирования. Микроядро учитывает дисциплину планирования при наличии в системе двух и более потоков в состоянии READY, выполняющихся, на одном приоритете.
-p
программе procnto
. Также следует учитывать, что на нулевом (самом низком) приоритете выполняется поток idle
, который всегда получает управление, если в системе нет больше потоков с более высокими приоритетами в состоянии READY.В операционной системе QNX6 поддерживается несколько дисциплин планирования потоков: FIFO, карусельная (циклическая, round-robin, RR) и спорадическая**. Этот атрибут потока будет учитываться только, если микроядру приходится выбирать между потоками с одинаковым уровнем приоритета. Дисциплины планирования будут описаны дальше.
Существует ещё один фактор, который влияет на порядок переключения потоков. Все потоки готовые выполняться и использовать процессор (т.е. потоки в состоянии READY) помещаются в очередь. Таких очередей в системе 256 (по количеству приоритетов). При прочих равных условиях, когда микроядру приходится выбирать среди двух потоков с одинаковым уровнем приоритета, выполняться начнёт тот поток, который находится первым в очереди. Ещё раз повторюсь, при вытеснении потока более приоритетным, он ставится первым в очереди, а при уступке (вызове
sched_yield()
), поток становится последним в очереди.Дисциплина планирования FIFO
Если потоку задана дисциплина планирования FIFO (First In First Out, первый на входе, первый на выходе), то он может выполняться сколь угодно долго. Управление будет передано другому потоку только если поток будет вытеснен более приоритетным потоком, поток заблокируется или добровольно отдаст управление. При использовании этой дисциплины планирования, поток, который выполняет длительные математические вычисления, может полностью захватить процессор (т.е. не позволит выполняться потокам с тем же и более низким приоритетом).
Карусельная дисциплина планирования
Эта дисциплина планирования полностью аналогичная FIFO, за исключением того, что поток не выполняется «бесконечно», а работает только на протяжении определённого кванта времени (timeslice). По истечении кванта времени микроядро ставит процесс в конец очереди потоков, готовых к выполнению, и управление передаётся следующему потоку (на том же уровне приоритета). Если же на этом уровне приоритета нет других потоков в состоянии READY, то потоку выделяется ещё один квант времени.
Квант времени, который выделяется потокам с карусельной дисциплиной диспетчеризации для работы, может быть определён при помощи функции
sched_rr_get_interval()
. На самом деле квант времени (timeslice) ровно в четыре раза больше тактового интервала (ticksize). В свою очередь, тактовый интервал равен 1мс в системах с процессором 40МГц и выше и 10мс в системах с более медленным процессором***. Получается, что в привычных нам x86 компьютерах и ноутбуках квант времени равен 4мс.Спорадическая дисциплина планирования
Как и в FIFO планировании, поток, для которого применяется спорадическое планирование, выполняется до тех пор, пока он не блокируется или не будет вытеснен потоком с более высоким приоритетом. Кроме того, так же как и в адаптивном планировании, поток, для которого применяется спорадическое планирование, получает пониженный приоритет. Однако спорадическое планирование даёт значительно более точное управление потоком.
При спорадическом планировании, приоритет потока может динамически изменяться между приоритетом переднего плана (foreground, нормальным приоритетом) и фоновым (background, пониженным) приоритетом. Для управления этим спорадическим переходом используются следующие параметры:
- Начальный бюджет потока (initial budget) (С) — количество времени, за которое поток может выполняться с нормальным приоритетом (N), перед тем как получить пониженный приоритет (L).
- Пониженный приоритет (low priority) (L) — приоритетный уровень, до которого приоритет потока будет снижен. При пониженном приоритете (L) поток выполняется в фоновом режиме. Если же поток имеет нормальный приоритет (N), он выполняется с приоритетом переднего плана.
- Период пополнения (replenishment period) (T) — период времени, в течение которого поток может расходовать свой бюджет выполнения (execution budget).
- Максимальное число текущих пополнений (max number of pending replenishments) — это значение устанавливает ограничение на количество выполняемых операций пополнения, тем самым ограничивая объём системных ресурсов, выделяемых на дисциплину спорадического планирования.

Рис. 1. Пополнение периода выполнения потока происходит периодически
При нормальном приоритете N поток выполняется в течение периода времени, установленного его начальным бюджетом выполнения C. После того как этот период истекает, приоритет потока снижается до пониженного уровня L до тех пор, пока не произойдёт операция пополнения.
Представим, например, систему, в которой поток никогда не блокируется и не прерывается — рис. 2.

Рис. 2. Приоритет потока снижается до того момента, пока его бюджет выполнения не пополнится
В этом случае поток перейдёт на уровень с пониженным приоритетом (фоновый режим), на котором его выполнение будет зависеть от приоритета других потоков в системе.
Как только происходит пополнение, приоритет потока повышается до начального уровня. Таким образом, в правильно настроенной системе поток выполняется каждый период времени Т в течение максимального времени С. Это обеспечивает такой порядок, при котором каждый поток, выполняемый с приоритетом N, будет использовать только C/T процентов системных ресурсов.
Когда поток блокируется несколько раз, несколько операций пополнения могут происходить в разные моменты времени. Это может означать, что бюджет выполнения потока в пределах периода времени T дойдёт до значения C; однако, на протяжении этого периода бюджет может не быть непрерывным.

Рис. 3. Приоритет потока изменяется между повышенным и пониженным
На рис. 3 видно, что в течение каждого 40 мс периода пополнения T бюджет выполнения потока C составляет 10 мс.
- Поток блокируется через 3 мс, поэтому 3 мс операция пополнения будет запланирована к выполнению через 40 мс, т.е. на тот момент завершения первого периода пополнения.
- Выполнение потока возобновляется на 6-й миллисекунде, и этот момент становится началом следующего периода пополнения T. В бюджете выполнения потока еще остается запас в 7 мс.
- Поток выполняется без блокировки в течение 7 мс, в результате чего бюджет выполнения потока исчерпывается, и приоритет потока снижается до уровня L, на котором он сможет или не сможет получить управление. Пополнение в объёме 7 мс запланировано на 46-ю миллисекунду (40 + 6), т.е. по истечении периода T.
- На 40-й миллисекунде бюджет потока пополняется на 3 мс (см. шаг 1 на схеме), в результате чего приоритет потока поднимается до нормального.
- Поток расходует 3 мс своего бюджета и затем снова переходит на пониженный приоритет.
- На 46-й миллисекунде бюджет потока пополняется на 7 мс (см. шаг 3), и поток снова получает нормальный приоритет.
Как установить приоритет и дисциплину планирования из программы
Каждый поток при запуске наследует свои приоритет и дисциплину планирования от потока-родителя. Во время работы поток может изменить эти атрибуты. Для этой цели в QNX6 присутствуют следующие функции:
POSIX-вызов | Описание |
---|---|
sched_getparam() |
Получить приоритет. |
sched_setparam() |
Установить приоритет. |
sched_getscheduler() |
Получить дисциплину планирования. |
sched_setscheduler() |
Установить дисциплину планирования. |
SchedGet()
и SchedSet()
.Немного администрирования
В предыдущей заметке я уже ссылался на команду
pidin
. В этот раз мы познакомимся ещё с двумя командами, которые специфичны для QNX. Знать и уметь пользоваться этими командами должен каждый администратор QNX6. И, пожалуй, из самых важных команд это use
.Утилита
use
в каком-то роде аналог команды man
. Утилита позволяет получить справку по исполняемому модулю (бинарному исполняемому файлу, скрипту или разделяемой библиотеке). Принцип работы use
несколько отличается от man
, т.к. вся справочная информация хранится в самом исполняемом модуле, а не отдельно. Вызывается команда, довольно просто, например:# use sleep
sleep - suspend execution for an interval (POSIX)
sleep time
Where:
time is the number of seconds to sleep and can be a non-negative
floating point number (0 <= time <= 4294967295).
Утилита
use
выводит небольшую справку по работе с командой, полное описание доступно в справочной системе.Другая полезная команда, которая обязательно должна быть в арсенале каждого уважающего себя администратора QNX6 это
pidin
. Эта утилита предоставляет различного рода информацию о системе, в том числе и по исполняющимся потокам и процессам (в этом случае утилита похожа на утилиту ps
). Например, чтобы посмотреть общую информацию по системе, следует выполнить следующую команду:# pidin in
CPU:X86 Release:6.5.0 FreeMem:166Mb/255Mb BootTime:Jul 05 15:53:27 MSKS 2011
Processes: 43, Threads: 107
Processor1: 131758 Pentium II Stepping 5 2593MHz FPU
Вызов утилиты без параметров выведет на экран информацию по всем процессам и потокам. Чтобы получить информацию по интересующему процессу достаточно указать ключ
-P
, например:# pidin -P io-audio
pid tid name prio STATE Blocked
90127 1 sbin/io-audio 10o SIGWAITINFO
90127 2 sbin/io-audio 10o RECEIVE 1
90127 3 sbin/io-audio 10o RECEIVE 1
90127 4 sbin/io-audio 10o RECEIVE 1
90127 5 sbin/io-audio 50r INTR
90127 6 sbin/io-audio 50r RECEIVE 7
Можно посмотреть, как процесс использует память, для этого надо выполнить следующую команду:
# pidin -P io-audio mem
pid tid name prio STATE code data stack
90127 1 sbin/io-audio 10o SIGWAITINFO 128K 112K 8192(516K)*
90127 2 sbin/io-audio 10o RECEIVE 128K 112K 4096(132K)
90127 3 sbin/io-audio 10o RECEIVE 128K 112K 8192(132K)
90127 4 sbin/io-audio 10o RECEIVE 128K 112K 4096(132K)
90127 5 sbin/io-audio 50r INTR 128K 112K 4096(132K)
90127 6 sbin/io-audio 50r RECEIVE 128K 112K 4096(132K)
libc.so.3 @b0300000 472K 12K
a-ctrl-audiopci.so @b8200000 12K 4096
deva-mixer-ac97.so @b8204000 24K 8192
Выводится информация даже об используемых разделяемых библиотеках. Это очень удобно, на мой взгляд. Утилита
pidin
поддерживает достаточно много команд и опций, список и описание которых можно посмотреть в справочной системе QNX.И последняя на сегодня, но не по значению, утилита
slay
. Как не сложно догадаться, эта команда используется для отправки сигналов процессам. По умолчанию отправляется сигнал SIGTERM, что, обычно, приводит к завершению процесса. Можно указать другой сигнал, который необходимо послать процессу. При таком использовании slay
аналогична команде kill, но что самое интересное, команда slay принимает не только идентификатор процесса (PID), но и имя процесса. Это тоже очень удобно при администрировании. Помимо отправки сигналов, утилита может использоваться для смены приоритета или дисциплины планирования процесса. Если требуется изменить характеристики какого-то одного потока, то можно указать ключ -T
. Следующие несколько команд изменяют приоритет и дисциплину планирования 3 потока процесса io-audio
:[22:47:33 root]# pidin -P io-audio
pid tid name prio STATE Blocked
90127 1 sbin/io-audio 10o SIGWAITINFO
90127 2 sbin/io-audio 10o RECEIVE 1
90127 3 sbin/io-audio 10o RECEIVE 1
90127 4 sbin/io-audio 10o RECEIVE 1
90127 5 sbin/io-audio 50r INTR
90127 6 sbin/io-audio 50r RECEIVE 7
[22:47:36 root]# slay -T 3 -P 11r io-audio
[22:47:38 root]# pidin -P io-audio
pid tid name prio STATE Blocked
90127 1 sbin/io-audio 10o SIGWAITINFO
90127 2 sbin/io-audio 10o RECEIVE 1
90127 3 sbin/io-audio 11r RECEIVE 1
90127 4 sbin/io-audio 10o RECEIVE 1
90127 5 sbin/io-audio 50r INTR
90127 6 sbin/io-audio 50r RECEIVE 7
Полное описание утилиты
slay
доступно в справочной системе QNX.На всякий случай, хотел отметить, что в QNX6 присутствуют и знакомые UNIX утилиты
ps
и kill
. Однако, пользоваться в QNX6 гораздо удобнее именно pidin
и slay
, т.к. они учитывают специфику системы.Заключение
После прочтения этой заметки вы получили представление о планировании потоков в QNX6, достаточные знания о приоритетах и дисциплинах планирования, системных функциях и утилитах для управления процессами и потоками. В QNX также существует интересная технология Adaptive Partitioning (адаптивная декомпозиция), которая позволяет формировать группы процессов и назначать им процент использования процессорного времени. Чтобы не валить всё в одну кучу, я постараюсь описать эту технологию в одной из следующих заметок.
Список литературы
- Операционная система реального времени QNX Neutrino 6.3. Системная архитектура. ISBN 5-94157-827-X
- Операционная система реального времени QNX Neutrino 6.3. Руководство пользователя. ISBN 978-5-9775-0370-9
- Роб Кртен, «Введение в QNX Neutrino 2. Руководство для разработчиков приложений реального времени», 2-е издание. ISBN 978-5-9775-0681-6
* Под QNX6 в данной заметке понимается QNX 6.5.0. Поскольку ядро QNX Neutrino может быть доработано в одной из следующих версий, то механизмы планирования потоков, изложенные тут, могут измениться.
** В QNX6 есть также другая дисциплина планирования (OTHER, o), которая тождественна карусельной (round-robin, RR, r).
*** Тактовый интервал (ticksize) можно изменить, например, при помощи функции
ClockPeriod()
.