add support white, black, avoid, prefer lists
This commit is contained in:
parent
6bccc3402f
commit
8825021f6b
8
.gitignore
vendored
8
.gitignore
vendored
@ -107,11 +107,3 @@ venv.bak/
|
|||||||
# Kate
|
# Kate
|
||||||
.kate-swp
|
.kate-swp
|
||||||
|
|
||||||
# man
|
|
||||||
*.1.gz
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,6 +57,7 @@ https://2ch.hk/s/res/2310304.html#2311483, https://archive.li/idixk
|
|||||||
- verbosity: опциональность печати параметров конфига при старте программы, опциональность печати результатов проверки памяти и времени между проверками памяти
|
- verbosity: опциональность печати параметров конфига при старте программы, опциональность печати результатов проверки памяти и времени между проверками памяти
|
||||||
- возможность предотвращения избыточного убийства процессов с помощью задания миниального `oom_score` для убиваемых процессов и установка минимальной задержки просле отправки сигналов (параметры конфига `min_delay_after_sigkill` и `min_delay_after_sigterm`)
|
- возможность предотвращения избыточного убийства процессов с помощью задания миниального `oom_score` для убиваемых процессов и установка минимальной задержки просле отправки сигналов (параметры конфига `min_delay_after_sigkill` и `min_delay_after_sigterm`)
|
||||||
- возможность показа десктопных уведомлений c помощью `notify-send`, с показом сигнала (`SIGTERM` или `SIGKILL`), который отправлен процессу, а также `Pid`, `oom_score`, `VmRSS`, `VmSwap`, которыми обладал процесс перед получением сигнала.
|
- возможность показа десктопных уведомлений c помощью `notify-send`, с показом сигнала (`SIGTERM` или `SIGKILL`), который отправлен процессу, а также `Pid`, `oom_score`, `VmRSS`, `VmSwap`, которыми обладал процесс перед получением сигнала.
|
||||||
|
- поддержка white, black, prefer и avoid списков с использованием Perl-compatible regular expressions
|
||||||
- наличие `man` страницы
|
- наличие `man` страницы
|
||||||
- наличие установщика для пользователей `systemd`
|
- наличие установщика для пользователей `systemd`
|
||||||
- протестировано на `Debian 9 x86_64`, `Debian 8 i386`, `Fedora 28 x86_64`
|
- протестировано на `Debian 9 x86_64`, `Debian 8 i386`, `Fedora 28 x86_64`
|
||||||
|
406
nohang
406
nohang
@ -10,6 +10,7 @@ import os
|
|||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
|
from re import fullmatch
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
@ -147,35 +148,58 @@ def pid_to_name(pid):
|
|||||||
|
|
||||||
|
|
||||||
def send_notify(signal, name, pid, oom_score, vm_rss, vm_swap):
|
def send_notify(signal, name, pid, oom_score, vm_rss, vm_swap):
|
||||||
|
|
||||||
# текст отправляемого уведомления
|
# текст отправляемого уведомления
|
||||||
info = '"<u>Nohang</u> sent <u>{}</u> \nto the process <b>{}</b> \n<i>P' \
|
info = '"<u>Nohang</u> sent <u>{}</u> \nto the process <b>{}</b> \n<i>P' \
|
||||||
'id:</i> <b>{}</b> \n<i>oom_score:</i> <b>{}</b> \n<i>VmRSS:</i> <b' \
|
'id:</i> <b>{}</b> \n<i>Badness:</i> <b>{}</b> \n<i>VmRSS:</i> <b' \
|
||||||
'>{} MiB</b> \n<i>VmSwap:</i> <b>{} MiB</b>" &'.format(
|
'>{} MiB</b> \n<i>VmSwap:</i> <b>{} MiB</b>" &'.format(
|
||||||
sig_dict[signal], name, pid, oom_score, vm_rss, vm_swap)
|
sig_dict[signal], name, pid, oom_score, vm_rss, vm_swap)
|
||||||
|
|
||||||
if root:
|
if root:
|
||||||
|
|
||||||
# отправляем уведомление всем залогиненным пользователям
|
# отправляем уведомление всем залогиненным пользователям
|
||||||
for uid in os.listdir('/run/user'):
|
for uid in os.listdir('/run/user'):
|
||||||
|
|
||||||
root_notify_command = 'sudo -u {} DISPLAY={} notify-send {} "Pr' \
|
root_notify_command = 'sudo -u {} DISPLAY={} notify-send {} "Pr' \
|
||||||
'eventing OOM" '.format(
|
'eventing OOM" '.format(
|
||||||
users_dict[uid], display, notify_options)
|
users_dict[uid], display, notify_options)
|
||||||
|
|
||||||
os.system(root_notify_command + info)
|
os.system(root_notify_command + info)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
# отправляем уведомление пользователю, который запустил nohang
|
# отправляем уведомление пользователю, который запустил nohang
|
||||||
user_notify_command = 'notify-send {} "Preventing OOM" '.format(
|
user_notify_command = 'notify-send {} "Preventing OOM" '.format(
|
||||||
notify_options)
|
notify_options)
|
||||||
|
|
||||||
os.system(user_notify_command + info)
|
os.system(user_notify_command + info)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def send_notify_black(signal, name, pid):
|
||||||
|
# текст отправляемого уведомления
|
||||||
|
info = '"<u>Nohang</u> sent <u>{}</u>\nto blacklisted proce' \
|
||||||
|
'ss <b>{}</b>, <i>Pid</i> <b>{}</b>" &'.format(
|
||||||
|
sig_dict[signal], name, pid)
|
||||||
|
if root:
|
||||||
|
# отправляем уведомление всем залогиненным пользователям
|
||||||
|
for uid in os.listdir('/run/user'):
|
||||||
|
root_notify_command = 'sudo -u {} DISPLAY={} notify-send {} "Pr' \
|
||||||
|
'eventing OOM" '.format(
|
||||||
|
users_dict[uid], display, notify_options)
|
||||||
|
os.system(root_notify_command + info)
|
||||||
|
else:
|
||||||
|
# отправляем уведомление пользователю, который запустил nohang
|
||||||
|
user_notify_command = 'notify-send {} "Preventing OOM" '.format(
|
||||||
|
notify_options)
|
||||||
|
os.system(user_notify_command + info)
|
||||||
|
|
||||||
|
|
||||||
|
def sleep_after_send_signal(signal):
|
||||||
|
if signal is 9:
|
||||||
|
if print_sleep_periods:
|
||||||
|
print(' sleep', min_delay_after_sigkill)
|
||||||
|
sleep(min_delay_after_sigterm)
|
||||||
|
else:
|
||||||
|
if print_sleep_periods:
|
||||||
|
print(' sleep', min_delay_after_sigterm)
|
||||||
|
sleep(min_delay_after_sigterm)
|
||||||
|
|
||||||
|
|
||||||
# принимает int (9 или 15)
|
# принимает int (9 или 15)
|
||||||
def find_victim_and_send_signal(signal):
|
def find_victim_and_send_signal_without_regexp_lists(signal):
|
||||||
|
|
||||||
# выставляем потолок для oom_score_adj всех процессов
|
# выставляем потолок для oom_score_adj всех процессов
|
||||||
if decrease_oom_score_adj and root:
|
if decrease_oom_score_adj and root:
|
||||||
@ -247,18 +271,160 @@ def find_victim_and_send_signal(signal):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
print(' oom_score {} < oom_score_min {}'.format(
|
print(' oom_score {} < oom_score_min {}'.format(
|
||||||
oom_score, oom_score_min)
|
oom_score, oom_score_min))
|
||||||
)
|
|
||||||
|
sleep_after_send_signal(signal)
|
||||||
|
|
||||||
|
|
||||||
|
# принимает int (9 или 15)
|
||||||
|
def find_victim_and_send_signal_with_regexp_lists(signal):
|
||||||
|
|
||||||
|
# выставляем потолок для oom_score_adj всех процессов
|
||||||
|
if decrease_oom_score_adj and root:
|
||||||
|
func_decrease_oom_score_adj(oom_score_adj_max)
|
||||||
|
|
||||||
|
# получаем список процессов ((pid, badness))
|
||||||
|
oom_list = []
|
||||||
|
|
||||||
|
# pid list
|
||||||
|
oom_black_list = []
|
||||||
|
|
||||||
|
for pid in os.listdir('/proc'):
|
||||||
|
if pid.isdigit() is not True:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
|
||||||
|
# пошла итерация для каждого существующего процесса
|
||||||
|
|
||||||
|
oom_score = int(rline1('/proc/' + pid + '/oom_score'))
|
||||||
|
|
||||||
|
name = pid_to_name(pid)
|
||||||
|
|
||||||
|
# если имя в списке,то пропускаем!
|
||||||
|
res = fullmatch(white_list, name)
|
||||||
|
if res is not None:
|
||||||
|
#print(' {} (Pid: {}) is in white list'.format(name, pid)),
|
||||||
|
continue
|
||||||
|
|
||||||
|
# если имя в черном списке - добавляем Pid в список для убийства
|
||||||
|
res = fullmatch(black_list, name)
|
||||||
|
if res is not None:
|
||||||
|
oom_black_list.append(pid)
|
||||||
|
#print(' {} (Pid: {}) is in black list'.format(name, pid)),
|
||||||
|
|
||||||
|
res = fullmatch(avoid_list, name)
|
||||||
|
if res is not None:
|
||||||
|
# тут уже получаем badness
|
||||||
|
oom_score = int(oom_score / avoid_factor)
|
||||||
|
#print(' {} (Pid: {}) is in avoid list'.format(name, pid)),
|
||||||
|
|
||||||
|
res = fullmatch(prefer_list, name)
|
||||||
|
if res is not None:
|
||||||
|
oom_score = int((oom_score + 1) * prefer_factor)
|
||||||
|
#print(' {} (Pid: {}) is in prefer list'.format(name, pid)),
|
||||||
|
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
oom_score = 0
|
||||||
|
except ProcessLookupError:
|
||||||
|
oom_score = 0
|
||||||
|
oom_list.append((pid, oom_score))
|
||||||
|
|
||||||
|
|
||||||
|
if oom_black_list != []:
|
||||||
|
|
||||||
|
print(' Black list is not empty')
|
||||||
|
|
||||||
|
for pid in oom_black_list:
|
||||||
|
|
||||||
|
name = pid_to_name(pid)
|
||||||
|
print(' Try to send the {} signal to blacklisted process {}, Pid {}'.format(
|
||||||
|
sig_dict[signal], name, pid))
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.kill(int(pid), signal)
|
||||||
|
print(' Success')
|
||||||
|
|
||||||
|
if desktop_notifications:
|
||||||
|
send_notify_black(signal, name, pid)
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(' No such process')
|
||||||
|
except ProcessLookupError:
|
||||||
|
print(' No such process')
|
||||||
|
except PermissionError:
|
||||||
|
print(' Operation not permitted')
|
||||||
|
|
||||||
|
# после отправки сигнала процессам из черного списка поспать и выйти из функции
|
||||||
|
#sleep_after_send_signal(signal)
|
||||||
|
#sleep(0.1)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
# получаем отсортированный по oom_score (по badness!) список пар (pid, oom_score)
|
||||||
|
pid_tuple_list = sorted(oom_list, key=itemgetter(1), reverse=True)[0]
|
||||||
|
|
||||||
|
# получаем максимальный oom_score
|
||||||
|
oom_score = pid_tuple_list[1]
|
||||||
|
|
||||||
|
# посылаем сигнал
|
||||||
|
if oom_score >= oom_score_min:
|
||||||
|
|
||||||
|
pid = pid_tuple_list[0]
|
||||||
|
|
||||||
|
name = pid_to_name(pid)
|
||||||
|
|
||||||
|
# находим VmRSS и VmSwap процесса, которому попытаемся послать сигнал
|
||||||
|
try:
|
||||||
|
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('\t')[1][:-4]))
|
||||||
|
continue
|
||||||
|
if n is vm_swap_index:
|
||||||
|
vm_swap = kib_to_mib(int(
|
||||||
|
line.split('\t')[1][:-4]))
|
||||||
|
break
|
||||||
|
except FileNotFoundError:
|
||||||
|
vm_rss = 0
|
||||||
|
vm_swap = 0
|
||||||
|
except ProcessLookupError:
|
||||||
|
vm_rss = 0
|
||||||
|
vm_swap = 0
|
||||||
|
except IndexError:
|
||||||
|
vm_rss = 0
|
||||||
|
vm_swap = 0
|
||||||
|
|
||||||
|
print(' Try to send the {} signal to {},\n Pid {}, Badness {}, V'
|
||||||
|
'mRSS {} MiB, VmSwap {} MiB'.format(
|
||||||
|
sig_dict[signal], name, pid, oom_score, vm_rss, vm_swap))
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.kill(int(pid), signal)
|
||||||
|
print(' Success')
|
||||||
|
|
||||||
|
if desktop_notifications:
|
||||||
|
send_notify(signal, name, pid, oom_score, vm_rss, vm_swap)
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(' No such process')
|
||||||
|
except ProcessLookupError:
|
||||||
|
print(' No such process')
|
||||||
|
|
||||||
# спать всегда или только при успешной отправке сигнала?
|
|
||||||
if signal is 9:
|
|
||||||
if print_sleep_periods:
|
|
||||||
print(' sleep', min_delay_after_sigkill)
|
|
||||||
sleep(min_delay_after_sigterm)
|
|
||||||
else:
|
else:
|
||||||
if print_sleep_periods:
|
print(' oom_score {} < oom_score_min {}'.format(
|
||||||
print(' sleep', min_delay_after_sigterm)
|
oom_score, oom_score_min))
|
||||||
sleep(min_delay_after_sigterm)
|
|
||||||
|
sleep_after_send_signal(signal)
|
||||||
|
|
||||||
|
|
||||||
|
def find_victim_and_send_signal(signal):
|
||||||
|
if use_regexp_lists:
|
||||||
|
find_victim_and_send_signal_with_regexp_lists(signal)
|
||||||
|
else:
|
||||||
|
find_victim_and_send_signal_without_regexp_lists(signal)
|
||||||
|
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
@ -370,6 +536,8 @@ except IndexError:
|
|||||||
# валидация всех параметров
|
# валидация всех параметров
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
if 'print_config' in config_dict:
|
if 'print_config' in config_dict:
|
||||||
print_config = config_dict['print_config']
|
print_config = config_dict['print_config']
|
||||||
if print_config == 'True':
|
if print_config == 'True':
|
||||||
@ -377,15 +545,15 @@ if 'print_config' in config_dict:
|
|||||||
elif print_config == 'False':
|
elif print_config == 'False':
|
||||||
print_config = False
|
print_config = False
|
||||||
else:
|
else:
|
||||||
print('Invalid print_config value {} (should be True or False)\nE'
|
print('Invalid print_config value {} (shou' \
|
||||||
'xit'.format(
|
'ld be True or False)\nExit'.format(print_config))
|
||||||
print_config))
|
|
||||||
exit()
|
exit()
|
||||||
else:
|
else:
|
||||||
print('Print_config not in config\nExit')
|
print('Print_config not in config\nExit')
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
if 'print_mem_check_results' in config_dict:
|
if 'print_mem_check_results' in config_dict:
|
||||||
print_mem_check_results = config_dict['print_mem_check_results']
|
print_mem_check_results = config_dict['print_mem_check_results']
|
||||||
if print_mem_check_results == 'True':
|
if print_mem_check_results == 'True':
|
||||||
@ -393,15 +561,16 @@ if 'print_mem_check_results' in config_dict:
|
|||||||
elif print_mem_check_results == 'False':
|
elif print_mem_check_results == 'False':
|
||||||
print_mem_check_results = False
|
print_mem_check_results = False
|
||||||
else:
|
else:
|
||||||
print('Invalid print_mem_check_results value {} (should be Tr'
|
print('Invalid print_mem_check_result' \
|
||||||
'ue or False)\nExit'.format(
|
's value {} (should be True or False)\nExit'.format(
|
||||||
print_mem_check_results))
|
print_mem_check_results))
|
||||||
exit()
|
exit()
|
||||||
else:
|
else:
|
||||||
print('print_mem_check_results not in config\nExit')
|
print('print_mem_check_results not in config\nExit')
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
if 'print_sleep_periods' in config_dict:
|
if 'print_sleep_periods' in config_dict:
|
||||||
print_sleep_periods = config_dict['print_sleep_periods']
|
print_sleep_periods = config_dict['print_sleep_periods']
|
||||||
if print_sleep_periods == 'True':
|
if print_sleep_periods == 'True':
|
||||||
@ -409,15 +578,15 @@ if 'print_sleep_periods' in config_dict:
|
|||||||
elif print_sleep_periods == 'False':
|
elif print_sleep_periods == 'False':
|
||||||
print_sleep_periods = False
|
print_sleep_periods = False
|
||||||
else:
|
else:
|
||||||
print('Invalid print_sleep_periods value {} (should be True or F'
|
print('Invalid print_sleep_periods value {} (shou' \
|
||||||
'alse)\nExit'.format(
|
'ld be True or False)\nExit'.format(print_sleep_periods))
|
||||||
print_sleep_periods))
|
|
||||||
exit()
|
exit()
|
||||||
else:
|
else:
|
||||||
print('print_sleep_periods not in config\nExit')
|
print('print_sleep_periods not in config\nExit')
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
if 'mlockall' in config_dict:
|
if 'mlockall' in config_dict:
|
||||||
mlockall = config_dict['mlockall']
|
mlockall = config_dict['mlockall']
|
||||||
if mlockall == 'True':
|
if mlockall == 'True':
|
||||||
@ -427,51 +596,48 @@ if 'mlockall' in config_dict:
|
|||||||
else:
|
else:
|
||||||
print(
|
print(
|
||||||
'Invalid mlockall value {} (should be True or False)\nExit'.format(
|
'Invalid mlockall value {} (should be True or False)\nExit'.format(
|
||||||
mlockall
|
mlockall))
|
||||||
)
|
|
||||||
)
|
|
||||||
exit()
|
exit()
|
||||||
else:
|
else:
|
||||||
print('mlockall not in config\nExit')
|
print('mlockall not in config\nExit')
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
if 'self_nice' in config_dict:
|
if 'self_nice' in config_dict:
|
||||||
self_nice = config_dict['self_nice']
|
self_nice = string_to_int_convert_test(config_dict['self_nice'])
|
||||||
if string_to_int_convert_test(self_nice) is None:
|
if self_nice is None:
|
||||||
print('Invalid self_nice value, not integer\nExit')
|
print('Invalid self_nice value, not integer\nExit')
|
||||||
exit()
|
exit()
|
||||||
else:
|
if self_nice < -20 or self_nice > 19:
|
||||||
self_nice = int(self_nice)
|
print('Недопустимое значение self_nice\nExit')
|
||||||
if self_nice < -20 or self_nice > 19:
|
exit()
|
||||||
print('Недопустимое значение self_nice\nExit')
|
|
||||||
exit()
|
|
||||||
else:
|
else:
|
||||||
print('self_nice not in config\nExit')
|
print('self_nice not in config\nExit')
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
if 'self_oom_score_adj' in config_dict:
|
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 = string_to_int_convert_test(self_oom_score_adj)
|
config_dict['self_oom_score_adj'])
|
||||||
if self_oom_score_adj is None:
|
if self_oom_score_adj is None:
|
||||||
print('Invalid self_oom_score_adj value, not integer\nExit')
|
print('Invalid self_oom_score_adj value, not integer\nExit')
|
||||||
exit()
|
exit()
|
||||||
else:
|
if self_oom_score_adj < -1000 or self_oom_score_adj > 1000:
|
||||||
if self_oom_score_adj < -1000 or self_oom_score_adj > 1000:
|
print('Недопустимое значение self_oom_score_adj\nExit')
|
||||||
print('Недопустимое значение self_oom_score_adj\nExit')
|
exit()
|
||||||
exit()
|
|
||||||
else:
|
else:
|
||||||
print('self_oom_score_adj not in config\nExit')
|
print('self_oom_score_adj not in config\nExit')
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
if 'rate_mem' in config_dict:
|
if 'rate_mem' in config_dict:
|
||||||
rate_mem = string_to_float_convert_test(config_dict['rate_mem'])
|
rate_mem = string_to_float_convert_test(config_dict['rate_mem'])
|
||||||
if rate_mem is None:
|
if rate_mem is None:
|
||||||
print('Invalid rate_mem value, not float\nExit')
|
print('Invalid rate_mem value, not float\nExit')
|
||||||
exit()
|
exit()
|
||||||
rate_mem = float(rate_mem)
|
|
||||||
if rate_mem <= 0:
|
if rate_mem <= 0:
|
||||||
print('rate_mem должен быть положительным\nExit')
|
print('rate_mem должен быть положительным\nExit')
|
||||||
exit()
|
exit()
|
||||||
@ -480,12 +646,12 @@ else:
|
|||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
if 'rate_swap' in config_dict:
|
if 'rate_swap' in config_dict:
|
||||||
rate_swap = string_to_float_convert_test(config_dict['rate_swap'])
|
rate_swap = string_to_float_convert_test(config_dict['rate_swap'])
|
||||||
if rate_swap is None:
|
if rate_swap is None:
|
||||||
print('Invalid rate_swap value, not float\nExit')
|
print('Invalid rate_swap value, not float\nExit')
|
||||||
exit()
|
exit()
|
||||||
rate_swap = float(rate_swap)
|
|
||||||
if rate_swap <= 0:
|
if rate_swap <= 0:
|
||||||
print('rate_swap должен быть положительным\nExit')
|
print('rate_swap должен быть положительным\nExit')
|
||||||
exit()
|
exit()
|
||||||
@ -494,12 +660,12 @@ else:
|
|||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
if 'rate_zram' in config_dict:
|
if 'rate_zram' in config_dict:
|
||||||
rate_zram = string_to_float_convert_test(config_dict['rate_zram'])
|
rate_zram = string_to_float_convert_test(config_dict['rate_zram'])
|
||||||
if rate_zram is None:
|
if rate_zram is None:
|
||||||
print('Invalid rate_zram value, not float\nExit')
|
print('Invalid rate_zram value, not float\nExit')
|
||||||
exit()
|
exit()
|
||||||
rate_zram = float(rate_zram)
|
|
||||||
if rate_zram <= 0:
|
if rate_zram <= 0:
|
||||||
print('rate_zram должен быть положительным\nExit')
|
print('rate_zram должен быть положительным\nExit')
|
||||||
exit()
|
exit()
|
||||||
@ -508,6 +674,7 @@ else:
|
|||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# НУЖНА ВАЛИДАЦИЯ НА МЕСТЕ!
|
||||||
if 'mem_min_sigterm' in config_dict:
|
if 'mem_min_sigterm' in config_dict:
|
||||||
mem_min_sigterm = config_dict['mem_min_sigterm']
|
mem_min_sigterm = config_dict['mem_min_sigterm']
|
||||||
else:
|
else:
|
||||||
@ -515,6 +682,7 @@ else:
|
|||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# НУЖНА ВАЛИДАЦИЯ НА МЕСТЕ!
|
||||||
if 'mem_min_sigkill' in config_dict:
|
if 'mem_min_sigkill' in config_dict:
|
||||||
mem_min_sigkill = config_dict['mem_min_sigkill']
|
mem_min_sigkill = config_dict['mem_min_sigkill']
|
||||||
else:
|
else:
|
||||||
@ -522,6 +690,7 @@ else:
|
|||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# НУЖНА ВАЛИДАЦИЯ НА МЕСТЕ!
|
||||||
if 'swap_min_sigterm' in config_dict:
|
if 'swap_min_sigterm' in config_dict:
|
||||||
swap_min_sigterm = config_dict['swap_min_sigterm']
|
swap_min_sigterm = config_dict['swap_min_sigterm']
|
||||||
else:
|
else:
|
||||||
@ -529,6 +698,7 @@ else:
|
|||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# НУЖНА ВАЛИДАЦИЯ НА МЕСТЕ!
|
||||||
if 'swap_min_sigkill' in config_dict:
|
if 'swap_min_sigkill' in config_dict:
|
||||||
swap_min_sigkill = config_dict['swap_min_sigkill']
|
swap_min_sigkill = config_dict['swap_min_sigkill']
|
||||||
else:
|
else:
|
||||||
@ -536,6 +706,7 @@ else:
|
|||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# НУЖНА ВАЛИДАЦИЯ НА МЕСТЕ!
|
||||||
if 'zram_max_sigterm' in config_dict:
|
if 'zram_max_sigterm' in config_dict:
|
||||||
zram_max_sigterm = config_dict['zram_max_sigterm']
|
zram_max_sigterm = config_dict['zram_max_sigterm']
|
||||||
else:
|
else:
|
||||||
@ -543,6 +714,7 @@ else:
|
|||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# НУЖНА ВАЛИДАЦИЯ НА МЕСТЕ!
|
||||||
if 'zram_max_sigkill' in config_dict:
|
if 'zram_max_sigkill' in config_dict:
|
||||||
zram_max_sigkill = config_dict['zram_max_sigkill']
|
zram_max_sigkill = config_dict['zram_max_sigkill']
|
||||||
else:
|
else:
|
||||||
@ -550,13 +722,13 @@ else:
|
|||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
if 'min_delay_after_sigterm' in config_dict:
|
if 'min_delay_after_sigterm' in config_dict:
|
||||||
min_delay_after_sigterm = string_to_float_convert_test(
|
min_delay_after_sigterm = string_to_float_convert_test(
|
||||||
config_dict['m' 'in_delay_after_sigterm'])
|
config_dict['min_delay_after_sigterm'])
|
||||||
if min_delay_after_sigterm is None:
|
if min_delay_after_sigterm is None:
|
||||||
print('Invalid min_delay_after_sigterm value, not float\nExit')
|
print('Invalid min_delay_after_sigterm value, not float\nExit')
|
||||||
exit()
|
exit()
|
||||||
min_delay_after_sigterm = float(min_delay_after_sigterm)
|
|
||||||
if min_delay_after_sigterm < 0:
|
if min_delay_after_sigterm < 0:
|
||||||
print('min_delay_after_sigterm должен быть неотрицательным\nExit')
|
print('min_delay_after_sigterm должен быть неотрицательным\nExit')
|
||||||
exit()
|
exit()
|
||||||
@ -565,13 +737,13 @@ else:
|
|||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
if 'min_delay_after_sigkill' in config_dict:
|
if 'min_delay_after_sigkill' in config_dict:
|
||||||
min_delay_after_sigkill = string_to_float_convert_test(
|
min_delay_after_sigkill = string_to_float_convert_test(
|
||||||
config_dict['mi' 'n_delay_after_sigkill'])
|
config_dict['min_delay_after_sigkill'])
|
||||||
if min_delay_after_sigkill is None:
|
if min_delay_after_sigkill is None:
|
||||||
print('Invalid min_delay_after_sigkill value, not float\nExit')
|
print('Invalid min_delay_after_sigkill value, not float\nExit')
|
||||||
exit()
|
exit()
|
||||||
min_delay_after_sigkill = float(min_delay_after_sigkill)
|
|
||||||
if min_delay_after_sigkill < 0:
|
if min_delay_after_sigkill < 0:
|
||||||
print('min_delay_after_sigkill должен быть неотрицательным\nExit')
|
print('min_delay_after_sigkill должен быть неотрицательным\nExit')
|
||||||
exit()
|
exit()
|
||||||
@ -580,21 +752,22 @@ else:
|
|||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
if 'oom_score_min' in config_dict:
|
if 'oom_score_min' in config_dict:
|
||||||
oom_score_min = config_dict['oom_score_min']
|
oom_score_min = string_to_int_convert_test(
|
||||||
if string_to_int_convert_test(oom_score_min) is None:
|
config_dict['oom_score_min'])
|
||||||
|
if oom_score_min is None:
|
||||||
print('Invalid oom_score_min value, not integer\nExit')
|
print('Invalid oom_score_min value, not integer\nExit')
|
||||||
exit()
|
exit()
|
||||||
else:
|
if oom_score_min < 0 or oom_score_min > 1000:
|
||||||
oom_score_min = int(oom_score_min)
|
print('Недопустимое значение oom_score_min\nExit')
|
||||||
if oom_score_min < 0 or oom_score_min > 1000:
|
exit()
|
||||||
print('Недопустимое значение oom_score_min\nExit')
|
|
||||||
exit()
|
|
||||||
else:
|
else:
|
||||||
print('oom_score_min not in config\nExit')
|
print('oom_score_min not in config\nExit')
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
if 'decrease_oom_score_adj' in config_dict:
|
if 'decrease_oom_score_adj' in config_dict:
|
||||||
decrease_oom_score_adj = config_dict['decrease_oom_score_adj']
|
decrease_oom_score_adj = config_dict['decrease_oom_score_adj']
|
||||||
if decrease_oom_score_adj == 'True':
|
if decrease_oom_score_adj == 'True':
|
||||||
@ -602,19 +775,18 @@ if 'decrease_oom_score_adj' in config_dict:
|
|||||||
elif decrease_oom_score_adj == 'False':
|
elif decrease_oom_score_adj == 'False':
|
||||||
decrease_oom_score_adj = False
|
decrease_oom_score_adj = False
|
||||||
else:
|
else:
|
||||||
print('invalid decrease_oom_score_adj value {} (should be Tru'
|
print('invalid decrease_oom_score_adj value {} (should b' \
|
||||||
'e or False)\nExit'.format(
|
'e True or False\nExit'.format(decrease_oom_score_adj))
|
||||||
decrease_oom_score_adj))
|
|
||||||
exit()
|
exit()
|
||||||
else:
|
else:
|
||||||
print('decrease_oom_score_adj not in config\nExit')
|
print('decrease_oom_score_adj not in config\nExit')
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
if 'oom_score_adj_max' in config_dict:
|
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(
|
||||||
|
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:
|
if oom_score_adj_max is None:
|
||||||
print('Invalid oom_score_adj_max value, not integer\nExit')
|
print('Invalid oom_score_adj_max value, not integer\nExit')
|
||||||
exit()
|
exit()
|
||||||
@ -626,6 +798,7 @@ else:
|
|||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
if 'desktop_notifications' in config_dict:
|
if 'desktop_notifications' in config_dict:
|
||||||
desktop_notifications = config_dict['desktop_notifications']
|
desktop_notifications = config_dict['desktop_notifications']
|
||||||
if desktop_notifications == 'True':
|
if desktop_notifications == 'True':
|
||||||
@ -640,15 +813,16 @@ if 'desktop_notifications' in config_dict:
|
|||||||
elif desktop_notifications == 'False':
|
elif desktop_notifications == 'False':
|
||||||
desktop_notifications = False
|
desktop_notifications = False
|
||||||
else:
|
else:
|
||||||
print('Invalid desktop_notifications value {} (should be Tru'
|
print('Invalid desktop_notifications value {} (shoul' \
|
||||||
'e or False)\nExit'.format(
|
'd be True or False)\nExit'.format(
|
||||||
desktop_notifications))
|
desktop_notifications))
|
||||||
exit()
|
exit()
|
||||||
else:
|
else:
|
||||||
print('desktop_notifications not in config\nExit')
|
print('desktop_notifications not in config\nExit')
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
if 'notify_options' in config_dict:
|
if 'notify_options' in config_dict:
|
||||||
notify_options = config_dict['notify_options'].strip()
|
notify_options = config_dict['notify_options'].strip()
|
||||||
else:
|
else:
|
||||||
@ -656,12 +830,90 @@ else:
|
|||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
if 'display' in config_dict:
|
if 'display' in config_dict:
|
||||||
display = config_dict['display'].strip()
|
display = config_dict['display'].strip()
|
||||||
else:
|
else:
|
||||||
print('display not in config\nExit')
|
print('display not in config\nExit')
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
if 'use_regexp_lists' in config_dict:
|
||||||
|
use_regexp_lists = config_dict['use_regexp_lists']
|
||||||
|
if use_regexp_lists == 'True':
|
||||||
|
use_regexp_lists = True
|
||||||
|
elif use_regexp_lists == 'False':
|
||||||
|
use_regexp_lists = False
|
||||||
|
else:
|
||||||
|
print('invalid use_regexp_lists value {} (shoul' \
|
||||||
|
'd be True or False)\nExit'.format(use_regexp_lists))
|
||||||
|
exit()
|
||||||
|
else:
|
||||||
|
print('use_regexp_lists not in config\nExit')
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
if 'white_list' in config_dict:
|
||||||
|
white_list = config_dict['white_list'].strip()
|
||||||
|
else:
|
||||||
|
print('white_list not in config\nExit')
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
if 'black_list' in config_dict:
|
||||||
|
black_list = config_dict['black_list'].strip()
|
||||||
|
else:
|
||||||
|
print('black_list not in config\nExit')
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
if 'prefer_list' in config_dict:
|
||||||
|
prefer_list = config_dict['prefer_list'].strip()
|
||||||
|
else:
|
||||||
|
print('prefer_list not in config\nExit')
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
if 'prefer_factor' in config_dict:
|
||||||
|
prefer_factor = string_to_float_convert_test(config_dict['prefer_factor'])
|
||||||
|
if prefer_factor is None:
|
||||||
|
print('Invalid prefer_factor value, not float\nExit')
|
||||||
|
exit()
|
||||||
|
if prefer_factor <= 0:
|
||||||
|
print('prefer_factor должен быть положительным\nExit')
|
||||||
|
exit()
|
||||||
|
else:
|
||||||
|
print('prefer_factor not in config\nExit')
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
if 'avoid_list' in config_dict:
|
||||||
|
avoid_list = config_dict['avoid_list'].strip()
|
||||||
|
else:
|
||||||
|
print('avoid_list not in config\nExit')
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
if 'avoid_factor' in config_dict:
|
||||||
|
avoid_factor = string_to_float_convert_test(config_dict['avoid_factor'])
|
||||||
|
if avoid_factor is None:
|
||||||
|
print('Invalid avoid_factor value, not float\nExit')
|
||||||
|
exit()
|
||||||
|
if avoid_factor <= 0:
|
||||||
|
print('avoid_factor должен быть положительным\nExit')
|
||||||
|
exit()
|
||||||
|
else:
|
||||||
|
print('avoid_factor not in config\nExit')
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
# получение уровней в кибибайтах
|
# получение уровней в кибибайтах
|
||||||
@ -813,6 +1065,17 @@ if print_config:
|
|||||||
print('desktop_notifications: {}'.format(desktop_notifications))
|
print('desktop_notifications: {}'.format(desktop_notifications))
|
||||||
if desktop_notifications:
|
if desktop_notifications:
|
||||||
print('notify_options: {}'.format(notify_options))
|
print('notify_options: {}'.format(notify_options))
|
||||||
|
print('display: {}'.format(display))
|
||||||
|
|
||||||
|
print('\nVII. BLACK, WHITE, AVOID AND PREFER LISTS')
|
||||||
|
print('use_regexp_lists: {}'.format(use_regexp_lists))
|
||||||
|
if use_regexp_lists:
|
||||||
|
print('white_list: {}'.format(white_list))
|
||||||
|
print('black_list: {}'.format(black_list))
|
||||||
|
print('prefer_list: {}'.format(prefer_list))
|
||||||
|
print('prefer_factor: {}'.format(prefer_factor))
|
||||||
|
print('avoid_list: {}'.format(avoid_list))
|
||||||
|
print('avoid_factor: {}'.format(avoid_factor))
|
||||||
|
|
||||||
|
|
||||||
# для рассчета ширины столбцов при печати mem и zram
|
# для рассчета ширины столбцов при печати mem и zram
|
||||||
@ -950,11 +1213,11 @@ while True:
|
|||||||
find_victim_and_send_signal(15)
|
find_victim_and_send_signal(15)
|
||||||
|
|
||||||
# задание периода в зависимости от рейтов и уровней доступной памяти
|
# задание периода в зависимости от рейтов и уровней доступной памяти
|
||||||
t_mem = mem_available / 1000000.0 / rate_mem
|
t_mem = mem_available / rate_mem
|
||||||
|
|
||||||
t_swap = swap_free / 10000000.0 / rate_swap
|
t_swap = swap_free / rate_swap
|
||||||
|
|
||||||
t_zram = (mem_total * 0.8 - mem_used_zram) / 1000000.0 / rate_zram
|
t_zram = (mem_total * 0.8 - mem_used_zram) / rate_zram
|
||||||
if t_zram < 0.01:
|
if t_zram < 0.01:
|
||||||
t_zram = 0.01
|
t_zram = 0.01
|
||||||
|
|
||||||
@ -972,3 +1235,4 @@ while True:
|
|||||||
sleep(t)
|
sleep(t)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
57
nohang.conf
57
nohang.conf
@ -24,7 +24,7 @@ print_mem_check_results = True
|
|||||||
Допустимые значения: True и False
|
Допустимые значения: True и False
|
||||||
(В этой ветке по дефолту True)
|
(В этой ветке по дефолту True)
|
||||||
|
|
||||||
print_sleep_periods = True
|
print_sleep_periods = False
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
|
|
||||||
@ -69,11 +69,11 @@ self_oom_score_adj = -1000
|
|||||||
|
|
||||||
В дефолтных настройках на данной интенсивности демон работает
|
В дефолтных настройках на данной интенсивности демон работает
|
||||||
достаточно хорошо, успешно справляясь с резкими скачками потребления
|
достаточно хорошо, успешно справляясь с резкими скачками потребления
|
||||||
памяти.
|
памяти.
|
||||||
|
|
||||||
rate_mem = 6
|
rate_mem = 6000000
|
||||||
rate_swap = 3
|
rate_swap = 3000000
|
||||||
rate_zram = 1
|
rate_zram = 1000000
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
|
|
||||||
@ -115,6 +115,11 @@ zram_max_sigkill = 60 %
|
|||||||
|
|
||||||
Значение должно быть целым числом из диапазона [0; 1000]
|
Значение должно быть целым числом из диапазона [0; 1000]
|
||||||
|
|
||||||
|
Процессы из black_list (см ниже) получат сигнал вне зависимости
|
||||||
|
от значения их oom_score.
|
||||||
|
|
||||||
|
Может min_badness с учетом списков?
|
||||||
|
|
||||||
oom_score_min = 20
|
oom_score_min = 20
|
||||||
|
|
||||||
Минимальная задержка после отправки соответствующих сигналов
|
Минимальная задержка после отправки соответствующих сигналов
|
||||||
@ -136,11 +141,11 @@ min_delay_after_sigkill = 3
|
|||||||
|
|
||||||
Требует root прав.
|
Требует root прав.
|
||||||
|
|
||||||
decrease_oom_score_adj = True
|
decrease_oom_score_adj = False
|
||||||
|
|
||||||
Допустимые значения - целые числа из диапазона [0; 1000]
|
Допустимые значения - целые числа из диапазона [0; 1000]
|
||||||
|
|
||||||
oom_score_adj_max = 30
|
oom_score_adj_max = 20
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
|
|
||||||
@ -170,3 +175,41 @@ notify_options =
|
|||||||
|
|
||||||
display = :0
|
display = :0
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
VII. BLACK, WHITE, AVOID AND PREFER LISTS
|
||||||
|
|
||||||
|
Можно задать списки с помощью Perl-compatible regular expressions.
|
||||||
|
|
||||||
|
Включение этой опции замедляет поиск жертвы, так как
|
||||||
|
имена всех процессов сравниваются с regexp паттернами всех
|
||||||
|
списков.
|
||||||
|
|
||||||
|
use_regexp_lists = False
|
||||||
|
|
||||||
|
Процессы из белого списка не получат сигнал.
|
||||||
|
|
||||||
|
white_list = ^(Xorg|sshd)$
|
||||||
|
|
||||||
|
При нехватке памяти все процессы из черного списка получат сигнал.
|
||||||
|
|
||||||
|
black_list = ^()$
|
||||||
|
|
||||||
|
Список предпочтительных для убийства процессов.
|
||||||
|
Badness процессов из prefer_list будет умножено на
|
||||||
|
prefer_factor перед выбором жертвы. На самом деле формула
|
||||||
|
для нахождения badness такая:
|
||||||
|
badness = (oom_score + 1) * prefer_factor
|
||||||
|
|
||||||
|
prefer_factor и avoid_factor должны быть положительными числами.
|
||||||
|
|
||||||
|
prefer_list = ^()$
|
||||||
|
prefer_factor = 2
|
||||||
|
|
||||||
|
Список нежелательных для убийства процессов.
|
||||||
|
Badness процессов из avoid_list будет поделено на
|
||||||
|
avoid_factor перед выбором жертвы.
|
||||||
|
|
||||||
|
avoid_list = ^()$
|
||||||
|
avoid_factor = 2
|
||||||
|
|
||||||
|
8
purge.sh
8
purge.sh
@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash -v
|
#!/bin/bash -v
|
||||||
systemctl stop nohang
|
systemctl stop nohang
|
||||||
systemctl disable nohang
|
systemctl disable nohang
|
||||||
rm -f /usr/local/share/man/man1/nohang.1.gz
|
rm /usr/local/share/man/man1/nohang.1.gz
|
||||||
rm -f /etc/systemd/system/nohang.service
|
rm /etc/systemd/system/nohang.service
|
||||||
rm -rf /etc/nohang
|
rm -r /etc/nohang
|
||||||
rm -f /usr/local/bin/nohang
|
rm /usr/local/bin/nohang
|
||||||
|
Loading…
Reference in New Issue
Block a user