Аффинность IRQ в Linux на многопроцессорных системах: практическое руководство по оптимизации сетевых настроек

Я на практических примерах покажу, как с помощью IRQ-аффинности в многопроцессорных системах можно целенаправленно привязывать сетевые прерывания к ядрам процессора, снижать задержку и повышать пропускную способность. С помощью четких инструкций, примеров и таблицы я объясняю, как выбирать шестнадцатеричные маски, учитывать NUMA и правильно привязывать процессы.

Центральные пункты

  • Сродство IRQ целенаправленно направляет аппаратные прерывания на процессоры и снижает накладные расходы.
  • Аффинность процессора Фиксация обслуживания на тех же ядрах позволяет сохранять кэши локально.
  • NUMA Обратите внимание: адаптеры и ядра должны относиться к одному и тому же узлу.
  • irqbalance взвесить все «за» и «против»: автоматическое распределение или точная ручная настройка.
  • Мониторинг а итеративная настройка обеспечивает стабильные задержки.

Понимание аффинности IRQ: основы и последствия

На хостах под управлением Linux решают проблемы с входящими пакетами, дисковым вводом-выводом и таймерами IRQs которые ядро распределяет между ядрами процессора. Я определяю через файлы /proc/irq//smp_affinity и .../smp_affinity_list, какие процессоры могут обращаться к данному источнику. Широкая стандартная маска на первый взгляд кажется гибкой, однако при высокой нагрузке она приводит к промахам кэша, затратным сменам контекста и разбросанным программным прерываниям (SoftIRQ) по многим Процессоры. Я привязываю критические очереди отдельных сетевых карт к заданным ядрам, снижаю нагрузку на «горячие точки» и сокращаю пути передачи данных. Такое управление заметно повышает стабильность, когда одновременно активны много потоков.

Настройка IRQ и аффинности процессора: хранение данных локально

Мне удалось подключить IRQ-Привязка очередей NIC к процессорам, к которым относятся соответствующие рабочие процессы. Для этого я привязываю потоки веб-сервера или прокси с помощью набор задач или CPUAffinity= в systemd на те ядра, которые также обрабатывают прерывания RX/TX. Благодаря этому строки кэша остаются локальными, и я свожу межпроцессорную коммуникацию к минимуму, что Латентность выравнивает. Именно API-бэкэнды, сервисы реального времени и виртуализированные стеки извлекают выгоду из этой согласованности. Я тестирую связь под производственной нагрузкой до тех пор, пока потоки, SoftIRQ и пользовательское пространство не начнут четко согласовываться друг с другом.

Автоматика против точной настройки: как правильно использовать irqbalance

Служба irqbalance автоматически распределяет прерывания по доступным ядрам, что хорошо работает на универсальных серверах. Однако в сетевых конфигурациях с высокой нагрузкой такое распределение снижает локальность кэша и затрудняет целенаправленную привязку. Я ограничиваю работу irqbalance или выборочно отключаю его, если определённым очередям требуются фиксированные ядра. Для понимания основ и подбора подходящих профилей мне помогает это руководство по Настройка irqbalance. В результате система автоматически управляет некритическими IRQ, а я вручную назначаю чувствительные IRQ.

Шаг: Отображение соответствующих IRQ

Начну с обзора /proc/interrupts и отфильтровать по названию устройства, например ens192, eno1 или eth0. Современные адаптеры создают несколько очередей RX и TX, поэтому я выделяю группу номеров IRQ, привязанных к одному и тому же сетевому адаптеру. Я обращаю внимание на значения счетчиков, чтобы быстро выявлять «горячие точки» и в первую очередь связывать сильно загруженные очереди. Я регулярно проверяю этот показатель во время нагрузочных тестов, чтобы обеспечить стабильность распределения. Кроме того, я проверяю названия драйверов, поскольку они дают подсказки о поддержке RSS и отгрузке.

# Просмотр всех прерываний
cat /proc/interrupts

# Отображение только строк, относящихся к сетевым картам (пример: ens192)
grep -i ens192 /proc/interrupts

Эффективное использование топологии NUMA

