Вопрос с собеседования · Основы

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

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

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

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

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

Подробно

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

Вытесняющая многозадачность. Таймер периодически прерывает выполнение, и ядро решает, оставить текущий поток или переключиться. Поток также уступает процессор сам, когда блокируется. Кооперативная многозадачность, где задача отдаёт управление только сама, осталась в пользовательских планировщиках — 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 даёт жёсткий порядок выполнения.

Проверьте себя

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✓Выдаёт готовым потокам кванты и вытесняет по таймеру; ждущие не занимают CPU
  • ✗Поток работает, пока сам не отдаст процессор
  • ✗Потоки, ожидающие диска, постоянно занимают ядро
  • ✗Load average — процент загрузки одного ядра

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

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

Партнёр платформы

Разбор собеседования с sotto

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования