Почему моя система Linux заикается, если я постоянно не сбрасываю кеш?

441
Pneumaticat

За последние несколько месяцев у меня возникла чрезвычайно раздражающая проблема с моей системой Linux: она заикается при воспроизведении аудио в Firefox, перемещении мыши и т. Д., С небольшим скачком в секунду (но все же заметным) каждые несколько секунд. Эта проблема усугубляется при заполнении кеша памяти или при запущенных программах, интенсивно использующих диск / память (например, программное обеспечение для резервного копирования restic). Однако, когда кеш не заполнен (например, при очень небольшой нагрузке), все работает очень гладко.

Просматривая perf topрезультаты, я вижу, что list_lru_count_oneу них высокие накладные расходы (~ 20%) в эти периоды задержки. htopтакже показывает kswapd0использование процессора на 50-90% (хотя кажется, что влияние намного выше). В периоды сильной задержки htopизмеритель ЦП часто зависит от использования ЦП ядра.

Единственный найденный мной обходной путь - либо заставить ядро ​​оставить свободную память ( sysctl -w vm.min_free_kbytes=1024000), либо непрерывно сбрасывать кеши памяти echo 3 > /proc/sys/vm/drop_caches. Конечно, ни один из них не идеален, и ни один не полностью решает заикание; это только делает это менее частым.

У кого-нибудь есть идеи о том, почему это может происходить?

Системная информация

  • i7-4820k с 20 ГБ (несовпадающей) оперативной памяти DDR3
  • Воспроизводится в Linux 4.14-4.18 в нестабильной среде NixOS
  • Запускает Docker-контейнеры и Kubernetes в фоновом режиме (что, как я чувствую, не должно создавать микрострукание?)

Что я уже пробовал

  • Изменение планировщиков ввода / вывода (bfq) с использованием многозадачных планировщиков ввода / вывода
  • Использование -ckпатча от Con Kolivas (не помогло)
  • Отключение подкачки, изменение подкачки, использование zram

РЕДАКТИРОВАТЬ : Для ясности, вот изображение htopи perfво время такого скачка задержки. Обратите внимание на высокую list_lru_count_oneзагрузку ЦП и kswapd0+ высокую загрузку ЦП ядра.

htop and perf output

2
Настройте использование процессора / io с помощью cgroup. Уделять больше внимания интерактивным задачам. Ipor Sircer 5 лет назад 0
Я испытываю то же самое в моей системе (без контейнеров). Я подозреваю, что какая-то ошибка приводила к кешу, но я не знаю. Я был бы чрезвычайно заинтересован в ответе. Я также попытался изменить планировщики ввода / вывода, не помогло. Система приостанавливается даже при очень легкой загрузке, поэтому приоритет для интерактивных задач определенно не поможет. dirkt 5 лет назад 0
Does the same thing happen when using just one of the ram sticks? You mention it happens even when lightly loaded, so its testable. There might be a subtle timing / electrical issue from the different RAM Christopher Hostage 5 лет назад 0
@ChristopherHostage Я только что попробовал сегодня только с 2/4 ОЗУ, и он все еще демонстрировал заикание. Pneumaticat 5 лет назад 0
@IporSircer Кстати, я не думаю, что это расстановка приоритетов ЦП; заикания совпадают с высокой загрузкой * ядра * процессора, а не с другими программами. Поправь меня, если я ошибаюсь. Pneumaticat 5 лет назад 0

2 ответа на вопрос

2
Austin Hemmelgarn

Похоже, вы уже попробовали многие вещи, которые я бы предложил вначале (настройка конфигурации подкачки, изменение расписаний ввода-вывода и т. Д.).

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

  • vm.dirty_ratio: Контролирует, сколько записей должно быть отложено для обратной записи, прежде чем она будет запущена. Обрабатывает обратную запись переднего плана (для каждого процесса) и выражается в виде целого процента ОЗУ. По умолчанию 10% оперативной памяти
  • vm.dirty_background_ratio: Контролирует, сколько записей должно быть отложено для обратной записи, прежде чем она будет запущена. Обрабатывает фоновую (общесистемную) обратную запись и выражается в виде целого процента ОЗУ. По умолчанию 20% оперативной памяти
  • vm.dirty_bytes: То же самое vm.dirty_ratio, за исключением выраженного как общее количество байтов. Либо это, либо vm.dirty_ratio будет использоваться, в зависимости от того, что было написано до конца.
  • vm.dirty_background_bytes: То же самое vm.dirty_background_ratio, за исключением выраженного как общее количество байтов. Либо это, либо vm.dirty_background_ratio будет использоваться, в зависимости от того, что было написано до конца.
  • vm.dirty_expire_centisecs: Сколько сотых долей секунды должно пройти, прежде чем начнется отложенная обратная запись, когда вышеуказанные четыре значения sysctl еще не сработают. По умолчанию 100 (одна секунда).
  • vm.dirty_writeback_centisecs: Как часто (в сотых долях секунды) ядро ​​будет оценивать грязные страницы для обратной записи. По умолчанию 10 (одна десятая секунды).