На хостах с несколькими узлами я предпочитаю переносить IRQ на ядра тех, NUMA-узел, к которому физически подключена сетевая карта. Я проверяю это с помощью lscpu и numactl --hardware и отмечаю подходящие наборы процессоров для последующего формирования масок. Процессы, использующие эти сетевые пути, я также привязываю к тому же узлу и с помощью политик использования памяти обеспечиваю локальное Память-присвоения. Таким образом я избегаю дорогостоящих удаленных обращений через каналы QPI/UPI. Такая дисциплина быстро приносит ощутимые преимущества в тестах на задержку.

Как правильно выбирать битовые маски: шестнадцатеричная логика вкратце

Файл smp_affinity поддерживает шестнадцатеричные битовые маски, которые напрямую сопоставляются с идентификаторами ядер и охватывают даже крупные системы. Я часто начинаю с простых шаблонов: CPU0 — 0x1, CPU1 — 0x2, CPU2 — 0x4, CPU3 — 0x8 и т. д., тогда как 0xF охватывает ядра 0–3. На машинах с большим количеством ядер я записываю несколько 32-битных блоков, разделенных запятыми, чтобы Маска точно отображает все идентификаторы. Эта сводка помогает мне производить правильное сопоставление и избегать непреднамеренных переносов. Следующую таблицу я часто использую в качестве памятки.

Номер процессора Бит (двоичный) Маска Hex Подсказка
0 …0001 0x1 CPU0 часто снимать нагрузку и использовать с умеренной интенсивностью.
1 …0010 0x2 IRQ на CPU1 управлять.
2 …0100 0x4 Подключить IRQ к CPU2.
3 …1000 0x8 Подключить IRQ к CPU3.
0–3 …1111 0xF Если включить все четыре ядра, задержка часто немного увеличивается.
0–7 11111111 0xFF Широкое распределение, страдает локальность кэша.

Настройка IRQ-аффинности: как привязать очереди к ядрам

После того как я определил IRQ-очереди сетевой карты, я распределяю их по выделенным Ядра такие как 1, 2 и 3, чтобы обеспечить плавное масштабирование параллельной обработки. Таким образом, каждая очередь RX/TX остается привязанной к своему ядру, что позволяет избежать перекрестных помех и обеспечить согласованность работы SoftIRQ. В процессе доработки я сравниваю пропускную способность и задержку, пока распределение не станет надежным. Для более глубокого понимания мне помогает краткий Практическое руководство с различными вариантами в зависимости от адаптера. Я намеренно запускаю эти команды в окнах технического обслуживания и фиксирую их с помощью скрипта загрузки.

Пример #: привязка трёх IRQ-очередей к процессорам CPU1–3
echo 2 > /proc/irq/181/smp_affinity   # CPU1 (0x2)
echo 4 > /proc/irq/182/smp_affinity   # CPU2 (0x4)
echo 8 > /proc/irq/183/smp_affinity   # CPU3 (0x8)

Процессорное фиксирование: размещение сервисов на одних и тех же ядрах

Я привязываю рабочие процессы приложения к тем Процессоры, которые обрабатывают соответствующие IRQ, чтобы пути передачи данных оставались короткими. В systemd я использую CPUAffinity=1 2 3 или запустите один раз с помощью taskset -c 1-3. Для серверов с несколькими рабочими процессами я назначаю фиксированные наборы ядер для каждой группы рабочих процессов, чтобы избежать конкуренции. Такая привязка обеспечивает стабильно более низкие Задержки, поскольку кэш-память процессора остается заполненной. После внесения изменений я проверяю потоки, сокеты и SoftIRQ с помощью htop, ss и perf.

Настройка сети: связь между RSS, RPS/RFS и параметрами ядра

Многие сетевые карты передают пакеты посредством RSS о очередях, которые я затем привязываю к ядрам с помощью IRQ-аффинности. Подробности и принципы действия я изложу в разделе Масштабирование на стороне приема в компактном виде. Кроме того, я управляю RPS/RFS таким образом, чтобы SoftIRQ не противоречили жесткой логике фиксации выводов. Параллельно с этим я настраиваю буферы через net.core.rmem_max и net.core.wmem_max и проверьте такие опции TCP, как tcp_timestamps. Эти компоненты способствуют достижению одной и той же цели: низкой задержки при высокой Скорость передачи данных.

Правильное толкование MSI‑X и структуры очереди

Современные сетевые адаптеры используют MSI‑X и создают отдельные IRQ для каждой очереди RX/TX. Сначала я проверяю, сколько очередей и каналов в данный момент активировано драйвером, и подстраиваю это под бюджет ядер соответствующего узла NUMA. Таким образом я предотвращаю ситуацию, когда слишком много очередей сосредотачивается на слишком малом количестве ядер или, наоборот, пропускная способность остается неиспользованной.

#: проверка и настройка количества очередей и каналов
ethtool -l ens192 # текущие ограничения (RX/TX/combined)
ethtool -L ens192 combined 4  #, например, активировать 4 очереди

# Проверка настроек RSS-Indirection и хэша
ethtool -x ens192 # Отобразить таблицу Indirection и хеш-ключ

Многие драйверы присваивают IRQ-кодам понятные названия (например,. ens192-TxRx-0). Я поддерживаю согласованность таблицы Indirection с распределением ядра, чтобы потоки стабильно попадали в „свои“ очереди. Если распределение аппаратных ресурсов отличается от этого, возникают ненужные перемещения SoftIRQ.

Грамотное использование RPS/RFS и XPS

RPS/RFS может распределять пакеты программным способом между ядрами — это хорошо для сетевых карт без большого количества очередей, но контрпродуктивно, если я уже использую точную привязку с помощью RSS и IRQ-Affinity. Поэтому я сознательно принимаю решение: либо жесткая привязка с помощью RSS+IRQ-Affinity, либо RPS выключено, либо небольшое количество аппаратных очередей и Целенаправленное применение RPS. Кроме того, я настраиваю XPS для направления TX, чтобы исходящие пакеты отправлялись с „нужных“ ядер.

IF=ens192

# Полная отключение RPS (при корректной привязке IRQ через RSS)
for q in /sys/class/net/$IF/queues/rx-*/rps_cpus; do echo 0 > "$q"; done
echo 0 > /proc/sys/net/core/rps_sock_flow_entries

# Альтернативный вариант: выборочная активация RPS (пример: процессоры 1–3)
for q in /sys/class/net/$IF/queues/rx-*/rps_cpus; do echo 0-0,0-0,0-0,0-0 > /dev/null; done
# Лучше: использовать синтаксис, аналогичный smp_affinity_list:
for q in /sys/class/net/$IF/queues/rx-*/rps_cpus; do echo 1-3 > "$q"; done
echo 32768 > /proc/sys/net/core/rps_sock_flow_entries
echo 32768 > /proc/sys/net/ipv4/tcp_rfs_sock_flow_entries

Установить значение # XPS в соответствии с выбранными рабочими процессорами (TX-пути)
for q in /sys/class/net/$IF/queues/tx-*; do echo 1-3 > "$q"/xps_cpus; done

Важна согласованность: ядро RX‑IRQ, нагрузка ksoftirqd и рабочий поток должны располагаться на одном ядре (или паре ядер). Это позволяет устранить многие эффекты «кросс-кор-баунс».

Учёт SMT/Hyper-Threading и пар ядер

В системах с SMT часто целесообразно зарезервировать одно физическое ядро для RX‑IRK и разместить соответствующий рабочий процесс на Дочерний поток объединить — или сознательно разделить, если рабочая нагрузка требует интенсивных вычислений. Я определяю пары потоков на основе топологии, а затем принимаю четкое решение вместо случайного распределения.

Определение пар "# Sibling"
for c in /sys/devices/system/cpu/cpu*/topology/thread_siblings_list; do
  echo "$(basename "$(dirname "$c")") : $(cat "$c")"
done

Если распределить RX-IRQ и рабочие процессы пользовательского пространства на одном физическом ядре (разные SMT-потоки), то обеспечивается хорошая локальность L1/L2, но при нагрузках, ограниченных производительностью ЦП, могут возникать узкие места. В качестве альтернативы я распределяю IRQ на ядро X, а рабочий процесс — на ядро Y в рамках одного и того же узла NUMA, чтобы обеспечить настоящий параллелизм. Оба варианта я тестирую методом A/B-тестирования и выбираю тот, который обеспечивает более стабильную задержку.

Использование smp_affinity_list, effective_affinity и Defaults

Помимо шестнадцатеричных масок, я люблю писать в smp_affinity_list, поскольку это позволяет охватить такие области, как 1-3,6,8-9 удобно установить. Для проверки я проверяю effective_affinity соответственно список_эффективных_аффинностей, поскольку ядро или драйверы могут исключать определенные процессорные ядра (например, ядра, отключенные от работы, или „управляемые прерывания“).

