Sunday, 3 May 2009

Про память: OOM Killer

Начинаю новый цикл «про память». Первой темой будет магический OOM Killer.

OOM Killer — это способ ядра решить проблему, когда памяти недостаточно. Известно, что виртуальной памяти может быть бесконечно много (в пределах адресации), а вот физической — вполне конечное число. Иногда процессы системы съедают ее всю, и системе надо кого‑то убить, чтобы продолжить работу. Текущая реализация OOM Killer в Linux стремится выбрать наименее важный процесс. Он выбирает среди всех процессов, кроме init и kernel threads, самый негодный (badness).

Алгоритм расчета уровня негодности процесса (итоговое значение будет измеряться в очках негодности (badness ponts)):

  1. Берется размер виртуальной памяти процесса (total_vm). Это базовые очки негодности (mm/oom_kill.c:69).

  2. К текущим очкам прибавляется total_vm/2 + 1 для всех порожденных процессов (mm/oom_kill.c:85).

  3. Текущие очки делятся на int_sqrt(cpu_time), где cpu_time — это user + system время процесса сдвинутое вправо на SHIFT_HZ + 3, т.е. для HZ=1000 приблизительно будет равен значению int_sqrt((utime+stime)/10). причем если результат деления и последующего округления будет 0 — то очки не изменяются (mm/oom_kill.c:100).

  4. Текущие очки делятся на int_sqrt(int_sqrt(run_time/1024)), где run_time — время прошедшое с момента запуска процесса. Если результат 0 — то очки не изменяются (mm/oom_kill.c:100).

  5. Очки умножаются на 2, если nice процесса больше 0 (mm/oom_kill.c:118).

  6. Если процесс имеет привилегию CAP_SYS_ADMIN или CAP_SYS_RESOURCE или (e)uid в нуле, то текущие очки делятся на 4 (mm/oom_kill.c:125).

  7. Если процесс имеет привилегию CAP_SYS_RAWIO, то текущие очки делятся на 4 (mm/oom_kill.c:133).

  8. Если память процесса, для которого мы считаем очки негодности, пересекается с памятью процесса, для которого в момент выделения новой памяти произошла ошибка out of memory, тогда очки делятся на 8 (для ядер старше 2.6.28, mm/oom_kill.c:142).

  9. Набранные очки умножаются на 2oom_adj, где oom_adj — берется из /proc/$PID/oom_adj, он может принимать значения от -17 до 15. В случае значения -17 процесс не будет тронут OOM Killer (mm/oom_kill.c:150).

И самый негодный (с самыми большими очками) процесс будет убит.

Небольшие пояснения.

  1. При расчете дочерних total_vm учитываются только процессы с самостоятельной виртуальной памятью. Т.е. не потоки.
  2. Предполагается, что если приоритет больше нуля, то выполнение этого процесса менее критично, чем выполнение процессов с отрицательным приоритетом.
  3. Предполагается, что root-процессы важнее, чем процессы непривилегированных пользователей.
  4. Убийство процессов, которые осуществляют прямую работу с устройствами, может повлечь за собой нежелательные последствия.
  5. OOM Killer стремится убивать более молодые процессы. Это надо, чтобы OOM Killer убил только что запущенный процесс с утечкой памяти и не тронул старые, добротные процессы, которые просто кушают много памяти ☺
  6. OOM Killer стремится сохранить жизнь процесса, при выделении памяти для которого произошла ошибка out of memory, и процессам, у которых с ним есть общая память (для ядер старше 2.6.28).

Доступные пользователю настройки:

  • самый простой способ повлиять на OOM Killer — использовать vm.overcommit_memory;
  • vm.oom_dump_tasks — делать dump всех процессов за исключением kernel threads, в dump попадает pid, uid, tgid, vm size, rss, cpu и oom_adj. Имеет смысл включать только для отладки OOM Killer;
  • vm.oom_kill_allocating_task — убивать процесс, процесс которому не хватило памяти, без выбора самого плохого;
  • vm.panic_on_oom — считать запуск OOM критической ошибкой.

