diff --git a/.gitignore b/.gitignore
index 91b4ee2..d1f3790 100644
--- a/.gitignore
+++ b/.gitignore
@@ -107,11 +107,3 @@ venv.bak/
# Kate
.kate-swp
-# man
-*.1.gz
-
-
-
-
-
-
diff --git a/README.md b/README.md
index 6ad4059..d1515ac 100644
--- a/README.md
+++ b/README.md
@@ -57,6 +57,7 @@ https://2ch.hk/s/res/2310304.html#2311483, https://archive.li/idixk
- verbosity: опциональность печати параметров конфига при старте программы, опциональность печати результатов проверки памяти и времени между проверками памяти
- возможность предотвращения избыточного убийства процессов с помощью задания миниального `oom_score` для убиваемых процессов и установка минимальной задержки просле отправки сигналов (параметры конфига `min_delay_after_sigkill` и `min_delay_after_sigterm`)
- возможность показа десктопных уведомлений c помощью `notify-send`, с показом сигнала (`SIGTERM` или `SIGKILL`), который отправлен процессу, а также `Pid`, `oom_score`, `VmRSS`, `VmSwap`, которыми обладал процесс перед получением сигнала.
+- поддержка white, black, prefer и avoid списков с использованием Perl-compatible regular expressions
- наличие `man` страницы
- наличие установщика для пользователей `systemd`
- протестировано на `Debian 9 x86_64`, `Debian 8 i386`, `Fedora 28 x86_64`
diff --git a/nohang b/nohang
index c508b19..b3f14f0 100755
--- a/nohang
+++ b/nohang
@@ -10,6 +10,7 @@ import os
from operator import itemgetter
from time import sleep
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):
-
# текст отправляемого уведомления
info = '"Nohang sent {} \nto the process {} \nP' \
- 'id: {} \noom_score: {} \nVmRSS: {} \nBadness: {} \nVmRSS: {} MiB \nVmSwap: {} MiB" &'.format(
sig_dict[signal], name, pid, oom_score, vm_rss, vm_swap)
-
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 send_notify_black(signal, name, pid):
+ # текст отправляемого уведомления
+ info = '"Nohang sent {}\nto blacklisted proce' \
+ 'ss {}, Pid {}" &'.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)
-def find_victim_and_send_signal(signal):
+def find_victim_and_send_signal_without_regexp_lists(signal):
# выставляем потолок для oom_score_adj всех процессов
if decrease_oom_score_adj and root:
@@ -247,18 +271,160 @@ def find_victim_and_send_signal(signal):
else:
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:
- if print_sleep_periods:
- print(' sleep', min_delay_after_sigterm)
- sleep(min_delay_after_sigterm)
+ print(' oom_score {} < oom_score_min {}'.format(
+ oom_score, oom_score_min))
+
+ 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:
print_config = config_dict['print_config']
if print_config == 'True':
@@ -377,15 +545,15 @@ if 'print_config' in config_dict:
elif print_config == 'False':
print_config = False
else:
- print('Invalid print_config value {} (should be True or False)\nE'
- 'xit'.format(
- print_config))
+ print('Invalid print_config value {} (shou' \
+ 'ld be True or False)\nExit'.format(print_config))
exit()
else:
print('Print_config not in config\nExit')
exit()
+# OK
if 'print_mem_check_results' in config_dict:
print_mem_check_results = config_dict['print_mem_check_results']
if print_mem_check_results == 'True':
@@ -393,15 +561,16 @@ if 'print_mem_check_results' in config_dict:
elif print_mem_check_results == 'False':
print_mem_check_results = False
else:
- print('Invalid print_mem_check_results value {} (should be Tr'
- 'ue or False)\nExit'.format(
- print_mem_check_results))
+ print('Invalid print_mem_check_result' \
+ 's value {} (should be True or False)\nExit'.format(
+ print_mem_check_results))
exit()
else:
print('print_mem_check_results not in config\nExit')
exit()
+# OK
if 'print_sleep_periods' in config_dict:
print_sleep_periods = config_dict['print_sleep_periods']
if print_sleep_periods == 'True':
@@ -409,15 +578,15 @@ if 'print_sleep_periods' in config_dict:
elif print_sleep_periods == 'False':
print_sleep_periods = False
else:
- print('Invalid print_sleep_periods value {} (should be True or F'
- 'alse)\nExit'.format(
- print_sleep_periods))
+ print('Invalid print_sleep_periods value {} (shou' \
+ 'ld be True or False)\nExit'.format(print_sleep_periods))
exit()
else:
print('print_sleep_periods not in config\nExit')
exit()
+# OK
if 'mlockall' in config_dict:
mlockall = config_dict['mlockall']
if mlockall == 'True':
@@ -427,51 +596,48 @@ if 'mlockall' in config_dict:
else:
print(
'Invalid mlockall value {} (should be True or False)\nExit'.format(
- mlockall
- )
- )
+ mlockall))
exit()
else:
print('mlockall not in config\nExit')
exit()
+# OK
if 'self_nice' in config_dict:
- self_nice = config_dict['self_nice']
- if string_to_int_convert_test(self_nice) is None:
+ self_nice = string_to_int_convert_test(config_dict['self_nice'])
+ if self_nice is None:
print('Invalid self_nice value, not integer\nExit')
exit()
- else:
- self_nice = int(self_nice)
- if self_nice < -20 or self_nice > 19:
- print('Недопустимое значение self_nice\nExit')
- exit()
+ if self_nice < -20 or self_nice > 19:
+ print('Недопустимое значение self_nice\nExit')
+ exit()
else:
print('self_nice not in config\nExit')
exit()
+# OK
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)
+ self_oom_score_adj = string_to_int_convert_test(
+ config_dict['self_oom_score_adj'])
if self_oom_score_adj is None:
print('Invalid self_oom_score_adj value, not integer\nExit')
exit()
- else:
- if self_oom_score_adj < -1000 or self_oom_score_adj > 1000:
- print('Недопустимое значение self_oom_score_adj\nExit')
- exit()
+ if self_oom_score_adj < -1000 or self_oom_score_adj > 1000:
+ print('Недопустимое значение self_oom_score_adj\nExit')
+ exit()
else:
print('self_oom_score_adj not in config\nExit')
exit()
+# OK
if 'rate_mem' in config_dict:
rate_mem = string_to_float_convert_test(config_dict['rate_mem'])
if rate_mem is None:
print('Invalid rate_mem value, not float\nExit')
exit()
- rate_mem = float(rate_mem)
if rate_mem <= 0:
print('rate_mem должен быть положительным\nExit')
exit()
@@ -480,12 +646,12 @@ else:
exit()
+# OK
if 'rate_swap' in config_dict:
rate_swap = string_to_float_convert_test(config_dict['rate_swap'])
if rate_swap is None:
print('Invalid rate_swap value, not float\nExit')
exit()
- rate_swap = float(rate_swap)
if rate_swap <= 0:
print('rate_swap должен быть положительным\nExit')
exit()
@@ -494,12 +660,12 @@ else:
exit()
+# OK
if 'rate_zram' in config_dict:
rate_zram = string_to_float_convert_test(config_dict['rate_zram'])
if rate_zram is None:
print('Invalid rate_zram value, not float\nExit')
exit()
- rate_zram = float(rate_zram)
if rate_zram <= 0:
print('rate_zram должен быть положительным\nExit')
exit()
@@ -508,6 +674,7 @@ else:
exit()
+# НУЖНА ВАЛИДАЦИЯ НА МЕСТЕ!
if 'mem_min_sigterm' in config_dict:
mem_min_sigterm = config_dict['mem_min_sigterm']
else:
@@ -515,6 +682,7 @@ else:
exit()
+# НУЖНА ВАЛИДАЦИЯ НА МЕСТЕ!
if 'mem_min_sigkill' in config_dict:
mem_min_sigkill = config_dict['mem_min_sigkill']
else:
@@ -522,6 +690,7 @@ else:
exit()
+# НУЖНА ВАЛИДАЦИЯ НА МЕСТЕ!
if 'swap_min_sigterm' in config_dict:
swap_min_sigterm = config_dict['swap_min_sigterm']
else:
@@ -529,6 +698,7 @@ else:
exit()
+# НУЖНА ВАЛИДАЦИЯ НА МЕСТЕ!
if 'swap_min_sigkill' in config_dict:
swap_min_sigkill = config_dict['swap_min_sigkill']
else:
@@ -536,6 +706,7 @@ else:
exit()
+# НУЖНА ВАЛИДАЦИЯ НА МЕСТЕ!
if 'zram_max_sigterm' in config_dict:
zram_max_sigterm = config_dict['zram_max_sigterm']
else:
@@ -543,6 +714,7 @@ else:
exit()
+# НУЖНА ВАЛИДАЦИЯ НА МЕСТЕ!
if 'zram_max_sigkill' in config_dict:
zram_max_sigkill = config_dict['zram_max_sigkill']
else:
@@ -550,13 +722,13 @@ else:
exit()
+# OK
if 'min_delay_after_sigterm' in config_dict:
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:
print('Invalid min_delay_after_sigterm value, not float\nExit')
exit()
- min_delay_after_sigterm = float(min_delay_after_sigterm)
if min_delay_after_sigterm < 0:
print('min_delay_after_sigterm должен быть неотрицательным\nExit')
exit()
@@ -565,13 +737,13 @@ else:
exit()
+# OK
if 'min_delay_after_sigkill' in config_dict:
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:
print('Invalid min_delay_after_sigkill value, not float\nExit')
exit()
- min_delay_after_sigkill = float(min_delay_after_sigkill)
if min_delay_after_sigkill < 0:
print('min_delay_after_sigkill должен быть неотрицательным\nExit')
exit()
@@ -580,21 +752,22 @@ else:
exit()
+# OK
if 'oom_score_min' in config_dict:
- oom_score_min = config_dict['oom_score_min']
- if string_to_int_convert_test(oom_score_min) is None:
+ oom_score_min = string_to_int_convert_test(
+ config_dict['oom_score_min'])
+ if oom_score_min is None:
print('Invalid oom_score_min value, not integer\nExit')
exit()
- else:
- oom_score_min = int(oom_score_min)
- if oom_score_min < 0 or oom_score_min > 1000:
- print('Недопустимое значение oom_score_min\nExit')
- exit()
+ if oom_score_min < 0 or oom_score_min > 1000:
+ print('Недопустимое значение oom_score_min\nExit')
+ exit()
else:
print('oom_score_min not in config\nExit')
exit()
+# OK
if 'decrease_oom_score_adj' in config_dict:
decrease_oom_score_adj = config_dict['decrease_oom_score_adj']
if decrease_oom_score_adj == 'True':
@@ -602,19 +775,18 @@ 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 b' \
+ 'e True or False\nExit'.format(decrease_oom_score_adj))
exit()
else:
print('decrease_oom_score_adj not in config\nExit')
exit()
+# OK
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)
+ oom_score_adj_max = string_to_int_convert_test(
+ config_dict['oom_score_adj_max'])
if oom_score_adj_max is None:
print('Invalid oom_score_adj_max value, not integer\nExit')
exit()
@@ -626,6 +798,7 @@ else:
exit()
+# OK
if 'desktop_notifications' in config_dict:
desktop_notifications = config_dict['desktop_notifications']
if desktop_notifications == 'True':
@@ -640,15 +813,16 @@ if 'desktop_notifications' in config_dict:
elif desktop_notifications == 'False':
desktop_notifications = False
else:
- print('Invalid desktop_notifications value {} (should be Tru'
- 'e or False)\nExit'.format(
- desktop_notifications))
+ print('Invalid desktop_notifications value {} (shoul' \
+ 'd be True or False)\nExit'.format(
+ desktop_notifications))
exit()
else:
print('desktop_notifications not in config\nExit')
exit()
+# OK
if 'notify_options' in config_dict:
notify_options = config_dict['notify_options'].strip()
else:
@@ -656,12 +830,90 @@ else:
exit()
+# OK
if 'display' in config_dict:
display = config_dict['display'].strip()
else:
print('display not in config\nExit')
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))
if desktop_notifications:
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
@@ -950,11 +1213,11 @@ while True:
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:
t_zram = 0.01
@@ -972,3 +1235,4 @@ while True:
sleep(t)
except KeyboardInterrupt:
exit()
+
diff --git a/nohang.conf b/nohang.conf
index ee5c6e1..d8994c0 100644
--- a/nohang.conf
+++ b/nohang.conf
@@ -24,7 +24,7 @@ print_mem_check_results = True
Допустимые значения: True и False
(В этой ветке по дефолту True)
-print_sleep_periods = True
+print_sleep_periods = False
#####################################################################
@@ -69,11 +69,11 @@ self_oom_score_adj = -1000
В дефолтных настройках на данной интенсивности демон работает
достаточно хорошо, успешно справляясь с резкими скачками потребления
- памяти.
+ памяти.
-rate_mem = 6
-rate_swap = 3
-rate_zram = 1
+rate_mem = 6000000
+rate_swap = 3000000
+rate_zram = 1000000
#####################################################################
@@ -115,6 +115,11 @@ zram_max_sigkill = 60 %
Значение должно быть целым числом из диапазона [0; 1000]
+ Процессы из black_list (см ниже) получат сигнал вне зависимости
+ от значения их oom_score.
+
+ Может min_badness с учетом списков?
+
oom_score_min = 20
Минимальная задержка после отправки соответствующих сигналов
@@ -136,11 +141,11 @@ min_delay_after_sigkill = 3
Требует root прав.
-decrease_oom_score_adj = True
+decrease_oom_score_adj = False
Допустимые значения - целые числа из диапазона [0; 1000]
-oom_score_adj_max = 30
+oom_score_adj_max = 20
#####################################################################
@@ -170,3 +175,41 @@ notify_options =
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
+
diff --git a/purge.sh b/purge.sh
index a57e6cc..72a774a 100755
--- a/purge.sh
+++ b/purge.sh
@@ -1,7 +1,7 @@
#!/bin/bash -v
systemctl stop nohang
systemctl disable nohang
-rm -f /usr/local/share/man/man1/nohang.1.gz
-rm -f /etc/systemd/system/nohang.service
-rm -rf /etc/nohang
-rm -f /usr/local/bin/nohang
+rm /usr/local/share/man/man1/nohang.1.gz
+rm /etc/systemd/system/nohang.service
+rm -r /etc/nohang
+rm /usr/local/bin/nohang