# Присвоение в удобочитаемом виде
echo 1-3 > /proc/irq/181/smp_affinity_list

# Проверка эффективной привязки
cat /proc/irq/181/effective_affinity_list

Чтобы новые IRQ или те, которые появились после перезагрузки драйверов, не распределялись слишком широко, я при необходимости устанавливаю /proc/irq/default_smp_affinity до разумного базового значения (например, все ядра соответствующего NUMA-узла, но без CPU0). Затем я целенаправленно переопределяю отдельные критические IRQ.

Обеспечение сохранности данных после перезагрузки и обновления страницы

Настройки Affinity являются эфемерными. Я сохраняю их с помощью модуля systemd-oneshot, который запускается после достижения цели инициализации сети, или с помощью небольшого скрипта, который динамически определяет и сопоставляет списки IRQ. Таким образом, сопоставления сохраняются даже после обновлений ядра и сброса соединений.

# /usr/local/sbin/net-irq-pin.sh (пример)
#!/bin/bash
set -euo pipefail
IF=${1:-ens192}
CPUS="1-3"   Целевые процессоры # (выбирать NUMA-согласованные)
for irq in $(grep -i "$IF" /proc/interrupts | awk '{print $1}' | tr -d ':'); do
  echo "$ CPU" > /proc/irq/$irq/smp_affinity_list || true
done

# Модуль systemd (в общих чертах)
# /etc/systemd/system/net-irq-pin.service
[Unit]
Description=Привязка IRQ сетевых карт
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/net-irq-pin.sh ens192

[Install]
WantedBy=multi-user.target

Важно: я запускаю скрипт заново, если драйвер был перезагружен или количество очередей изменилось, так как в этом случае номера IRQ сдвигаются.

Виртуализация и контейнеры: комплексное подход к хосту и гостю

В средах KVM я монтирую на Хозяин физические IRQ-сигналы сетевых карт на ядра соответствующего NUMA-узла. Параллельно я подключаю vhost-net‑потоки и процесс QEMU (или отдельные виртуальные процессоры) также размещаются там, чтобы пути передачи данных со стороны хоста оставались короткими. В Гость Я настраиваю IRQ-аффинность виртуальных сетевых карт (vNIC) на те виртуальные процессоры (vCPU), которые я сопоставил на стороне хоста с физическими ядрами. Контейнерные рабочие нагрузки (cgroups/cpuset) получают преимущество, если разрешённые процессоры контейнеров пересекаются с ядрами RX/TX хоста — в противном случае возникают излишние удалённые обращения.

Углубленный анализ: SoftIRQ, NAPI и обнаружение заторов

Кроме того, /proc/interrupts я заглядываю в /proc/softirqs, чтобы понять, много ли работы в контексте ksoftirqd вместо того, чтобы выполняться непосредственно в обработчике IRQ — это свидетельствует о постоянно высокой нагрузке. С помощью napi_defer_hard_irqs (В зависимости от ядра) и с помощью правильного распределения очередей я регулирую, насколько активно NAPI выполняет пакетную обработку. ethtool -S предоставляет мне статистику по каждому очереди относительно потерь, состояний «занято» и скорости передачи пакетов; благодаря этому я выявляю несбалансированные очереди и соответствующим образом корректирую аффинность или RSS-индирекцию.

# Краткий обзор распределения SoftIRQ
cat /proc/softirqs | egrep 'NET_RX|NET_TX'

# Просмотр статистики драйверов и очередей
ethtool -S ens192 | egrep -i 'rx|tx|drop|busy'

Практика: распределение 4 очередей по одному узлу

Типичная конфигурация, которую я часто использую: сетевая карта на NUMA-узле 0 с 4 очередями RSS. Я избегаю использования CPU0 и привязываю очереди к CPU1–4. Соответствующие веб- или прокси-рабочие процессы я также размещаю на 1–4, XPS назначаю аналогичным образом, а RPS оставляю отключенным. Таким образом я получаю короткие и стабильные пути в обоих направлениях.

IF=ens192
QUEUES=(181 182 183 184)   # Примеры IRQ (определить заранее)
CPUS="1-4"

Установить аффинность IRQ и XPS для #
for i in ${!QUEUES[@]}; do
  echo "$CPUS" > /proc/irq/${QUEUES[$i]}/smp_affinity_list
