Linux. OOM

Материал из Wiki
Версия от 22:29, 26 ноября 2024; Cless (обсуждение | вклад)
(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к навигации Перейти к поиску


Что такое OOM Killer и как он работает

OOM Killer, пожалуй, одна из немногих технологий в Linux, овеянная мифами и легендами. Часто из уст в уста передаются истории, претендующие на хороший триллер. На самом деле все гораздо проще и прозаичнее, а работа OOM Killer подчиняется строгим правилам. Cегодня мы попробуем разобраться в том, что такое OOM Killer и для чего он предназначен, а также разберемся как он работает и по каким критериям выбирает процессы для завершения. Поэтому, если OOM Killer для вас все еще мифический и непонятный черный ящик, то обязательно прочитайте эту статью.

Начнем с начала, т.е. с выделения памяти в Linux. Любой запущенный процесс получает собственный выделенный участок памяти. Но этот участок не отображается на доступные физические адреса, а использует виртуальное адресное пространство, которое является дополнительным слоем абстракции между процессом и физической памятью. Для сопоставления адресов между виртуальной и физической памятью используются специальные структуры - таблицы страниц.

Что это дает? Во-первых, изоляцию процессов друг от друга, каждый из них работает в собственном виртуальном адресном пространстве и не имеет доступа к памяти других процессов. Во-вторых, позволяет системе гибко управлять физической памятью, например, для неактивного процесса часть страниц может быть перенесено из оперативной памяти в пространства подкачки, но это никак не повлияет на работу самого процесса, его адресное пространство не изменится, поменяется лишь трансляция в таблице страниц. И, наконец, это позволяет экономить физическую память, так если несколько процессов используют одни и те же данные, то система может транслировать адреса виртуальных пространств на один и тот же участок физической памяти.

Таким образом размер виртуальной памяти в системе теоретически ничем не ограничен и общий ее объем может существенно превышать имеющуюся в наличии физическую память. И в этом нет ничего плохого, например, мы запустили несколько графических приложений, для работы которым требуются библиотеки Qt или GTK, но нужно ли каждый раз загружать их в память? Нет, достаточно сделать это один раз, а затем просто транслировать адреса виртуальной памяти процессов на одни и те же участки физической памяти. Но при этом каждый процесс будет думать, что у него загружена собственная копия библиотек.

Поэтому современные Linux-системы выделяют память процессам с превышением имеющейся в наличии физической памяти (RAM + Swap), по умолчанию используя эвристические механизмы. Но случается, что процессы съедают всю доступную физическую память и вот тут системе надо выбрать что-то одно. А выбор, скажем честно, между плохим или очень плохим. Можно убить один из процессов и продолжить работать дальше, а можно вызвать Kernel panic. Понятно, что последнее - это совсем ни в какие ворота, поэтому на сцену выходит OOM Killer, это поведение используется в современных Linux системах по умолчанию.

Но кого же убьет OOM Killer? Бытует ошибочное мнение, что самый "толстый" процесс, занимающий самое большое количество памяти, однако это не так. Хотя заблуждения живучи, и народная молва рисует OOM Killer в виде ковбоя, врывающегося в переполненный салун и открывающий беспорядочную стрельбу по посетителям, преимущественно по толстым. Почему? Да попасть, наверное, проще...

На самом деле OOM Killer использует достаточно сложный анализ и присваивает каждому процессу очки негодности (badness) и при исчерпании памяти будет убит не кто-попало, а самый негодный процесс. Очки негодности могут принимать значения от -1000 до 1000, чем выше значение, тем более высока вероятность что OOM Killer убьет процесс. Процесс с -1000 очков никогда не будет убит OOM Killer.

Как вычисляются очки негодности? За основу берется процент физической памяти используемый процессом и умножается на 10, таким образом получаем базовое значение. Нетрудно заметить, что 1000 очков - это 100% использования памяти. Затем к данному значению начинают применяться различные модификаторы:

Прибавляется половина очков всех дочерних процессов, имеющих собственную виртуальную память.

Если приоритет процесса больше нуля очки умножаются на два. Приоритет может иметь значение от -20 (наивысший приоритет) до 20 (самый низкий).

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

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

Для процессов, запущенных от имени root, обслуживающих систему или процессов ввода-вывода очки делятся на 4.

Для процесса, при выделении памяти которому произошла ошибка out of memory и процессам имеющим с ним общую память, очки делятся на 8.

Очки умножаются на 2^oom_adj, где oom_adj - специальный коэффициент, имеющий значения от -17 до 15, при -17 процесс никогда не будет убить OOM Killer.

Если внимательно изучить эти критерии, то становится ясно, что:

  !!!

  OOM Killer убьет самый "толстый" процесс, который наименее активен в системе и имеет самое короткое время жизни

  !!!

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

Не так давно нам пришлось разбираться с ситуацией: в Linux Mint начал подвисать и падать браузер Chrome. Анализ ситуации выявил, что пользователь поставил стороннее расширение, которое приводило к утечке памяти и зависанию браузера, после чего на сцену выходил OOM Killer и убивал процесс. При этом параллельно были открыты процессы, которые потребляли больше памяти, чем утекало через Chrome, но OOM Killer их не трогал.

В системах виртуализации это будет неактивная виртуалка с наибольшим выделением памяти. Поэтому если у вас регулярно выключается одна из второстепенных машин - проверьте ситуацию с памятью, скорее всего это работа OOM Killer.

Как узнать количество очков, присвоенных процессу? Довольно просто, выполните команду:

    cat /proc/PID/oom_score

Где PID - идентификатор процесса. При необходимости мы можем изменить количество очков используя коэффициент oom_adj:

    echo -17 > /proc/PID/oom_adj

В данном случае мы отправили процессу с указанным PID значение -17, т.е. обеспечили ему полную защиту от OOM Killer.

Все это имеет один большой недостаток: применить коэффициент oom_adj мы можем только к запущенному процессу и по факту это ручная работа. Не следует применять эти методы на практике, кроме, разве что диагностики и отладки.

В современных системах для управления службами используется systemd, который дает нам в руки простые и эффективные инструменты для управления очками негодности. Для этого в секцию [Service] юнита службы добавьте опцию:

  [Service]

  ...

  OOMScoreAdjust=-500

  ...

Значение OOMScoreAdjust показывает насколько следует изменить количество очков процесса относительно рассчитанного количества, в нашем примере мы уменьшили его на 500. Таким образом мы можем защитить важные службы вашей системы от убийства со стороны OOM Killer, но не стоит злоупотреблять этой опцией, в критической ситуации будет лучше, если OOM Killer убьет даже важный процесс, но сохранит вам доступ к системе, нежели вы окажетесь в ситуации с полным исчерпанием физической памяти и недоступности или перезагрузки вашего сервера.

В любом случае, если вы столкнулись с работой OOM Killer, то следует тщательно проанализировать причины нехватки физической памяти и принять необходимые меры.

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