Получить доступ

Как планировщик ОС решает, какой поток выполнять?

MiddleОперационные системы
Ещё спрашивают так:
  • Что такое вытесняющая многозадачность?
  • Чем CPU-bound задача отличается от I/O-bound?
  • Что такое load average?
Коротко

Планировщик выдаёт потокам, готовым к работе, кванты времени на ядрах и вытесняет их по таймеру — так один поток не захватит процессор.

  • Ждущие ввода-вывода потоки не занимают процессор — они спят в очереди ожидания
  • В Linux — CFS/EEVDF: честно делит время с учётом nice-приоритета
  • I/O-bound задачи большую часть времени ждут, CPU-bound — считают

А ещё Load average — средняя длина очереди готовых и ждущих диска потоков, сравнивать с числом ядер

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Подробно

Состояния потока: выполняется (на ядре), готов (ждёт своей очереди на процессор), ждёт (ввода-вывода, блокировки, таймера). Планировщик работает с готовыми.

Вытесняющая многозадачность. Таймер периодически прерывает выполнение, и ядро решает, оставить текущий поток или переключиться. Поток также уступает процессор сам, когда блокируется. Кооперативная многозадачность, где задача отдаёт управление только сама, осталась в пользовательских планировщиках — event loop в asyncio и Node.js: одна задача с бесконечным циклом без await повесит всё.

Linux. Долгое время работал CFS — «совершенно честный планировщик»: каждому потоку учитывается виртуальное время выполнения, и следующим идёт тот, кто получил меньше всего (структура — красно-чёрное дерево). С версии 6.6 его заменил EEVDF, сохранив идею честного деления. Приоритет задаётся nice от −20 до 19 — это вес при делении, а не жёсткий порядок. Для задач реального времени есть отдельные политики (SCHED_FIFO).

CPU-bound и I/O-bound. Вычислительная задача использует весь квант, и потоков больше, чем ядер, ей не нужно. Задача ввода-вывода почти всё время ждёт, поэтому потоков может быть больше ядер — или вообще один поток с event loop. Для пулов это даёт оценку: число потоков ≈ ядра × (1 + время ожидания / время вычисления).

Load average в uptime и top — экспоненциальные средние за 1, 5 и 15 минут числа потоков, которые выполняются или готовы выполняться (в Linux ещё и ждущие диска в состоянии D). Значение 8 на 8 ядрах — полная загрузка, 30 на 8 ядрах — очередь, задержки растут. Высокий load average при низкой загрузке CPU указывает на ожидание диска.

Контейнеры. Лимит CPU в Kubernetes реализован квотой CFS: при превышении квоты потоки замораживаются до следующего периода (throttling) — отсюда скачки задержки при «свободном» процессоре.

Пример

$ uptime
 10:21:04 up 12 days, load average: 15.82, 14.10, 9.37
$ nproc
8
# Очередь вдвое длиннее числа ядер и растёт — запросы ждут процессор.

$ nice -n 10 ./batch-report      # фоновая задача получит меньшую долю CPU

Что хотят услышать

  • Состояния потока и что планирует планировщик.
  • Вытеснение по таймеру и добровольная уступка при ожидании.
  • Разница CPU-bound и I/O-bound и вывод для размера пулов.
  • Load average и сравнение с числом ядер.
  • Throttling в контейнерах — плюс.

Частые ошибки

  • Считать load average процентом загрузки CPU.
  • Думать, что nice даёт жёсткий порядок выполнения.

Что спросят дальше

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем