September 8, 2021

Алгоритм расчёта OOM Score

Алгоритм расчета уровня негодности процесса (итоговое значение будет измеряться в очках негодности (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 критической ошибкой.