done
for q in /sys/class/net/$IF/queues/tx-*; do echo "$CPUS" > "$q"/xps_cpus; done

# Привязка рабочих процессов (systemd или taskset)
# systemd: CPUAffinity=1 2 3 4
# Однократно: taskset -c 1-4

Если нагрузка будет продолжать расти, я увеличу количество очередей (ethtool -L) до количества целесообразных ядер узла и распределяйте их строго по узнаваемому шаблону (например, ID очереди → ID ядра), чтобы потоки не „перемещались“ с течением времени.

Передовые методы для многоядерных хостов

Я снимаю нагрузку CPU0, поскольку там часто запущены таймеры и внутренние службы ядра, которые при высокой нагрузке создают помехи. Поэтому я предпочитаю назначать критические IRQ на другие ядра, а на CPU0 оставляю лишь несколько некритических источников. На системах NUMA я следую этому принципу последовательно и размещаю адаптеры, IRQ, процессы и обращения к памяти на одном и том же Узел. В условиях высокой нагрузки я разделяю ядра ввода-вывода и ядра приложений и, при необходимости, изолирую их. Все изменения я сопровождаю постоянным мониторингом и итеративно корректирую распределение ресурсов.

Подход, основанный на измеримых показателях: анализ, сценарии и план действий на случай непредвиденных обстоятельств

Перед внесением изменений я фиксирую текущее состояние с помощью mpstat, htop, /proc/interrupts и измерения задержки с помощью iperf3. Я настраиваю скрипты, которые автоматически применяют настройки аффинности после перезагрузки или перезагрузки драйверов. На случай необходимости отката я готовлю нейтральные шаблоны, чтобы при возникновении сбоев сразу же вернуться к исходному состоянию. В тестовой среде я тестирую профили нагрузки, максимально приближенные к моей производственной среде, и повторяю Измерение после каждой настройки. Только после этого я окончательно активирую профиль на целевом хосте.

Обходите часто встречающиеся камни преткновения

Слишком широкие маски распределяют нагрузку между слишком большим количеством Процессоры и замедляют работу кэшей, в то время как слишком узкие маски перегружают очереди. Неучтенные особенности NUMA приводят к обращениям к удаленной памяти, что вызывает колебания времени отклика. Жёсткая привязка иногда вступает в конфликт с настройками RPS/RFS, поэтому я явно проверяю распределение нагрузки SoftIRQ. После обновлений ядра или смены драйверов я заново проверяю все номера IRQ, поскольку сопоставления могут измениться. Осторожные действия и чёткое Документальный фильм я сохраняю способность действовать.

Краткое резюме

Целенаправленная фиксация IRQ позволяет привязать сетевые прерывания к нескольким подходящим Ядра, снижает накладные расходы и стабилизирует время отклика. Для этого я настраиваю IRQ и аффинность процессора, учитываю NUMA и проверяю эффективность с помощью серий измерений. Там, где достаточно автоматики, я позволяю работать irqbalance, а критические очереди направляю на фиксированные ядра. С помощью RSS, RPS/RFS и настроенных параметров ядра настройка раскрывает весь свой потенциал. Эффект. Те, кто дисциплинированно выполняет эти шаги, смогут значительно повысить сетевую производительность на многоядерных серверах под управлением Linux.

Текущие статьи

Общие сведения

Хороши ли совместимые картриджи для принтеров? Анализ качества, ресурса и технологий

Совместимые картриджи от проверенных поставщиков обеспечивают качество печати и ресурс, очень близкие к оригинальным — при этом по цене, в разы ниже. Решающим фактором является не

Общие сведения

Как снизить расходы на электроэнергию при работе из дома: какую роль действительно играют техническая конфигурация и тарифный план

Тем, кто хочет сократить расходы на электроэнергию при работе из дома, следует воспользоваться четырьмя способами: измерять потребление, правильно настроить устройства, постоянно работающие в режиме ожидания, такие как NAS и домашняя лаборатория, а также переключать нагрузки на тарифы с более выгодными

Общие сведения

Почему компании отсутствуют в результатах поиска по ИИ: какие настройки серверов и ботов незаметно блокируют доступ для сканеров

Многие сайты блокируют сканеры, даже не подозревая об этом: системы управления ботами, правила WAF или жесткие ограничения частоты запросов возвращают определенным пользовательским агентам код 403. Для Googlebot это означает