Итак, со значениями по умолчанию, каждую десятую секунды ядро ​​будет делать следующее:

  • Запишите любые измененные страницы в постоянное хранилище, если они были последний раз изменены более секунды назад.
  • Запишите все измененные страницы для процесса, если его общий объем измененной памяти, который не был записан, превышает 10% ОЗУ.
  • Запишите все измененные страницы в системе, если общий объем измененной памяти, который не был записан, превышает 20% ОЗУ.

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

В настоящее время общее согласие состоит в том, vm.dirty_ratioчтобы установить 1% ОЗУ и vm.dirty_background_ratio2%, что для систем с менее чем 64 ГБ ОЗУ приводит к поведению, эквивалентному тому, что изначально планировалось.

Некоторые другие вещи, чтобы посмотреть на:

  • Попробуйте vm.vfs_cache_pressureнемного увеличить sysctl. Это контролирует, насколько агрессивно ядро ​​восстанавливает память из кэша файловой системы, когда ему требуется оперативная память. По умолчанию 100, не опускайте его на что - нибудь ниже 50 (вы будете получать действительно плохое поведение, если вы идете ниже 50, в том числе условия OOM), и не поднимать его гораздо больше, чем около 200 (гораздо выше, и ядро будет тратить время, пытаясь восстановить память, которую он действительно не может). Я обнаружил, что увеличение его до 150 на самом деле заметно улучшает скорость отклика, если у вас достаточно быстрое хранилище.
  • Попробуйте изменить режим переполнения памяти. Это можно сделать, изменив значение vm.overcommit_memorysysctl. По умолчанию ядро ​​будет использовать эвристический подход, чтобы попытаться предсказать, сколько ОЗУ он может выделить. Установка этого значения в 1 отключает эвристику и заставляет ядро ​​действовать так, как будто оно имеет бесконечную память. Если установить значение 2, ядро ​​не будет выделять больше памяти, чем общий объем пространства подкачки в системе, плюс процент фактической оперативной памяти (контролируемой vm.overcommit_ratio).
  • Попробуйте настроить vm.page-clustersysctl. Это контролирует, сколько страниц будет выгружено или выгружено за раз (это логарифмическое значение base-2, поэтому значение по умолчанию 3 переводит в 8 страниц). Если вы на самом деле меняете местами, это может помочь повысить производительность обмена страницами.
Спасибо за подробные предложения! К сожалению, ни один из них не работает для меня. Я попытался изменить коэффициент грязи, нагрузку на кэш VFS и кластер страниц безрезультатно. vm.overcommit_memory = 1 не изменилось, а = 2, в основном, привело к сбою системы, так что это тоже нельзя. Pneumaticat 5 лет назад 0
Do you have KSM or THP enabled? If so, I would suggest disabling them and seeing if that helps at all. Austin Hemmelgarn 5 лет назад 0
Нет, ни один не включен. На самом деле THP явно отключен через «transparent_hugepage = never», но я попытался включить его снова, и это не имело значения. Pneumaticat 5 лет назад 0
0
Pneumaticat

Проблема была найдена!

Оказывается, что это проблема с производительностью в памяти Linux, когда имеется большое количество контейнеров / групп памяти. (Отказ от ответственности: моё объяснение может быть ошибочным, я не разработчик ядра.) Эта проблема была исправлена ​​в 4.19-rc1 + в этом наборе патчей :

Этот набор исправлений решает проблему с медленным shrink_slab (), возникающим на машинах, имеющих много сжатых машин и cgroups памяти (то есть, со многими контейнерами). Проблема в том, что shrink_slab () имеет сложность O (n ^ 2) и растет слишком быстро с ростом числа контейнеров.

Пусть у нас будет 200 контейнеров, и у каждого контейнера 10 монтирований и 10 групп. Все задачи контейнера изолированы и не затрагивают монтирование сторонних контейнеров.

В случае глобального восстановления, задача должна выполнить итерацию по всем memcgs и вызвать все усматривающие memcg сокращения для всех из них. Это означает, что задача должна посещать 200 * 10 = 2000 сокращателей для каждой memcg, и, поскольку существует 2000 memcgs, общее количество вызовов do_shrink_slab () составляет 2000 * 2000 = 4000000.

Моя система пострадала особенно сильно, так как я запустил большое количество контейнеров, что, вероятно, и стало причиной появления проблемы.

Мои шаги по устранению неполадок, в случае, если они полезны для тех, кто сталкивается с подобными проблемами:

  1. Обратите внимание на kswapd0использование тонны процессора, когда мой компьютер заикается
  2. Попробуйте остановить контейнеры Docker и снова заполнить память → компьютер не заикается!
  3. Run ftrace(после великолепного объяснения блога Julia Эванса ), чтобы получить след, видеть, что kswapd0имеет тенденцию застревать в shrink_slab, super_cache_countи list_lru_count_one.
  4. Google shrink_slab lru slow, найди набор патчей!
  5. Переключитесь на Linux 4.19-rc3 и убедитесь, что проблема устранена.

Похожие вопросы