Управление частотой процессора это баланс между производительностью и энергопотреблением. Загруженное ядро требует максимальной частоты для скорости, простаивающее - минимальной для экономии ватт и охлаждения. Выбор неверного governor или драйвера на горячем пути задержек приводит к миллисекундам ожидания переключения, а на ферме из тысяч узлов оборачивается заметными суммами за электричество. Когда ядро загружено - поднимаем частоту, когда пусто - опускаем. За эту работу в Linux отвечает подсистема cpufreq с семейством драйверов для каждой архитектуры и поколений процессоров. В эпоху энергосбережения и датацентров с тысячами машин выбор драйвера и его настройка напрямую влияют и на счёт за электричество, и на задержки прикладных систем. От драйвера зависит, насколько быстро частота отреагирует на рост нагрузки, какие градации доступны, и можно ли вообще управлять частотой вручную.

Разберём архитектуру cpufreq, драйверы для x86 и ARM, governors, тонкую настройку через sysfs, профилирование частоты и практический порядок выбора.

Архитектура подсистемы cpufreq и её уровни

Подсистема cpufreq в ядре строится из двух уровней. Верхний уровень - governors, алгоритмы, решающие, какую частоту выставить. Нижний - драйверы, которые переводят решение governor в конкретные команды железу: запись в MSR, регистр контроллера, обращение к ACPI или прошивке. Интерфейс между ними - struct cpufreq_driver и struct cpufreq_policy. Каждый драйвер объявляет функции init, verify, target, get, и list_freqs - список доступных частот. Ядро создаёт для каждого CPU объект policy, где хранятся текущая частота, минимальная, максимальная, список градаций и ссылка на governor. Родственные ядра с общим контроллером объединяются в одну policy: на Intel с гипертредингом оба логических ядра делят объект, на ARM big.LITTLE кластер живёт в общем policy, и настройки применяются целиком.

Пользовательский интерфейс живёт в sysfs:

ls /sys/devices/system/cpu/cpu0/cpufreq/
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq

Файл scaling_available_frequencies показывает список градаций, поддерживаемых процессором. scaling_cur_freq - текущая частота в кГц, и её чтение в цикле даёт мгновенный профиль реакции governor на нагрузку. Путь /sys/devices/system/cpu/cpufreq/policy0 объединяет настройки кластера, и запись в файлы policy применяет их ко всем связанным ядрам сразу, что удобнее, чем обходить каждое ядро по отдельности. Смена governor и частоты делается записью в соответствующие файлы, и изменения применяются немедленно без перезагрузки. Каталог stats с файлом time_in_state копит статистику пребывания на каждой частоте при включённой CONFIG_CPU_FREQ_STAT.

Драйверы cpufreq для x86 платформ

На x86 платформах работает семейство драйверов Intel P-state и AMD frequency sensitivity. Intel P-state (intel_pstate) появился в ядре 3.9 и стал стандартом для процессоров Intel начиная с Sandy Bridge. Он обходит ACPI и общается напрямую с контроллером частоты через MSR IA32_HWP_REQUEST и IA32_PERF_CTL. Причина появления - медлительность ACPI-пути: старый acpi-cpufreq через объекты _PSS переключал частоту за десятки микросекунд, тогда как прямой доступ к MSR занимает единицы. На серверах с тысячами переключений в секунду разница складывается в измеримый процент времени. Драйвер работает в двух режимах: active и passive. В active режиме governor встроен в драйвер, и доступен только powersave или performance. В passive режиме работает внешний governor вроде schedutil или ondemand.

Современные процессоры с HWP (Hardware P-states) умеют менять частоту сами, и драйвер настраивает только границы через EPP (Energy Performance Preference). Запись в IA32_HWP_REQUEST байта EPP задаёт приоритет энергосбережения от 0 (максимальная производительность) до 255 (максимальная экономия):

cat /sys/devices/system/cpu/intel_pstate/hwp_dynamic_boost
cat /sys/devices/system/cpu/intel_pstate/status

AMD частотный драйвер amd-pstate появился в ядре 5.17 и работает по схеме CPPC (Collaborative Processor Performance Control). Название CPPC расшифровывает идею: ОС задаёт желаемую производительность в нормализованных единицах, а прошивка переводит её в частоту с учётом температуры и токовых ограничений. Такой подход снимает с ОС ответственность за калибровку частот, которая различается между экземплярами чипов. Он отправляет желаемую частоту прошивке через ACPI, и прошивка сама решает, как её реализовать. amd-pstate тоже поддерживает HWP-подобный режим, и его настройки доступны через sysfs. Версия amd-pstate-epp работает в active режиме с встроенным governor, базовая версия - в passive с внешним. Выбор режима делается параметром загрузки ядра amd_pstate=active или amd_pstate=passive, и смена требует перезагрузки. Активный драйвер виден в файле scaling_driver.

Драйверы cpufreq для ARM платформ

На ARM платформах главный драйвер - cpufreq-dt, Device Tree-based. Он читает таблицу частот из devicetree и общается с контроллером через API clk. Каждая платформа имеет собственный драйвер для управления частотой: для Allwinner sun8i-cpufreq, для Rockchip rockchip-cpufreq, для Qualcomm qcom-cpufreq-hw. Универсальный подход через cpufreq-dt покрывает большинство SoC, где контроллер частоты стандартный. Таблица operating-points-v2 в devicetree описывает пары частота-напряжение, и драйвер выставляет обе величины согласованно через регулятор, ведь снижение частоты без снижения напряжения экономит мало. Связка clk и регулятора в cpufreq-dt обрабатывает пару атомарно, предотвращая нестабильные состояния.

На серверных ARM процессорах вроде Neoverse или Ampere Altra работает ACPI CPPC через драйвер cppc_cpufreq. Он читает таблицы _CPC из ACPI и общается с прошивкой по тому же протоколу, что и amd-pstate. Для платформ с кастомными контроллерами драйвер определяет операции get_rate и set_rate через регистры. Отдельная история - big.LITTLE архитектуры: кластеры разнородных ядер имеют разные таблицы частот, и ядро создаёт отдельные policy для каждого. Планировщик EAS (Energy Aware Scheduling) раскладывает задачи по кластерам ради минимального расхода и работает на данных из Energy Model в devicetree: маленькая задача уходит на эффективный кластер, тяжёлая на производительный. Отсутствующая Energy Model отключает EAS, и планировщик возвращается к разбору по загрузке.

Governors и их выбор

Governor решает, когда и на какую частоту переключаться. Историческая четвёрка: performance держит максимальную частоту постоянно, powersave держит минимальную, ondemand поднимает частоту при загрузке и опускает при простое, conservative поднимает постепенно. Современный стандарт - schedutil, интегрированный с планировщиком: governor читает утилизацию из счётчиков планировщика и обновляет частоту каждый тик, что даёт быструю реакцию без лишних переключений.

Проверка и смена governor:

cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors
echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor

Для серверных нагрузок с постоянной утилизацией performance или schedutil с EPP=performance дают минимальную задержку. Для приложений с переменной нагрузкой schedutil без EPP ограничений балансирует между производительностью и энергопотреблением. Для встраиваемых систем и ноутбуков powersave или schedutil с EPP=power экономят батарею. ondemand остаётся для legacy систем, где schedutil недоступен, но его polling-механизм с периодическими проверками загрузки уступает schedutil в отзывчивости и добавляет лишние пробуждения. Характерные параметры ondemand: sampling_rate и up_threshold настраиваются в каталоге /sys/devices/system/cpu/cpufreq/ondemand/. Тюнинг делается перебором: частый sampling сжигает процессор, редкий даёт задержку. Для фоновых систем 10 миллисекунд и 95 процентов экономят энергию без потери отзывчивости.

Тонкая настройка через sysfs

Помимо смены governor и частоты, sysfs даёт тонкий контроль. Минимальная и максимальная частоты ограничивают диапазон governor:

echo 2200000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq
echo 4000000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq

Для Intel P-state с HWP доступны min_perf_pct и max_perf_pct - проценты от максимальной производительности, и EPP для каждого ядра отдельно. Параметр no_turbo отключает турбо-буст целиком, что полезно на узлах с ограниченным теплопакетом. Для AMD amd-pstate есть аналогичные параметры lowest_perf и highest_perf.

Общая настройка на группе ядер делается циклом:

for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do echo schedutil > $cpu; done

Для постоянного применения конфигурация переносится в udev-правило или systemd-юнит, который применяет настройки при загрузке до запуска служб. systemd-udev обрабатывает события добавления CPU: правило SUBSYSTEM==cpu, ACTION==add запускает скрипт настройки, и фермы получают одинаковую конфигурацию автоматически. В контейнерных средах настройки наследуются от хоста, а ограничение через cgroups cpu.max дополняет картину квотами времени. Для фиксированной конфигурации фермы удобен скрипт в /etc/rc.local или отдельный unit, запускаемый до multi-user.target.

Профилирование и мониторинг частоты

Мониторинг частоты в реальном времени показывает, как governor реагирует на нагрузку. Команда turbostat из пакета linux-tools выводит текущую частоту каждого ядра:

turbostat --quiet --show Bzy_MHz,PkgTmp -- sleep 5

Вывод показывает среднюю частоту Busy_MHz за интервал и температуру пакета. Для долгосрочного мониторинга cpufreq-статистика собирается через:

cat /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state

Файл time_in_state показывает, сколько времени процессор провёл на каждой частоте, и по нему видно, работает ли governor как ожидается. Строки с большим временем на минимальной частоте при заявленной высокой нагрузке сигнал о проблеме: governor не реагирует, частоты ограничены настройками или BIOS заблокировал турбо-режим. Дополнительно turbostat с ключом --debug выводит детальную телеметрию: residency пакетных C-состояний, частоту каждого ядра, потребление RAPL. Дополнительно perf с событием power/energy-cores/ даёт энергопотребление, и связка частоты с потреблением показывает, где происходит неэффективный расход.

Практический порядок выбора драйвера и governor

Выбор драйвера и governor укладывается в шесть шагов:

  1. проверить доступный драйвер через /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver и убедиться, что это intel_pstate, amd-pstate или cppc_cpufreq для серверного железа;
  2. проверить поддержку HWP через /sys/devices/system/cpu/intel_pstate/hwp_dynamic_boost или флаг cpb в cpuinfo для AMD;
  3. выбрать governor под профиль нагрузки: schedutil для универсального, performance для серверов с постоянной нагрузкой, powersave для встраиваемых;
  4. настроить границы частот и EPP, если драйвер поддерживает, ограничив диапазон под тепловой бюджет;
  5. прогнать эталонную нагрузку с turbostat и time_in_state, зафиксировать фактические частоты и энергопотребление;
  6. закрепить настройки в udev-правиле или systemd-юните, добавить мониторинг частоты в общий дашборд.

Итог у cpufreq зрелая система: драйверы для всех платформ, governors на любой вкус, тонкая настройка через sysfs и полная наблюдаемость. Механизм HWP на свежих процессорах передаёт управление частотой в железо, и ОС лишь рисует границы, что снимает нагрузку с ядра и даёт микросекундное время реакции. На серверах с Intel P-state и HWP частота меняется за микросекунды без участия ядра, на ARM с schedutil реакция занимает доли миллисекунды, и правильный выбор комбинации драйвера и governor экономит и ватты, и миллисекунды задержки. Ключ к успеху - в измерении на реальной нагрузке, а не в доверии к рекомендациям из документации.