Write on: 14:46 | 46 comments | | tags: , , , | permalink |
Add post to:   Delicious Reddit Slashdot Digg Technorati Google


Add comment

Pingbacks

ST Blog - Отключение OOM Killer в Ubuntu 12.04 @blog.st58.su 25.09.2014 23:54
http://catap.ru/blog/2009/05/03/about-memory-oom-killer/
Разработка VPN-клиента под Android (Часть 1) | Zit@i0 @wp.zitaio.tk 1.08.2014 18:28
В Linux есть такой замечательный механизм, как OOM Killer, и если вдруг пользователь запустит приложение, которое будет «пожирать» большое количество оперативной памяти устройства (для экспериментов можно взять Firefox, вот уж кому
Падение сервера и небольшое расследование | asidorov @asidorov.name 19.07.2014 3:37
Полезные ссылки:[1] HowTo: Speed UP Linux Software Raid Building And Re-syncing[2] Увеличение скорости перестроения программного RAID в Linux[3] Про память: OOM Killer
Про Linux и память (OOM-killer) | Блог им. Красного знамени | Заметки для себя @mkdev.ru 6.03.2013 10:59
Здесь описывается OOM-killer — один из способов решить проблему с нехваткой памяти в процессе выполнения. http://catap.ru/blog/2009/05/03/about-memory-oom-killer/
Отключение OOM Killer в Ubuntu 12.04 | Записки на полях @notes.ogloblin.net 27.12.2012 17:35
http://catap.ru/blog/2009/05/03/about-memory-oom-killer/
Rost's Blog - Overcommit memory @rostnix.co.cc 29.03.2011 23:07
P.S Про OOM Killer хорошо расписано тут : Про память: OOM Killer
Немного здорового бреда » Разбор memorykiller механизма в Android @tamerlan311.ru 12.03.2011 21:52
MemoryKiller – это модуль ядра, разработанный Google специально для Андройда, он является коллегой стандартного линуксового OOM Killer и использует его некоторые функции.
Недавно обнаружена дыра, способная намертво положить любую Linux систему. | drbatty.ru @blog.drbatty.ru 28.11.2010 7:27
приводит к появлению OOM Killer’а, который обычно выходит на сцену при нехватке памяти. По довольно сложному алгоритму, OOM выбирает самый ненужный процесс, и его уничтожает. Уничтожает он что угодно – у некоторых на LOR’е он уничтожил этот код, а у некоторых – его не трону

Comments

anonymous_user 3.05.2009 15:53

Всё чётко и понятно.. кроме одной вещи: в пунктах 3, 4 и 5 в случае получения нуля после округления очки е меняются. правильно ли я понял, что это значит следующее: допустим, количество очков было равно 49, вот мы делим это на 100, получаем 0.49,округляем, итого — ноль, значит количество очков остаётся 40? Но при этом же если было 51 очко -> 51/100 =0.51, то в результате будет единица, да? как-то это плохо.. изначальная разница в 2 очка в результате приводит к разнице в 38 очков.. я что-то не так понял?)

reply
Kirill A. Korinskiy 3.05.2009 16:19

Что не понятно в пункте 5?

Про пункт 3 и 4 записал в пояснение, спасибо.

reply
anonymous_user 3.05.2009 16:27

Боюсь это была опечатка)) имелись ввиду конечно же только 3 и 4 пункты. Пояснение осознал, спасибо.

reply
человек 3.05.2009 22:40

для чего, вообще этот страшный оом киллер? Можно, вообще, обойтись без него? Я, честно говоря, обрадовался, не найдя в параметрах ядра overcommit_memory, я думал, что эту систему убрали из ядра насовсем…

reply
юзер 4.05.2009 11:40

Как зачем? А что системе делать если кончилась физическая память? Падать в панику?

reply
человек 4.05.2009 23:33

как положено, сказать “памяти больше нет”, извините.

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

