Простые вопросы не всегда имеют простые ответы. Вот, например, вопрос : «А какой приоритет имеет данный процесс» , который часто задают наши клиенты, когда хотят, чтобы мы объяснили почему планировщик центрального процессора отдал предпочтение тому, или иному процессу. Ответ на этот вопрос не так прост, как может показаться на первый взгляд. Что бы ответить на него правильно, сначала нужно постараться найти ответы на ряд дополнительных вопросов:
- Какой именно поток исполнения, внутри этого процесса, нас интересует?
- К какой политике планирования относится этот поток исполнения?
- Кто назначал приоритет исполнения для этого потока?
Процессы. Потоки. Задачи.
Как и большинство современных операционных систем, Linux многопоточность (Multithreading). В самом простом случае запущенный процесс будет иметь один единственный поток исполнения, называемый главным. Если процесс имеет несколько потоков исполнения, то они совместно используют доступные этому процессу ресурсы. Самым востребованным ресурсом, как правило, является системная память. Все потоки исполнения внутри процесса используют в своей работе одну и ту же память, выделенную системой данному процессу.
Внутри ядра ( kernel ) ОС Linux наименьшей единицей, которой оперирует планировщик центрального процессора ( Linux CPU Scheduler ), является задача ( task ) — любая работа, выполняемая центральным процессором. Такой работой может быть поток исполнения ядра ( kernel thread ), поток исполнения пространства пользователя (userspace thread), многопоточное прерывание ( threaded IRQ ) и т. д. ( Для простоты изложения, предположим, что задачи (tasks) и потоки выполнения (threads) — это одно и то же. ) Резюмируя вышеизложенное можно сказать, что планировщик центрального процессора в ОС Linux не управляет процессами, как таковыми, а, фактически, оперирует только потоками исполнения.
Во время своей работы планировщик центрально процессора гарантирует, что для каждой задачи ( а, следовательно, и потока исполнения) будет назначена своя очередность выполнения — приоритет ( priority ). Чтобы узнать, какой приоритет имеет конкретный процесс, необходимо изучить приоритеты всех потоков исполнения внутри него. По умолчанию, большинство служебных программ в ОС Linux, способных отображать информацию о процессах, показывают лишь приоритет главного потока исполнения для процесса. Хотя, надо отдать должное, что некоторые подобные инструменты все же предусматривают способ отображения потоков исполнения( например, в программе top, путем нажатия «H» )
Итак, мы узнали, что задачи (task) внутри выполняемого процесса (process) имеют свой приоритет (priority), и чтобы узнать приоритет (priority) этого процесса (process), нужно исследовать приоритеты (priority) всех составляющих его задач. Теперь мы способны ответить на первый вопрос из серии, заданных выше.
Политика планирования
Требования стандартов POSIX для операционных систем предполагают наличие различных политик планирования (scheduling policy), иногда называемых классами планирования (scheduling classes). По умолчанию в ОС Linux для планирования выполнения задач используются политики SCHED_NORMAL или SCHED_OTHER
Алгоритм политики SCHED_OTHER не описан в POSIX и зависит от конкретной его реализации разработчиком. OC Linux гарантирует справедливое отношение планировщика (scheduler) ко всем задачам в рамках этой политики планирования. Это означает, что ни одна задача не будет испытывать недостатка в ресурсах, и, с точки зрения пользователя, будет казаться, что все программы работают стабильно не прерываясь, а более высокие рабочие нагрузки не будут влиять на время выполнения. В рамках этой политики планирования, каждая задача будет иметь свой приоритет, так называемое значение nice. Значение nice ( приятный, хороший — пер. с английского) показывает насколько «приятной, хорошей, удобной» является выполняемая задача для конкурирующих с ней за ресурсы других задач. Значения приоритета nice для политики планирования SCHED_OTHER находятся в диапазоне от 19 до -20. Где -20 означает, что выполняемая задача является очень «неприятной» для конкурирующих с ней задач, так как хочет запускаться так часто, как это только возможно, и это «мешает» остальным. Значение nice, равное 19, означает, что задача очень «хорошая» и «не будет против», если планировщик для запуска выберет другую задачу, а не ее. Значение nice принятое в ОС Linux по умолчанию — 0, означает, что задача не слишком «эгоистична», и не слишком «альтруистична». Текущая реализация алгоритма планирования, стоящего за SCHED_OTHER , называется Completely Fair Scheduling — CFS ( Полностью Справедливым Планированием ) и включает в себя два специальных дополнительных класса планирования: SCHED_BATCH и SCHED_IDLE. Алгоритм планирования SCHED_BATCH предполагает запуск потоков исполнения, если это возможно, в пакетном режиме, такой подход актуален для неинтерактивных задач. Суть политики планирования SCHED_IDLE — поток исполнения должен запускаться в случае, если ни одна другая задача не выполняется, что подходит для задач только с крайне низким приоритетом. Надо отметить, что политика планирования SCHED_IDLE не имеет ничего общего с так называемым idle thread (Простаивающим потоком), который запускается планировщиком только в том случае, когда вообще больше нечего запускать. Надо отметить, что, для политик планирования SCHED_BATCH и SCHED_IDLE , величина приоритета не имеет значения, хотя некоторые системные инструменты OC Linux её отображают.
Политики SCHED_FIFO и SCHED_RR, описанные в Расширениях Реального Времени POSIX (Realtime Extensions POSIX), и реализованные в ОС Linux, предопределяют взаимодействие между потоками, позволяющее удовлетворять требования программ ко времени выполнения, в случаях, когда это имеет решающее значение. Диапазон значений приоритета, использующийся в обеих политиках, находится от 1 до 99, от самого низкого до самого высокого соответственно, и планировщик будет соблюдать очередность запуска задач основываясь на этих значениях.
Еще одна политика планирования в ОС Linux — это SCHED_DEADLINE. Эта политика не разрешает устанавливать приоритет запуска, вместо этого она оперирует понятием ближайшего предельного срока завершения ( Deadline ), к которому запущенная задача должна быть выполнена. Так что планировщик должен «быть уверен», что у запущенной задачи будет достаточно времени для ее выполнения.
Алгоритмы и порядок принятия решений планировщиком в ОС Linux.
У кого-то может возникнуть вопрос: «Почему не происходит путаницы, когда все, перечисленные выше, политики планирования одновременно используются в одной системе?» Дело в том, что у планировщика центрального процессора в ОС Linux есть пять алгоритмов принятия решений, которые он использует для взаимодействия с потоками. Работают эти алгоритмы следующим образом:
- Завершение задачи ( Stop-Task ): Этот алгоритм может быть использован только внутренним потоком исполнения ядра, для того, чтобы завершить работу конкретного центрального процессора ( ЦП ). Позаботиться о задачах, выполняемых ЦП, перед тем, как завершить его работу — вот единственная цель этого алгоритма.
- Ближайший предельный срок завершения ( Deadline ): этот алгоритм реализует политику планирования SCHED_DEADLINE, основанную на идее выбора для выполнения из очереди ожидающих процессов задачи, наиболее близкой к истечению крайнего расчётного времени своего выполнения (deadline). Если в системе присутствует поток SCHED_DEADLINE— он будет запущен раньше любого другого, видимого пользователю, потока.
- Алгоритмы настоящего(реального) времени ( Realtime ) : Здесь реализуются политики планирования SCHED_FIFO и SCHED_RR . Приоритет, а значит и очередность, запуска у задач в рамках этой политики выше, чем у обычных задач, но ниже чем у SCHED_DEADLINE.
- CFS ( политика Полностью Справедливого Планирования ) : CFS включает в себя политики SCHED_OTHER, SCHED_BATCH и SCHED_IDLE. Большинство потоков, присутствующих в системе, управляются этими алгоритмами.
- Политика Простоя (Idle) : Если ни один алгоритм не может найти задачу для выполнения, то, согласно этой политики, центральный процессор переводится в режим бездействия.
Давайте подведем промежуточные итоги: значения приоритета для потоков, управляемых политикой SCHED_OTHER, лежат в диапазоне от -20 до 19, 19 наименьший, а -20 наивысший приоритет. Для задач, запущенных в рамках политик SCHED_FIFO и SCHED_RR, используется диапазон от 1 до 99, где 1 — это самый низкий приоритет, а 99 самый высокий. Задачи, выполняемые согласно политикам планирования SCHED_IDLE и SCHED_BATCH, тоже имеют показатели «хорошести» ( nice ), однако, эти значения будут игнорироваться планировщиком. Так, что не давайте ввести себя в заблуждение служебным программам, отображающим значение nice для SCHED_BATCH или SCHED_IDLE . Для вывода информации о выполняющихся задачах в ОС Linux существует очень мощная служебная программа ps. Чтобы вывести список всех потоков в системе, с их идентификаторами, с идентификаторами их процессов, коротким именем, аббревиатурой политики планирования, значением «хорошести» (nice) и приоритетом времени исполнения, достаточно запустить программу ps с набором параметров, как показано ниже:
ps axHo pid, lwp, comm, policy, nice, rtprio
Аббревиатуры названий политик планирования, присутствующие в выводе этой программы, расшифровываются следующим образом: DLN означает SCHED_DEADLINE , TS - это SCHED_OTHER , B - SCHED_BATCH, IDL - SCHED_IDLE, FF -SCHED_FIFO и RR расшифровывается, как SCHED_RR.
Различные диапазоны значений приоритета в ОС Linux
Итак мы знаем, что «официально» в ОС Linux, для различных политик планирования, у приоритета может быть либо значение nice ( «хорошесть» ), находящееся в диапазоне от -20 до 19, либо значение real time ( настоящего времени ) из диапазона от 1 до 99. По крайней мере, такие значения, как правило присутствуют в выводе программ типа ps, отображающих информацию о процессах в ОС Linux.
Однако, если мы захотим написать свой инструмент, оперирующий «сырыми» данными приоритета, предоставляемые ядром операционной системы, то столкнемся с тем, что внутри ядра используются совершенно другие диапазоны значений приоритета.
Системный вызов GETPRIORITY()
Системный вызов getpriority(), выполненный посредством языка программирования Си, после предварительной обработки результата специальной библиотекой, вернет значение из диапазона от -20 до 19, характерного для приоритета типа nice. Если же вызвать его напрямую, без обработки, то нам вернется значение nice из смещенного диапазона от 1 до 40. Причина подобного сдвига диапазона значений заключается в системном вызове Linux ABI ( отрицательные возвращаемые значения интерпретируются операционной системой, как коды ошибок ) Так, что если, разрабатывая приложение, вы решите использовать данный системный вызов напрямую - результат может быть обескураживающим.
Использование этого системного вызова имеет смысл только в рамках политики планированияSCHED_OTHER. Для любых других политик возвращаемый приоритет не будет иметь значения.
Унифицированный приоритет
Обычно в ОС Linux, когда разработчику программного обеспечения нужно посмотреть, какую задачу выбирает планировщик центрального процессора в тот или иной момент времени, он пользуется программами, подобными ftrace, позволяющими трассировать процессы, протекающие в ядре ОС Linux. Точка трассировки, используемая в данном случае, называетсяsched_switch позволяет посмотреть состояние планировщика :
[002] d... 565635.368831: sched_switch: prev_comm=kworker/u8:2 prev_pid=891 prev_prio=120 prev_state=R+ ==> next_comm=kworker/u9:4 next_pid=983 next_prio=100
Вывод программы ftrace показывает, что планировщик второго центрального процессора переходит от задачи c PID 891 к задаче с PID 893. Надо отметить, что в данном случае, внутри ядра ОС, PID — это идентификатор потока исполнения( задачи ), а не процесса, как это могло бы ожидаться в контексте окружения пользователя. То, что в контексте окружения пользователя получается в результате системного вызова getpid() на самом деле является идентификатором группы потоков исполнения.
Там же мы можем видеть значения приоритетов для обеих задач. Однако, пока эти значения: prev_prio=120 и next_prio=100 не имеют для нас смысла, поскольку ни одно из них не принадлежит к знакомым нам диапазонам nice или real time приоритетов. Кроме того, информация, полученная нами, в данной точке трассировки ни чего не говорит об используемой центральным процессором политике планирования.
Чтобы как-то интерпретировать эти значения нам нужно знать, что все политики планирования внутри ядра ОС Linux для определения очередности задач используют значения, так называемого, унифицированного приоритета. Его значения лежат в диапазоне от -1, самого высокого, до 139, самого низкого. Он может быть рассчитан с помощью приведенной таблицы:
Теперь мы можем привести prev_prio к удобному nice значению 0, а next_prio к значению -20 по шкале nice.
Обычно, при работе c приложениями реального времени, возникает путаница между внутренними значениями приоритетов внутри ядра ОС Linux и значениями приоритетов, возвращаемых инструментами окружения пользователя. Оба значения могут быть в одном диапазоне, но противоположны по смыслу.
Внимательный читатель, наверняка заметил, что значение 99 унифицированного приоритета не используется ни одной политикой. Эта внутренняя особенность программного кода планировщика ОС Linux все еще имеет место на момент написания статьи (v5.17-rc3)
Псевдофайловая система /PROC
Все программы, подобные ps, отражающие информацию о процессах в ОС Linux, черпают ее из данных, предоставляемых псевдофайловой системой /proc. Каждому потоку исполнения в ней соответствует статистический файл, содержащий, помимо всего прочего, всю информацию о политике планирования.
Для того, чтобы узнать каким приоритетом обладает задача, нужно посмотреть на 18 и 19 поле в стат-файле по адресу /proc/<pid>/task/<tid>/stat . Например, если задача придерживается политики планирования SCHED_OTHER , тогда 19 поле стат-файла станет ненулевым и будет отображать значение приоритета nice. Поле номер 18 всегда содержит значение унифицированного приоритета со смещением равным -100
Итог
- Планировщик центрально процессора в ОС Linux оперирует потоками исполнения, а не процессами.
- Каждый такой поток исполняется в рамках одной из политик планирования, либо SCHED_DEADLINE, SCHED_OTHER, SCHED_BATCH, SCHED_IDLE, SCHED_FIFO либо SCHED_RR.
- Политики планированияSCHED_OTHER, SCHED_FIFO and SCHED_RR позволяют назначать приоритеты потокам.
- В политике планирования SCHED_OTHER приоритетом является, так называемое, значение nice( «хорошесть» ), лежащее в диапазоне от-20 до 19.
- В политикахSCHED_FIFO иSCHED_RR приоритет может принимать значения от1 до99.
- Программные инструменты уровня ядра операционной системы в качестве приоритета возвращают его унифицированную, общую, форму, как правило, с некоторым смещением относительно приоритетов политик планирования.
Автор: sigma star gmbh DEV BLOG