Про память: 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 критической ошибкой.

Возврат к списку

Голосов: 3