reply
ramok 10.05.2009 1:58

сравнивать файлы на диски и запущенные программы некорректно. если проводить аналогию то винда выдает тебе окошко “не хватает места на диске, не хотите почистить кое какие временные файлы?”

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

reply
человек 18.05.2009 12:13

Позолю себе не согласиться. 1) для рута резервируестя 5% памяти и диска и 2) Так прибейте ту прогу, которая в данный момент пытаестя писать в память. Ну нету у нас больше памяти. Если прога течет, она самоубъется с очень большой вероятностью и освободит всю свою память.

Так нет же. Пишущий процесс неприкосновенен.

Сорри за флейм…

reply
Kirill A. Korinskiy 18.05.2009 15:23

Не верно одно утверждение. Не все fs резервируют место под root. И этот резерв устанавливается при создание fs. В то время как для памяти он kernel-specific.

Вообще смерть процесса который пишет, и еще в raw-mode в устройства, может быть куда опасней для данных, что хранятся, чем просто процесса который спит, согласен?

reply
Ant0 4.05.2009 14:00

Очень сильно эта штука пригождается на embedded-устройствах, где нет свапа. В панику никак нельзя, железка должна работать!

reply
человек 4.05.2009 23:56

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

Зачем мне аллоцировать память, которую я не буду использовать? Как тот счетовод из “маленького принца”, что бы знать, что она у меня есть?

Если я, как программист, аллоцирую память, стало быть, я рассчитываю ее использовать. Если памяти нет, надо мне об этом сообщить. Иначе мне прийдется уже после malloc делать проверку, что в системе еще есть память перед каждой попыткой записи. Маразм.

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

Спасибо за параметр overcommit_ratio. Было не понятно, надо его ставить в 0 или в 1. Теперь все будет хорошо. :)

reply
Ant0 5.05.2009 17:46

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

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

и еще масса примеров, где это нужно… не нужно — вырубайте! благо есть такая возможность! система-то гибкая!

reply
Kirill A. Korinskiy 5.05.2009 20:41

Я дописал про настройки и написал отдельный пост про overcommit. Думаю этого будет достаточно что бы скорректировать поведение OOM Killer как вам хочется :)

А что касается примера с сриптом, то не факт что прибьется скрипт, может запросто подохнуть какой-нибудь торрент клиент ;)

reply
человек 6.05.2009 9:24

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

reply
Kirill A. Korinskiy 6.05.2009 13:07

vm.oom_kill_allocating_task ставишь в 1 и подохнет тот процесс, на выделение памяти которого произошел out of memory

reply
человек 18.05.2009 12:17

Век живи, век учись. Спасибо.

reply
Kirill A. Korinskiy 18.05.2009 15:23

Я где-то давал ссылку на то, с какой версии это появилось :)

reply
pavlenko 4.05.2009 16:04

Да, без него обойтись можно и другие ОС так и делают. Да и в Linux его вполне можно отключить.

OOM killer это всего лишь одна из вариантов решения проблемы виртуального пространства. И именно она принята в Linux по умолчанию.

Дело в том, что часто приложения аллоцируют намного больше памяти, чем им реально нужно. Если кратко, и не вдаваясьв детали, то работает это так : Когда прилоение просит выделить память (например используя malloc ) по получает ссылку на виртуальную страницу. Реальное же пространство выделяется только тогда, когда это пространство начинает использоваться.

Когда же приложение не использует выделенную ей память и памяти совсем мало то эти страницы перемещаются из оперативной памяти в swap.

Теперь давайте рассмотрим ситуацию, когда в swap уже нет места. В оперативной памяти его тоже нет, а приложение, ранее выделевшее себе кучу памяти наконец решило в него что-то записать. Аллоцировать эту память ядру не от куда. При этом сообщить приложению что нет памяти так невозможно, так как malloc на эту память запускался ранее и отработал без ошибок. Что же делать в такой ситуации? В linux это решается запуском OOM killer’а который убивает процессы, высвобождая память, необходимую нашему приложению для работы.

