diff --git a/README.md b/README.md
index 7b42636..95cd811 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
The No Hang Daemon
==================
-`Nohang` - аналог [earlyoom](https://github.com/rfjakob/earlyoom) с поддержкой `zram` и `SIGTERM`. При дефиците доступной памяти `nohang` корректно завершает наиболее прожорливые процессы сигналом `SIGTERM` или `SIGKILL`, тем самым препятствуя зависанию, а также избыточному убийству процессов ядерным `OOM killer`'ом.
+`Nohang` - аналог [earlyoom](https://github.com/rfjakob/earlyoom) с поддержкой `zram` и `SIGTERM`. При дефиците доступной памяти `nohang` корректно завершает наиболее прожорливые процессы сигналом `SIGTERM` или `SIGKILL`, тем самым препятствуя [зависанию](https://en.wikipedia.org/wiki/Hang_(computing)), а также избыточному убийству процессов ядерным `OOM killer`'ом.
### Зачем это нужно?
@@ -11,6 +11,12 @@ The No Hang Daemon
"А можете рассказать, как сделать OOM Killer более агрессивным? Например, в ситуации, когда приложение открыло/создало множество мелких файлов и держит их в памяти, при внезапной нехватке памяти ядро пытается высвободить эти файловые страницы, что вешает систему намертво со 100%-м дисковым I/O на несколько (десятков) минут. А ведь зачастую гораздо проще просто грохнуть само приложение с дальнешим его перезапуском."
https://habr.com/company/flant/blog/348324/#comment_10659202
+"Это хорошо, если приходит OOM-killer. Плохо, когда есть огромный iowait, а киллера так никто и не видел, спит. Надежда на то, что без свопа протекающий фаерфокс будет пристрелен, очень быстро растворились в хрусте винта."
+https://habr.com/company/flant/blog/348324/#comment_10660004
+
+"А можете рассказать, как сделать OOM Killer более агрессивным? Например, в ситуации, когда приложение открыло/создало множество мелких файлов и держит их в памяти, при внезапной нехватке памяти ядро пытается высвободить эти файловые страницы, что вешает систему намертво со 100%-м дисковым I/O на несколько (десятков) минут. А ведь зачастую гораздо проще просто грохнуть само приложение с дальнешим его перезапуском."
+https://habr.com/company/flant/blog/348324/#comment_10659202
+
"Говно какое-то этот оом-киллер, нихрена не работает.
Но чтобы это нормально работало, я думаю, нужен какой-то демон, который постоянно мониторит потребление памяти и прибивает тот процесс, который резко начинает набирать обороты. В общем сам этот демон будет проц грузить, хотя можно ограничить процессы, которые он будет проверять только пользовательскими процессами, добавить black-white list ну и настраиваемый интервал проверки.
Короче если кто-то напишет будет круто."
@@ -40,17 +46,17 @@ https://2ch.hk/s/res/2310304.html#2311483, https://archive.li/idixk
- поддержка работы со `zram`, возможность реакции на `mem_used_total`
- удобный конфиг с возможностью тонкой настройки
- аргументы командной строки -h/--help и -c/--config
-- возможность раздельного задания уровней `MemAvailable`, `SwapFree`, `mem_used_total` для отпраки `SIGTERM` и `SIGKILL`, возможность задания в процентах (%), кибибайтах (K), мебибайтах (M), гибибайтах (G)
+- возможность раздельного задания уровней `MemAvailable`, `SwapFree`, `mem_used_total` для отпраки `SIGTERM` и `SIGKILL`, возможность задания в процентах и мебибайтах.
- возможность снижения `oom_score_adj` процессов, чьи `oom_score_adj` завышены (актуально для `chromium`)
- лучший алгоритм выбора периодов между проверками доступной памяти: при больших объемах доступной памяти нет смысла проверять ее состояние часто, поэтому период проверки уменьшается по мере уменьшения размера доступной памяти
- интенсивность мониторинга можно гибко настраивать (параметры конфига `rate_mem`, `rate_swap`, `rate_zram`)
-- по умолчанию память заблокирована с помощью `mlockall()` для предотвращения своппинга процесса
-- по умолчанию высокий приоритет процесса `nice -20`, может регулироваться через конфиг
+- возможность блокировки памяти с помощью `mlockall()` для предотвращения своппинга процесса
+- по умолчанию высокий приоритет процесса `nice -15`, может регулироваться через конфиг
- предотвращение самоубийства с помощью `self_oom_score_adj = -1000`
- возможность задания `oom_score_min` для предотвращения убийства невиновных
- verbosity: опциональность печати параметров конфига при старте программы, опциональность печати результатов проверки памяти и времени между проверками памяти
- возможность предотвращения избыточного убийства процессов с помощью задания миниального `oom_score` для убиваемых процессов и установка минимальной задержки просле отправки сигналов (параметры конфига `min_delay_after_sigkill` и `min_delay_after_sigterm`)
-- возможность показа десктопных уведомлений
+- возможность показа десктопных уведомлений c помощью notify-send, с показом сигнала (SIGTERM или SIGKILL), который отправлен процессу, а также Pid, oom_score, VmRSS, VmSwap, которыми обладал процесс перед получением сигнала.
- наличие `man` страницы
- наличие установщика для пользователей `systemd`
- протестировано на `Debian 9 x86_64`, `Debian 8 i386`, `Fedora 28 x86_64`
@@ -97,6 +103,7 @@ sudo ./uninstall.sh
- Скорость разработки на Python значительно выше. Больше фич за приемлемое время.
- Практически единственный минус Python - большее потребление памяти процессом.
+- На самом деле я просто не знаю C и немножко изучал Python, поэтому пишу на последнем.
### Подсказка
diff --git a/nohang b/nohang
index ca5bad7..cec3c30 100755
--- a/nohang
+++ b/nohang
@@ -2,9 +2,9 @@
# Nohang - The No Hang Daemon for Linux
-################################################################################
+##########################################################################
-### импорты
+# импорты
import os
from ctypes import CDLL
@@ -12,13 +12,13 @@ from operator import itemgetter
from time import sleep
from argparse import ArgumentParser
-################################################################################
+##########################################################################
-### задание констант
+# задание констант
version = 'unknown'
-sig_dict = {9:'SIGKILL', 15:'SIGTERM'}
+sig_dict = {9: 'SIGKILL', 15: 'SIGTERM'}
# директория, в которой запущен скрипт
cd = os.getcwd()
@@ -27,11 +27,10 @@ cd = os.getcwd()
default_configs = (
cd + '/nohang.conf',
'/etc/nohang/nohang.conf'
- )
+)
# универсальное сообщение при инвалидном конфиге
-conf_err_mess = "\nSet up the path to the valid config file w' \
-'ith -c/--config CONFIG option!\nExit"
+conf_err_mess = '\nSet up the path to the valid config file with -c/--config CONFIG option!\nExit'
# означает, что при задани zram disksize = 10000M доступная память
# уменьшится на 42M
@@ -43,9 +42,9 @@ conf_err_mess = "\nSet up the path to the valid config file w' \
# но это утверждение противоречит опытным данным
zram_disksize_factor = 0.0042
-################################################################################
+##########################################################################
-### задание функций
+# задание функций
def string_to_float_convert_test(string):
@@ -62,7 +61,7 @@ def string_to_int_convert_test(string):
return None
-def func_decrease_oom_score_adj(oom_score_adj_before, oom_score_adj_after):
+def func_decrease_oom_score_adj(oom_score_adj_max):
# цикл для наполнения oom_list
for i in os.listdir('/proc'):
@@ -72,11 +71,9 @@ def func_decrease_oom_score_adj(oom_score_adj_before, oom_score_adj_after):
try:
oom_score_adj = int(rline1('/proc/' + i + '/oom_score_adj'))
- if oom_score_adj > oom_score_adj_before:
- write(
- '/proc/' + i + '/oom_score_adj',
- oom_score_adj_after + '\n'
- )
+ if oom_score_adj > oom_score_adj_max:
+ write('/proc/' + i + '/oom_score_adj',
+ str(oom_score_adj_max) + '\n')
except FileNotFoundError:
pass
except ProcessLookupError:
@@ -107,6 +104,7 @@ def percent(num):
def just_percent_mem(num):
return str(round(num * 100, 1)).rjust(4, ' ')
+
def just_percent_swap(num):
return str(round(num * 100, 1)).rjust(5, ' ')
@@ -133,7 +131,7 @@ def zram_stat(zram_id):
mem_used_total = mm_stat_list[2]
except FileNotFoundError:
mem_used_total = rline1('/sys/block/' + zram_id + '/mem_used_total')
- return disksize, mem_used_total # BYTES, str
+ return disksize, mem_used_total # BYTES, str
# имя через пид
@@ -151,9 +149,7 @@ def pid_to_name(pid):
def find_victim_and_send_signal(signal):
if decrease_oom_score_adj and root:
- func_decrease_oom_score_adj(
- oom_score_adj_before, oom_score_adj_after
- )
+ func_decrease_oom_score_adj(oom_score_adj_max)
oom_list = []
for i in os.listdir('/proc'):
@@ -173,14 +169,24 @@ def find_victim_and_send_signal(signal):
# посылаем сигнал
if oom_score >= oom_score_min:
+
pid = pid_tuple_list[0]
+
name = pid_to_name(pid)
- print(
- ' Try to send the {} signal to {}, Pid {}, oom_score {}'.format(
- sig_dict[signal], name, pid, oom_score
- )
- )
+ with open('/proc/' + pid + '/status') as f:
+ for n, line in enumerate(f):
+
+ if n is vm_rss_index:
+ vm_rss = kib_to_mib(int(line.split(':')[1].split(' ')[-2]))
+ continue
+
+ if n is vm_swap_index:
+ vm_swap = kib_to_mib(int(line.split(':')[1].split(' ')[-2]))
+ break
+
+ print(' Try to send the {} signal to {},\n Pid {}, oom_score {}, VmRSS {} MiB, VmSwap {} MiB'.format(
+ sig_dict[signal], name, pid, oom_score, vm_rss, vm_swap))
try:
os.kill(int(pid), signal)
@@ -188,28 +194,26 @@ def find_victim_and_send_signal(signal):
if desktop_notifications:
- info = '"The {} signal has been sent to {},\nPid: {}, oom_score: {}" &'.format(sig_dict[signal], name, pid, oom_score)
+ info = '"The {} signal has been sent to {}, Pid: {}, oom_score: {}, VmRSS {} MiB, VmSwap {} MiB" &'.format(
+ sig_dict[signal], name, pid, oom_score, vm_rss, vm_swap)
if root:
- # получаем множество залогиненных юзеров из вывода команды users
+ # получаем множество залогиненных юзеров из вывода команды
+ # users
users_set = set(str(
- Popen('users', shell=True, stdout=PIPE).communicate()[0][0:-1]
- )[2:-1].split(' '))
+ Popen('users', shell=True,
+ stdout=PIPE).communicate()[0][0:-1]
+ )[2:-1].split(' '))
# отправляем уведомление всем залогиненным юзерам
for i in users_set:
- root_notify_command = 'DISPLAY=:0.0 sudo -u {} {} -i dialog-warnig "Nohang tried to prevent OOM" -t {} -u critical '.format(
- i, notify_command, notify_time
- )
+ root_notify_command = 'DISPLAY=:0.0 sudo -u {} notify-send "Nohang tried to prevent OOM" '.format(
+ i)
os.system(root_notify_command + info)
else:
- user_notify_command =(
- '{} -i dialog-warnig "Nohang tried to prevent OOM" -t {} -u critical '.format(
- notify_command, notify_time
- )
- )
+ user_notify_command = (
+ 'notify-send "Nohang tried to prevent OOM" ')
os.system(user_notify_command + info)
-
except ProcessLookupError:
print(' No such process')
except PermissionError:
@@ -231,12 +235,10 @@ def find_victim_and_send_signal(signal):
sleep(min_delay_after_sigterm)
-################################################################################
+##########################################################################
-### поиск позиций
+# поиск позиций и mem_total
-
-# ищем позиции
with open('/proc/meminfo') as file:
mem_list = file.readlines()
@@ -253,24 +255,31 @@ swap_free_index = swap_total_index + 1
mem_total = int(mem_list[0].split(':')[1].split(' ')[-2])
+with open('/proc/self/status') as file:
+ status_list = file.readlines()
-# еще найти позиции VmRSS & VmSwap
+# список имен из /proc/*/status для дальнейшего поиска позиций VmRSS and VmSwap
+status_names = []
+for s in status_list:
+ status_names.append(s.split(':')[0])
+vm_rss_index = status_names.index('VmRSS')
+vm_swap_index = status_names.index('VmSwap')
-################################################################################
+##########################################################################
-### получение пути к конфигу
+# получение пути к конфигу
# парсинг аргументов командной строки
parser = ArgumentParser()
parser.add_argument(
- '-c',
- '--config',
+ '-c',
+ '--config',
help="""path to the config file, default values:
- ./nohang.conf, /etc/nohang/nohang.conf""",
- default=None,
+ ./nohang.conf, /etc/nohang/nohang.conf""",
+ default=None,
type=str
- )
+)
arg_config = parser.parse_args().config
@@ -298,19 +307,19 @@ else:
print('The path to the config file to be used:')
print(config)
-################################################################################
+##########################################################################
-### парсинг конфига с получением словаря параметров
+# парсинг конфига с получением словаря параметров
try:
with open(config) as f:
config_dict = dict()
- for line in f:
+ for line in f:
a = line.startswith('#')
b = line.startswith('\n')
c = line.startswith('\t')
d = line.startswith(' ')
- if not a and not b and not c and not d:
+ if not a and not b and not c and not d:
a = line.split('=')
config_dict[a[0].strip()] = a[1].strip()
except PermissionError:
@@ -327,12 +336,11 @@ except IndexError:
exit()
-################################################################################
-
-### извлечение параметров из словаря
-### проверка наличия всех необходимых параметров
-### валидация всех параметров
+##########################################################################
+# извлечение параметров из словаря
+# проверка наличия всех необходимых параметров
+# валидация всех параметров
if 'print_config' in config_dict:
@@ -343,18 +351,16 @@ if 'print_config' in config_dict:
print_config = False
else:
print(
- 'Invalid print_config value {} (should be True or Fa' \
- 'lse)\nExit'.format(
+ 'Invalid print_config value {} (should be True or False)\nExit'.format(
print_config
- )
)
+ )
exit()
else:
print('Print_config not in config\nExit')
exit()
-
if 'print_mem_check_results' in config_dict:
print_mem_check_results = config_dict['print_mem_check_results']
if print_mem_check_results == 'True':
@@ -363,18 +369,16 @@ if 'print_mem_check_results' in config_dict:
print_mem_check_results = False
else:
print(
- 'Invalid print_mem_check_results value {} (should be True o' \
- 'r False)\nExit'.format(
+ 'Invalid print_mem_check_results value {} (should be True or False)\nExit'.format(
print_mem_check_results
- )
)
+ )
exit()
else:
print('print_mem_check_results not in config\nExit')
exit()
-
if 'print_sleep_periods' in config_dict:
print_sleep_periods = config_dict['print_sleep_periods']
if print_sleep_periods == 'True':
@@ -383,11 +387,10 @@ if 'print_sleep_periods' in config_dict:
print_sleep_periods = False
else:
print(
- 'Invalid print_sleep_periods value {} (should be Tru' \
- 'e or False)\nExit'.format(
+ 'Invalid print_sleep_periods value {} (should be True or False)\nExit'.format(
print_sleep_periods
- )
)
+ )
exit()
else:
print('print_sleep_periods not in config\nExit')
@@ -404,8 +407,8 @@ if 'mlockall' in config_dict:
print(
'Invalid mlockall value {} (should be True or False)\nExit'.format(
mlockall
- )
)
+ )
exit()
else:
print('mlockall not in config\nExit')
@@ -427,7 +430,6 @@ else:
exit()
-
if 'self_oom_score_adj' in config_dict:
self_oom_score_adj = config_dict['self_oom_score_adj']
self_oom_score_adj = string_to_int_convert_test(self_oom_score_adj)
@@ -485,7 +487,6 @@ else:
exit()
-
if 'mem_min_sigterm' in config_dict:
mem_min_sigterm = config_dict['mem_min_sigterm']
else:
@@ -528,10 +529,9 @@ else:
exit()
-
if 'min_delay_after_sigterm' in config_dict:
- min_delay_after_sigterm = string_to_float_convert_test(config_dict['m' \
- 'in_delay_after_sigterm'])
+ min_delay_after_sigterm = string_to_float_convert_test(
+ config_dict['m' 'in_delay_after_sigterm'])
if min_delay_after_sigterm is None:
print('Invalid min_delay_after_sigterm value, not float\nExit')
exit()
@@ -545,8 +545,8 @@ else:
if 'min_delay_after_sigkill' in config_dict:
- min_delay_after_sigkill = string_to_float_convert_test(config_dict['mi' \
- 'n_delay_after_sigkill'])
+ min_delay_after_sigkill = string_to_float_convert_test(
+ config_dict['mi' 'n_delay_after_sigkill'])
if min_delay_after_sigkill is None:
print('Invalid min_delay_after_sigkill value, not float\nExit')
exit()
@@ -581,49 +581,29 @@ if 'decrease_oom_score_adj' in config_dict:
elif decrease_oom_score_adj == 'False':
decrease_oom_score_adj = False
else:
- print(
- 'invalid decrease_oom_score_adj value {} (should be Tru' \
- 'e or False)\nExit'.format(
- decrease_oom_score_adj
- )
- )
+ print('invalid decrease_oom_score_adj value {} (should be True or False)\nExit'.format(
+ decrease_oom_score_adj))
exit()
else:
print('decrease_oom_score_adj not in config\nExit')
exit()
-if 'oom_score_adj_before' in config_dict:
- oom_score_adj_before = config_dict['oom_score_adj_before']
- if string_to_int_convert_test(oom_score_adj_before) is None:
- print('Invalid oom_score_adj_before value, not integer\nExit')
+if 'oom_score_adj_max' in config_dict:
+ oom_score_adj_max = config_dict['oom_score_adj_max']
+
+ oom_score_adj_max = string_to_int_convert_test(oom_score_adj_max)
+ if oom_score_adj_max is None:
+ print('Invalid oom_score_adj_max value, not integer\nExit')
exit()
- else:
- oom_score_adj_before = int(oom_score_adj_before)
- if oom_score_adj_before < 0 or oom_score_adj_before > 1000:
- print('Недопустимое значение oom_score_adj_before\nExit')
- exit()
-else:
- print('oom_score_adj_before not in config\nExit')
- exit()
-
-
-if 'oom_score_adj_after' in config_dict:
- oom_score_adj_after = config_dict['oom_score_adj_after']
- if string_to_int_convert_test(oom_score_adj_after) is None:
- print('Invalid oom_score_adj_after value, not integer\nExit')
+ if oom_score_adj_max < 0 or oom_score_adj_max > 1000:
+ print('Недопустимое значение oom_score_adj_max\nExit')
exit()
- else:
- oom_score_adj_after = int(oom_score_adj_after)
- if oom_score_adj_after < 0 or oom_score_adj_after > 1000:
- print('Недопустимое значение oom_score_adj_after\nExit')
- exit()
else:
- print('oom_score_adj_after not in config\nExit')
+ print('oom_score_adj_max not in config\nExit')
exit()
-
if 'desktop_notifications' in config_dict:
desktop_notifications = config_dict['desktop_notifications']
if desktop_notifications == 'True':
@@ -632,43 +612,17 @@ if 'desktop_notifications' in config_dict:
elif desktop_notifications == 'False':
desktop_notifications = False
else:
- print(
- 'Invalid desktop_notifications value {} (should be True or False)\nExit'.format(
- desktop_notifications
- )
- )
+ print('Invalid desktop_notifications value {} (should be True or False)\nExit'.format(
+ desktop_notifications))
exit()
else:
print('desktop_notifications not in config\nExit')
exit()
-if 'notify_command' in config_dict:
- notify_command = config_dict['notify_command'].strip()
-else:
- print('notify_command not in config\nExit')
- exit()
+##########################################################################
-
-if 'notify_time' in config_dict:
- notify_time = config_dict['notify_time']
- notify_time = string_to_float_convert_test(notify_time)
- if notify_time is None:
- print('Invalid notify_time value, not float\nExit')
- exit()
- if notify_time <= 0:
- print('Недопустимое значение notify_time\nExit')
- exit()
- else:
- notify_time = int(notify_time * 1000)
-else:
- print('notify_time not in config\nExit')
- exit()
-
-
-################################################################################
-
-### получение уровней в кибибайтах
+# получение уровней в кибибайтах
def sig_level_to_kb(string):
@@ -681,9 +635,7 @@ def sig_level_to_kb(string):
elif string.endswith('G'):
return float(string[:-1].strip()) * 1048576
else:
- print(
- 'Конфиг инвалид, где-то неверно указаны единицы измерения\nExit'
- )
+ print('Конфиг инвалид, где-то неверно указаны единицы измерения\nExit')
exit()
@@ -715,14 +667,14 @@ def sig_level_to_kb_swap(string):
swap_min_sigterm_swap = sig_level_to_kb_swap(swap_min_sigterm)
swap_min_sigkill_swap = sig_level_to_kb_swap(swap_min_sigkill)
-if type(swap_min_sigterm_swap) is tuple:
+if isinstance(swap_min_sigterm_swap, tuple):
swap_term_is_percent = True
swap_min_sigterm_percent = swap_min_sigterm_swap[0]
else:
swap_term_is_percent = False
swap_min_sigterm_kb = swap_min_sigterm_swap
-if type(swap_min_sigkill_swap) is tuple:
+if isinstance(swap_min_sigkill_swap, tuple):
swap_kill_is_percent = True
swap_min_sigkill_percent = swap_min_sigkill_swap[0]
else:
@@ -730,10 +682,9 @@ else:
swap_min_sigkill_kb = swap_min_sigkill_swap
+##########################################################################
-################################################################################
-
-### самозащита и печать конфига
+# самозащита и печать конфига
# повышаем приоритет
@@ -775,7 +726,6 @@ else:
decrease_res = 'Impossible'
-
if print_config:
print('\nI. VERBOSITY')
@@ -787,10 +737,10 @@ if print_config:
print('mlockall: {} ({})'.format(mlockall, mla_res))
print('self_nice: {} ({})'.format(
self_nice, self_nice_result
- ))
+ ))
print('self_oom_score_adj: {} ({})'.format(
self_oom_score_adj, self_oom_score_adj_result
- ))
+ ))
print('\nIII. ИНТЕНСИВНОСТЬ МОНИТОРИНГА')
print('rate_mem: {}'.format(rate_mem))
@@ -813,16 +763,12 @@ if print_config:
# False (OK) - OK не нужен когда фолс
print('decrease_oom_score_adj: {} ({})'.format(
decrease_oom_score_adj, decrease_res
- ))
+ ))
if decrease_oom_score_adj:
- print('oom_score_adj_before: {}'.format(oom_score_adj_before))
- print('oom_score_adj_after: {}'.format(oom_score_adj_after))
+ print('oom_score_adj_max: {}'.format(oom_score_adj_max))
print('\nVI. DESKTOP NOTIFICATIONS')
print('desktop_notifications: {}'.format(desktop_notifications))
- if desktop_notifications:
- print('notify_command: {}'.format(notify_command))
- print('notify_time: {}'.format(round(notify_time / 1000, 1)))
# для рассчета ширины столбцов при печати mem и zram
@@ -831,9 +777,9 @@ mem_len = len(str(round(mem_total / 1024.0)))
print('\nStart monitoring...')
-################################################################################
+##########################################################################
-### цикл проверки уровней доступной памяти
+# цикл проверки уровней доступной памяти
while True:
@@ -851,7 +797,6 @@ while True:
swap_free = int(line.split(':')[1].split(' ')[-2])
break
-
# если swap_min_sigkill задан в процентах
if swap_kill_is_percent:
swap_min_sigkill_kb = swap_total * swap_min_sigkill_percent / 100.0
@@ -859,7 +804,6 @@ while True:
if swap_term_is_percent:
swap_min_sigterm_kb = swap_total * swap_min_sigterm_percent / 100.0
-
# находим MemUsedZram
disksize_sum = 0
mem_used_total_sum = 0
@@ -870,13 +814,11 @@ while True:
mem_used_total_sum += int(stat[1])
mem_used_zram = (
mem_used_total_sum + disksize_sum * zram_disksize_factor
- ) / 1024.0
-
+ ) / 1024.0
# для рассчета ширины столбца свопа
swap_len = len(str(round(swap_total / 1024.0)))
-
# печать размеров доступной памяти
if swap_total == 0 and mem_used_zram == 0:
if print_mem_check_results:
@@ -884,8 +826,8 @@ while True:
'MemAvail: {} M, {} %'.format(
human(mem_available, mem_len),
just_percent_mem(mem_available / mem_total)
- )
)
+ )
elif swap_total > 0 and mem_used_zram == 0:
if print_mem_check_results:
print(
@@ -894,22 +836,21 @@ while True:
just_percent_mem(mem_available / mem_total),
human(swap_free, swap_len),
just_percent_swap(swap_free / (swap_total + 0.0001))
- )
)
+ )
else:
if print_mem_check_results:
print(
- 'MemAvail: {} M, {} % | SwapFree: {} M, {} % | MemUsed' \
- 'Zram: {} M, {} %'.format(
+ 'MemAvail: {} M, {} % | SwapFree: {} M, {} % | MemUsed'
+ 'Zram: {} M, {} %'.format(
human(mem_available, mem_len),
- just_percent_mem(mem_available / mem_total),
+ just_percent_mem(mem_available / mem_total),
human(swap_free, swap_len),
- just_percent_swap(swap_free / (swap_total + 0.0001)),
+ just_percent_swap(swap_free / (swap_total + 0.0001)),
human(mem_used_zram, mem_len),
just_percent_mem(mem_used_zram / mem_total)
- )
)
-
+ )
# если swap_min_sigkill задан в абсолютной величине и Swap_total = 0
if swap_total > swap_min_sigkill_kb:
@@ -922,12 +863,11 @@ while True:
else:
swap_sigterm_pc = '-'
-
# MEM SWAP KILL
if mem_available <= mem_min_sigkill_kb and swap_free <= swap_min_sigkill_kb:
print(
- '+ MemAvail ({} M, {} %) < mem_min_sigkill ({} M, {} %)\n S' \
- 'wapFree ({} M, {} %) < swap_min_sigkill ({} M, {} %)'.format(
+ '+ MemAvail ({} M, {} %) < mem_min_sigkill ({} M, {} %)\n S'
+ 'wapFree ({} M, {} %) < swap_min_sigkill ({} M, {} %)'.format(
kib_to_mib(mem_available),
percent(mem_available / mem_total),
kib_to_mib(mem_min_sigkill_kb),
@@ -936,31 +876,29 @@ while True:
percent(swap_free / (swap_total + 0.0001)),
kib_to_mib(swap_min_sigkill_kb),
swap_sigkill_pc
- )
)
+ )
find_victim_and_send_signal(9)
continue
-
# ZRAM KILL
if mem_used_zram >= zram_max_sigkill_kb:
print(
'+ MemUsedZram ({} M, {} %) > zram_max_sigkill ({} M, {} %)'.format(
kib_to_mib(mem_used_zram),
- percent(mem_used_zram / mem_total),
+ percent(
+ mem_used_zram /
+ mem_total),
kib_to_mib(zram_max_sigkill_kb),
- percent(zram_max_sigkill_kb / mem_total)
- )
- )
+ percent(
+ zram_max_sigkill_kb /
+ mem_total)))
find_victim_and_send_signal(9)
continue
-
# MEM SWAP TERM
if mem_available <= mem_min_sigterm_kb and swap_free <= swap_min_sigterm_kb:
- print(
- '+ MemAvail ({} M, {} %) < mem_min_sigterm ({} M, {} %)\n SwapFree' \
- ' ({} M, {} %) < swap_min_sigterm ({} M, {} %)'.format(
+ print('+ MemAvail ({} M, {} %) < mem_min_sigterm ({} M, {} %)\n SwapFree ({} M, {} %) < swap_min_sigterm ({} M, {} %)'.format(
kib_to_mib(mem_available),
percent(mem_available / mem_total),
kib_to_mib(mem_min_sigterm_kb),
@@ -968,31 +906,28 @@ while True:
kib_to_mib(swap_free),
percent(swap_free / (swap_total + 0.0001)),
kib_to_mib(swap_min_sigterm_kb),
- swap_sigterm_pc
- )
- )
+ swap_sigterm_pc))
find_victim_and_send_signal(15)
-
# ZRAM TERM
if mem_used_zram >= zram_max_sigterm_kb:
- print(
- '+ MemUsedZram ({} M, {} %) > zram_max_sigterm ({} M, {} %)'.format(
+ print('+ MemUsedZram ({} M, {} %) > zram_max_sigterm ({} M, {} %)'.format(
kib_to_mib(mem_used_zram),
- percent(mem_used_zram / mem_total),
+ percent(
+ mem_used_zram /
+ mem_total),
kib_to_mib(zram_max_sigterm_kb),
- percent(zram_max_sigterm_kb / mem_total)
- )
- )
+ percent(
+ zram_max_sigterm_kb /
+ mem_total)))
find_victim_and_send_signal(15)
-
# задание периода в зависимости от рейтов и уровней доступной памяти
t_mem = mem_available / 1000000.0 / rate_mem
t_swap = swap_free / 10000000.0 / rate_swap
- t_zram = (mem_total * 0.8 - mem_used_zram) / 1000000.0 / rate_zram
+ t_zram = (mem_total * 0.8 - mem_used_zram) / 1000000.0 / rate_zram
if t_zram < 0.01:
t_zram = 0.01
@@ -1010,4 +945,3 @@ while True:
sleep(t)
except KeyboardInterrupt:
exit()
-
diff --git a/nohang.conf b/nohang.conf
index f1aa80d..c1fe9d2 100644
--- a/nohang.conf
+++ b/nohang.conf
@@ -32,15 +32,18 @@ print_sleep_periods = False
True - заблокировать процесс в памяти для запрета его своппинга.
False - не блокировать. Значения чувствительны к регистру!
-mlockall = True
+ В Fedora 28 значение True вызывает увеличение потребления
+ памяти процессом на 200 MiB, в Debian 8 и 9 такой проблемы нет.
+
+mlockall = False
Установка отрицательных значений self_nice и self_oom_score_adj
требует наличия root прав.
- Повысить приоритет процесса, установив niceness -20
+ Установка отрицательного self_nice повышает приоритет процесса.
Допустимые значения - целые числа из диапазона [-20; 19]
-self_nice = -20
+self_nice = -15
Задать oom_score_adj для процесса.
Установка значения -1000 запретит самоубийство.
@@ -67,8 +70,8 @@ self_oom_score_adj = -1000
достаточно хорошо, успешно справляясь с резкими скачками потребления
памяти.
-rate_mem = 6
-rate_swap = 0.5
+rate_mem = 4
+rate_swap = 1
rate_zram = 1
#####################################################################
@@ -81,12 +84,7 @@ rate_zram = 1
Сигнал отправляется если MemAvailable и SwapFree одновременно
опустятся ниже соответствующих значений.
- Значения могут быть выражены в процентах (%),
- кибибайтах (K), мебибайтах (M) или гибибайтах (G).
- Например:
- swap_min_sigterm = 8 %
- mem_min_sigterm = 0.5 G
- swap_min_sigkill = 200 M
+ Значения могут быть выражены в процентах (%) и мебибайтах (M)
mem_min_sigterm = 8 %
mem_min_sigkill = 4 %
@@ -100,7 +98,7 @@ swap_min_sigkill = 4 %
система виснет или запускается OOM killer.
По мере увеличения доли zram в памяти может падать
отзывчивость системы.
- Может также задаваться в %, K, M, G
+ Может также задаваться в % и M.
zram_max_sigterm = 55 %
zram_max_sigkill = 60 %
@@ -128,41 +126,35 @@ min_delay_after_sigkill = 3
Процессы браузера chromium обычно имеют oom_score_adj
200 или 300. Это приводит к тому, что процессы хрома умирают
первыми вместо действительно тяжелых процессов.
-
Если параметр decrease_oom_score_adj установлен
в значение True, то у процессов, имеющих oom_score_adj выше
- oom_score_adj_before значение oom_score_adj будет опущено
- до oom_score_adj_after перед поиском жертвы.
+ oom_score_adj_max значение oom_score_adj будет опущено
+ до oom_score_adj_max перед поиском жертвы.
False - не изменять oom_score_adj процессов перед поиском
жертвы. Значения чувствительны к регистру!
Требует root прав.
-decrease_oom_score_adj = False
+decrease_oom_score_adj = True
Допустимые значения - целые числа из диапазона [0; 1000]
-oom_score_adj_before = 10
-oom_score_adj_after = 5
+oom_score_adj_max = 20
#####################################################################
VI. DESKTOP NOTIFICATIONS
Эта возможность требует наличия notify-send в системе.
+
В Debian/Ubuntu это обеспечивается установкой пакета
- libnotify-bin. Также требуется наличие notification-daemon.
+ libnotify-bin. В Fedora и Arch Linux - пакет libnotify.
+ Также требуется наличие notification-daemon.
+ При запуске nohang от рута уведомления рассылаются всем
+ залогиненным пользователям.
+ See also wiki.archlinux.org/index.php/Desktop_notifications
+
Допустимые значения: True и False
-desktop_notifications = False
-
- Может использоваться другая команда, если ее синтаксис совместим
- с синтаксисом notify-send
-
-notify_command = notify-send
-
- Время показа уведомлений в секундах.
- Должно быть положительным числом.
-
-notify_time = 15
+desktop_notifications = True