В других OS это реализовано иначе. Да и в linux можно сделать echo 2 > /proc/sys/vm/overcommit_memory

reply
Kirill A. Korinskiy 4.05.2009 16:32

Не забываем ставить vm.overcommit_ratio в 0.

Наверное следующий пост будет про эти магические опции.

reply
Kirill A. Korinskiy 4.05.2009 17:32

Почитал вдумчиво документацию, вот понять не могу одного а как overcommit_memory=2 влияет на COW при fork()? По идеи он должен выключать вообще все оптимизации такого рода?

Можешь ссылки в ядро кинуть?

reply
pavlenko 5.05.2009 0:42

overcommit_memory никак не влияет на COW при fork. Он включает в ядре проверку на суммарный допустимый размер адресуемого виртаульного пространства (vm).

Когда запускается fork(), или же например делается mmap c MAP_PRIVATE то размер адресного пространства, необходимого для адресации известен. Но пока данные не изменены страницы просто ссылаются на неизмененные данные.

В случае overcommit_memory=2 размер виртуального адресного пространства (vm) будет равен “размер swap”+ “размер RAM”*(x/100) где x собственно и есть overcommit_ratio.

Таким образом при использовании overcommit_ratio=0 в реальной жизни большая часть памяти не исользуется, так как резервируется место подо все виртуальноа адресное пространство, тогда как далеко не все оно адресует уникальные данные(скопированные COW алгоритмом и принадлежащие именно этому процессу страницы). По этому я думаю логичным будет overcommit_ratio=50 и по моему оно и является значением по умолчанию.

Насчет ссылко на код то если я не ошибаюсь, функция называется что-то вроде __vm_enough_memory где очень показательна проверка при overcommit_memory=0 ;-) Хотя с тех пор когда я туда заглядывал могло и измениться.

reply
человек 5.05.2009 12:33

Коллеги, а что такое COW (http://progopedia.ru/dialect/cow/)?

“размер swap”+ “размер RAM(x/100), при х=0 будет равно размеру swap. Скорее всего, проверка будет такой: “размер swap”+ “размер RAM(1+ x/100).


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

А скажите тогда вот что. Всякие там top’ы показывают какое количество памяти?

reply
Kirill A. Korinskiy 5.05.2009 13:43

COW — Copy On Write

В каких колонках? В RES — сколько физической памяти, в VIRT — сколько у процесса всего виртуальной. Ну а SWAP это VIRTRES :)

reply
Kirill A. Korinskiy 5.05.2009 16:00

Спасибо. Сообразил.

reply
Алексей Лебедев 4.05.2009 13:24

Как всегда, фраза «с большим приоритетом» воспринимается неправильно. Под приоритетом подразумевается nice конечно же?

А вообще, черная магия. Есть какая-то мат-основа под этим? Или чисто эмпирические наблюдения.

reply
Kirill A. Korinskiy 4.05.2009 14:42

nice-nice. Спасибо, записался :)

А мат. основания — нет, нету. Весь этот алгоритм именно что русская рулетка. Для процессов.

reply
anonymous_user2 5.05.2009 23:43

В заметке приведены ссылки на исходный код с указанием номеров строк. Возможно я ошибаюсь, но в гиперссылках отсутствует явное указание на <<ту самую>> версию файла oom_kill.c, которую имел в виду автор, когда писал заметку. В будущем, по мере внесения изменений в этот файл, номера строк перестанут соответствовать описанной в тексте функциональности (например, функциональность может остаться той же, но будет реализована в строчках с другими номерами). Подобное несоответствие может ввести читателя в заблуждение.

reply
Kirill A. Korinskiy 6.05.2009 0:33

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

reply
ramok 10.05.2009 1:51

я ж советовал сделать ссылку на релиз, а не на HEAD Ж:-)

reply
Kirill A. Korinskiy 10.05.2009 23:29

ну на head я и писал :)

зато там есть хитрый хеш ;)

reply
Kirill A. Korinskiy 6.05.2009 0:36

hash коммита на который писал статью добавил, спасибо.

reply
Arxell 11.05.2009 21:25

Товарищи, а почему oom killer не может отследить fork-бомбу?

например на питоне:

import os while(1): os.fork()

reply
player 12.05.2009 16:04

не подскажете, параметр vm.oom_kill_allocating_task с какой версии ядра появился? нашёл упоминание в 2.6.24 в 2.6.18 вроде еще нету

и ещё вопрос может ли oom-killer начинать своё кровавое дело, если и свопа и физ. памяти ещё предостаточно?

а то у меня тут ситуация забавная, ядро 2.6.18, overcommit_memory=2, overcommit_ratio=50, кто-то захотел памяти, сперва пореклеймили весь кеш фс, полезли в своп (при этом освобождённую память никто не занял) на ~3.3Gb свободной памяти и ~5.3 свопа проснулся oom-killer и устроил тотал десматч вендетту

reply
Kirill A. Korinskiy 14.05.2009 19:42

Появился он начиная с этого коммита а если идти по версиям, то начиная с 2.6.24.

Про ваш случай — программа могла просто захотеть много памяти. Скорее всего это и было.

reply
человек 13.05.2009 10:47

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

reply
Kirill A. Korinskiy 14.05.2009 19:44

Вообще если ты создаешь fork() большого и тяжелого приложения, то благодаря COW форкнется оно мгновенно и память будет, фактически, общей, до тех пор пока дочка или родитель не начнет модифицировать память. Тогда создаться копия страницы в которой живет память, которую решили начать править.

Т.е. если делать форк бомбу так:

malloc(много_памяти) заполняем ее единицами fork() заполняем ее нулями for() заполняем ее единицами

и т.п., то да, oom-killer придет крайне быстро ;)

reply
человек 13.05.2009 12:12

от форк бомбы как раз поможет ограничение по количеству процессов/памяти для одного пользователя.

reply
Arxell 13.05.2009 21:09

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

reply
человек 14.05.2009 12:28

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

Потом, как убивать – если прибить родителя, не факт, что погибнут все головы гидры. А как только останется одна веточка, гидра возродится вновь.

сори за офтоп.

PS: Думай. Для того тебе курсач и выдали, что бы ты думал :)

reply
Kirill A. Korinskiy 14.05.2009 19:44

Процессов да. Памяти нет.

reply
Kirill A. Korinskiy 14.05.2009 19:36

потому что создание fork() не выделяет, фактически много новой памяти. Спасибо COW. Т.е. oom-killer может начать грохать форки, но, блин, система уйдет в себя раньше :(

reply
Marko Kevac 15.07.2009 16:53

А где другие части “про память”?

reply
Kirill A. Korinskiy 15.07.2009 22:01

Пишутся. Просто нужно поймать вдохновение и дописать. Часто это бывает сложно :(

reply
russian-knight 20.10.2009 11:27

У нас на работе тут такая ситуация…

36 GiB памяти на 32bit ядре. Оказалось что LowMem быстро заполняется и из-за LowMem как раз вызывается OOM killer, который убивает машину напрочь. При этом в HighMem памяти выше крыши и swap вообще не используется.

Попробовали пока на одной машине отключить OOM killer. Посмотрим что произойдет.

reply
Kirill A. Korinskiy 3.12.2009 21:35

А почему бы не взять 64bit ядро?

reply
zubastiy 12.01.2010 21:35

Доброздравствуйте. Набрел на ваши преполезные статьи о памяти, спасибо за подробные разъяснения. У вас упоминается — новый цикл о памяти, не подкинете ссылку на старые статьи? Не смог самостоятельно найти в блоге.

И не подскажете — можно ли в ветке 2.6.3х регулировать количество памяти отдаваемой под cached?

reply

Comment form for «Про память: OOM Killer»

Required. 30 chars of fewer.

Required.