улучшение валидации параметров конфига, улучшение вывода результатов проверки доступной памяти

This commit is contained in:
Alexey Avramov 2018-06-13 15:00:34 +09:00
parent a82159ee68
commit b0805dc450
4 changed files with 314 additions and 156 deletions

View File

@ -32,7 +32,7 @@ https://2ch.hk/s/res/2310304.html#2311483, https://archive.li/idixk
- `earlyoom` завершает (точнее убивает) процессы исключительно с помощью сигнала `SIGKILL`, в то время как `nohang` дает возможность сначала отправлять `SIGTERM`, и только если процесс не реагирует на `SIGTERM` - отправляется сигнал `SIGKILL`. - `earlyoom` завершает (точнее убивает) процессы исключительно с помощью сигнала `SIGKILL`, в то время как `nohang` дает возможность сначала отправлять `SIGTERM`, и только если процесс не реагирует на `SIGTERM` - отправляется сигнал `SIGKILL`.
- `earlyoom` не поддерживает работу со `zram` и не реагирует на общую долю `zram` в памяти (`mem_used_total`). Это может привести к тому, что система все также встанет колом, как если бы `earlyoom` и не было (если `disksize` большой, а энтропия сжимаемых данных велика). `Nohang` позволяет избавиться от этой проблемы. По умолчанию если доля `zram` достигнет 60% памяти - будет отправлен сигнал `SIGTERM` процессу с наибольшим `oom_score`. - `earlyoom` не поддерживает работу со `zram` и не реагирует на общую долю `zram` в памяти (`mem_used_total`). Это может привести к тому, что система все также встанет колом, как если бы `earlyoom` и не было (если `disksize` большой, а энтропия сжимаемых данных велика). `Nohang` позволяет избавиться от этой проблемы. По умолчанию если доля `zram` достигнет 60% памяти - будет отправлен сигнал `SIGTERM` процессу с наибольшим `oom_score`.
### Особенности ### Некоторые особенности
- задача - препятствовать зависанию системы при нехватке доступной памяти, а также корректное завершение процессов с целью увеличения объема доступной памяти - задача - препятствовать зависанию системы при нехватке доступной памяти, а также корректное завершение процессов с целью увеличения объема доступной памяти
- демон на Python 3, VmRSS не более 13 MiB - демон на Python 3, VmRSS не более 13 MiB
- требуется ядро `Linux 3.14` или новее - требуется ядро `Linux 3.14` или новее
@ -55,14 +55,14 @@ https://2ch.hk/s/res/2310304.html#2311483, https://archive.li/idixk
- протестировано на `Debian 9 x86_64`, `Debian 8 i386`, `Fedora 28 x86_64` - протестировано на `Debian 9 x86_64`, `Debian 8 i386`, `Fedora 28 x86_64`
- пример вывода с отчетом об успешной отпраке сигнала: - пример вывода с отчетом об успешной отпраке сигнала:
``` ```
MemAvail: 0M 0.0% | SwapFree: 1400M 11.9% | MemUsedZram: 397M 6.8% MemAvail: 0 M, 0.0 % | SwapFree: 97 M, 8.3 % | MemUsedZram: 147 M, 2.5 %
MemAvail: 0M 0.0% | SwapFree: 861M 7.3% | MemUsedZram: 413M 7.0% MemAvail: 0 M, 0.0 % | SwapFree: 80 M, 6.8 % | MemUsedZram: 147 M, 2.5 %
+ MemAvail (0M, 0.0%) < mem_min_sigterm (470M, 8.0%) + MemAvail (0 M, 0.0 %) < mem_min_sigterm (470 M, 8.0 %)
SwapFree (861M, 7.3%) < swap_min_sigterm (940M, 8.0%) SwapFree (80 M, 6.8 %) < swap_min_sigterm (94 M, 8.0 %)
Try to send signal 15 to tail, Pid 10435, oom_score 826 Try to send signal 15 to tail, Pid 17907, oom_score 837
Success Success
MemAvail: 102M 1.7% | SwapFree: 8106M 69.0% | MemUsedZram: 338M 5.7% MemAvail: 640 M, 10.9 % | SwapFree: 730 M, 62.1 % | MemUsedZram: 141 M, 2.4 %
MemAvail: 4507M 76.7% | SwapFree: 10908M 92.8% | MemUsedZram: 296M 5.0% MemAvail: 5197 M, 88.5 % | SwapFree: 734 M, 62.5 % | MemUsedZram: 141 M, 2.4 %
``` ```
### Установка и удаление для пользователей systemd ### Установка и удаление для пользователей systemd

403
nohang
View File

@ -3,9 +3,9 @@
# Nohang - No Hang Daemon # Nohang - No Hang Daemon
########################################################################################### ################################################################################
# - импорты ### импорты
import os import os
from ctypes import CDLL from ctypes import CDLL
@ -13,19 +13,25 @@ from operator import itemgetter
from time import sleep from time import sleep
from argparse import ArgumentParser from argparse import ArgumentParser
########################################################################################### ################################################################################
# - задание констант ### задание констант
# директория, в которой запущен скрипт
cd = os.getcwd()
# где искать конфиг, если не указан через опцию -c/--config CONFIG # где искать конфиг, если не указан через опцию -c/--config CONFIG
default_configs = ( default_configs = (
'./nohang.conf', cd + '/nohang.conf',
'/etc/nohang/nohang.conf' '/etc/nohang/nohang.conf'
) )
err_mess = '\nSet up path to the valid config file with -c/--config CONFIG option!\nexit' # универсальное сообщение при инвалидном конфиге
conf_err_mess = "\nSet up the path to the valid config file w' \
'ith -c/--config CONFIG option!\nExit"
# означает, что при задани zram disksize = 10000M доступная память уменьшится на 42M # означает, что при задани zram disksize = 10000M доступная память
# уменьшится на 42M
# найден экспериментально, требует уточнения с разными ядрами и архитектурами # найден экспериментально, требует уточнения с разными ядрами и архитектурами
# на небольших дисксайзах (до гигабайта) может быть больше, до 0.0045 # на небольших дисксайзах (до гигабайта) может быть больше, до 0.0045
# создатель модуля zram утверждает, что zram_disksize_factor доожен быть 0.001 # создатель модуля zram утверждает, что zram_disksize_factor доожен быть 0.001
@ -34,9 +40,23 @@ err_mess = '\nSet up path to the valid config file with -c/--config CONFIG optio
# но это утверждение противоречит опытным данным # но это утверждение противоречит опытным данным
zram_disksize_factor = 0.0042 zram_disksize_factor = 0.0042
########################################################################################### ################################################################################
# - задание функций ### задание функций
def string_to_float_convert_test(string):
try:
return float(string)
except ValueError:
return None
def string_to_int_convert_test(string):
try:
return int(string)
except ValueError:
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_before, oom_score_adj_after):
@ -50,7 +70,10 @@ def func_decrease_oom_score_adj(oom_score_adj_before, oom_score_adj_after):
try: try:
oom_score_adj = int(rline1('/proc/' + i + '/oom_score_adj')) oom_score_adj = int(rline1('/proc/' + i + '/oom_score_adj'))
if oom_score_adj > oom_score_adj_before: if oom_score_adj > oom_score_adj_before:
write('/proc/' + i + '/oom_score_adj', oom_score_adj_after + '\n') write(
'/proc/' + i + '/oom_score_adj',
oom_score_adj_after + '\n'
)
except FileNotFoundError: except FileNotFoundError:
pass pass
except ProcessLookupError: except ProcessLookupError:
@ -78,7 +101,10 @@ def percent(num):
return round(num * 100, 1) return round(num * 100, 1)
def just_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, ' ') return str(round(num * 100, 1)).rjust(5, ' ')
@ -122,7 +148,9 @@ def pid_to_name(pid):
def find_victim_and_send_signal(signal): def find_victim_and_send_signal(signal):
if decrease_oom_score_adj and root: 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_before, oom_score_adj_after
)
oom_list = [] oom_list = []
for i in os.listdir('/proc'): for i in os.listdir('/proc'):
@ -160,22 +188,24 @@ def find_victim_and_send_signal(signal):
print(' Operation not permitted') print(' Operation not permitted')
else: else:
print(' oom_score {} < oom_score_min {}'.format(oom_score, oom_score_min)) print(' oom_score {} < oom_score_min {}'.format(
oom_score, oom_score_min)
)
# спать всегда или только при успешной отправке сигнала? # спать всегда или только при успешной отправке сигнала?
if signal is 9: if signal is 9:
if print_sleep_pediods: if print_sleep_periods:
print(' sleep', min_delay_after_sigkill) print(' sleep', min_delay_after_sigkill)
sleep(min_delay_after_sigterm) sleep(min_delay_after_sigterm)
else: else:
if print_sleep_pediods: if print_sleep_periods:
print(' sleep', min_delay_after_sigterm) print(' sleep', min_delay_after_sigterm)
sleep(min_delay_after_sigterm) sleep(min_delay_after_sigterm)
########################################################################################### ################################################################################
# - поиск позиций ### поиск позиций
# ищем позиции # ищем позиции
@ -187,7 +217,7 @@ for s in mem_list:
mem_list_names.append(s.split(':')[0]) mem_list_names.append(s.split(':')[0])
if mem_list_names[2] != 'MemAvailable': if mem_list_names[2] != 'MemAvailable':
print('Your Linux kernel is too old (3.14+ requie), bye!') print('Your Linux kernel is too old, 3.14+ requie\nExit')
exit() exit()
swap_total_index = mem_list_names.index('SwapTotal') swap_total_index = mem_list_names.index('SwapTotal')
@ -199,16 +229,17 @@ mem_total = int(mem_list[0].split(':')[1].split(' ')[-2])
# еще найти позиции VmRSS & VmSwap # еще найти позиции VmRSS & VmSwap
########################################################################################### ################################################################################
# - получение пути к конфигу ### получение пути к конфигу
# парсинг аргументов командной строки # парсинг аргументов командной строки
parser = ArgumentParser() parser = ArgumentParser()
parser.add_argument( parser.add_argument(
'-c', '-c',
'--config', '--config',
help='path to the config file, default values: ./nohang.conf, /etc/nohang/nohang.conf', help="""path to the config file, default values:
./nohang.conf, /etc/nohang/nohang.conf""",
default=None, default=None,
type=str type=str
) )
@ -224,7 +255,7 @@ if arg_config is None:
config = i config = i
break break
if config is None: if config is None:
print('По дефолтным путям конфиг не найден', err_mess) print('По дефолтным путям конфиг не найден\nExit', conf_err_mess)
exit() exit()
else: else:
@ -232,16 +263,16 @@ else:
if os.path.exists(arg_config): if os.path.exists(arg_config):
config = arg_config config = arg_config
else: else:
print('нет файла по указанному пути: {}'.format(arg_config), err_mess) print("File {} doesn't exists{}".format(arg_config, conf_err_mess))
exit() exit()
print('Path to nohang config file:', config) print('The path to the config file to be used:')
print(config)
################################################################################
########################################################################################### ### парсинг конфига с получением словаря параметров
# - парсинг конфига с получением словаря параметров
try: try:
with open(config) as f: with open(config) as f:
@ -255,22 +286,25 @@ try:
a = line.split('=') a = line.split('=')
config_dict[a[0].strip()] = a[1].strip() config_dict[a[0].strip()] = a[1].strip()
except PermissionError: except PermissionError:
print('PermissionError', err_mess) print('PermissionError', conf_err_mess)
exit() exit()
except UnicodeDecodeError: except UnicodeDecodeError:
print('UnicodeDecodeError', err_mess) print('UnicodeDecodeError', conf_err_mess)
exit() exit()
except IsADirectoryError: except IsADirectoryError:
print('IsADirectoryError', err_mess) print('IsADirectoryError', conf_err_mess)
exit() exit()
except IndexError: except IndexError:
print('IndexError', err_mess) print('IndexError', conf_err_mess)
exit() exit()
########################################################################################### ################################################################################
### извлечение параметров из словаря
### проверка наличия всех необходимых параметров
### валидация всех параметров
# - извлечение параметров из словаря, проверка наличия всех необходимых параметров
if 'print_config' in config_dict: if 'print_config' in config_dict:
@ -281,15 +315,17 @@ if 'print_config' in config_dict:
print_config = False print_config = False
else: else:
print( print(
'invalid print_config value {} (should be True or False), exit!'.format( 'Invalid print_config value {} (should be True or Fa' \
'lse)\nExit'.format(
print_config print_config
) )
) )
exit() exit()
else: else:
print('print_config not in config, exit!') print('Print_config not in config\nExit')
exit() exit()
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':
@ -298,32 +334,36 @@ if 'print_mem_check_results' in config_dict:
print_mem_check_results = False print_mem_check_results = False
else: else:
print( print(
'invalid print_mem_check_results value {} (should be True or False), exit!'.format( 'Invalid print_mem_check_results value {} (should be True o' \
'r False)\nExit'.format(
print_mem_check_results print_mem_check_results
) )
) )
exit() exit()
else: else:
print('print_mem_check_results not in config, exit!') print('print_mem_check_results not in config\nExit')
exit() exit()
if 'print_sleep_pediods' in config_dict:
print_sleep_pediods = config_dict['print_sleep_pediods'] if 'print_sleep_periods' in config_dict:
if print_sleep_pediods == 'True': print_sleep_periods = config_dict['print_sleep_periods']
print_sleep_pediods = True if print_sleep_periods == 'True':
elif print_sleep_pediods == 'False': print_sleep_periods = True
print_sleep_pediods = False elif print_sleep_periods == 'False':
print_sleep_periods = False
else: else:
print( print(
'invalid print_sleep_pediods value {} (should be True or False), exit!'.format( 'Invalid print_sleep_periods value {} (should be Tru' \
print_sleep_pediods 'e or False)\nExit'.format(
print_sleep_periods
) )
) )
exit() exit()
else: else:
print('print_sleep_pediods not in config, exit!') print('print_sleep_periods not in config\nExit')
exit() exit()
if 'mlockall' in config_dict: if 'mlockall' in config_dict:
mlockall = config_dict['mlockall'] mlockall = config_dict['mlockall']
if mlockall == 'True': if mlockall == 'True':
@ -332,107 +372,177 @@ if 'mlockall' in config_dict:
mlockall = False mlockall = False
else: else:
print( print(
'invalid mlockall value {} (should be True or False), exit!'.format( 'Invalid mlockall value {} (should be True or False)\nExit'.format(
mlockall mlockall
) )
) )
exit() exit()
else: else:
print('mlockall not in config, exit!') print('mlockall not in config\nExit')
exit() exit()
if 'self_nice' in config_dict: if 'self_nice' in config_dict:
self_nice = int(config_dict['self_nice']) self_nice = config_dict['self_nice']
else: if string_to_int_convert_test(self_nice) is None:
print('self_nice not in config, exit!') print('Invalid self_nice value, not integer\nExit')
exit() exit()
else:
self_nice = int(self_nice)
if self_nice < -20 or self_nice > 19:
print('Недопустимое значение self_nice\nExit')
exit()
else:
print('self_nice not in config\nExit')
exit()
if 'self_oom_score_adj' in config_dict: if 'self_oom_score_adj' in config_dict:
self_oom_score_adj = int(config_dict['self_oom_score_adj']) self_oom_score_adj = config_dict['self_oom_score_adj']
else: if string_to_int_convert_test(self_oom_score_adj) is None:
print('self_oom_score_adj not in config, exit!') print('Invalid self_oom_score_adj value, not integer\nExit')
exit() exit()
else:
self_oom_score_adj = int(self_oom_score_adj)
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()
if 'rate_mem' in config_dict: if 'rate_mem' in config_dict:
rate_mem = float(config_dict['rate_mem']) 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: if rate_mem <= 0:
print('rate_mem должен быть положительным') print('rate_mem должен быть положительным\nExit')
exit() exit()
else: else:
print('rate_mem not in config, exit!') print('rate_mem not in config\nExit')
exit() exit()
if 'rate_swap' in config_dict: if 'rate_swap' in config_dict:
rate_swap = float(config_dict['rate_swap']) 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: if rate_swap <= 0:
print('rate_swap должен быть положительным') print('rate_swap должен быть положительным\nExit')
exit() exit()
else: else:
print('rate_swap not in config, exit!') print('rate_swap not in config\nExit')
exit() exit()
if 'rate_zram' in config_dict: if 'rate_zram' in config_dict:
rate_zram = float(config_dict['rate_zram']) 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: if rate_zram <= 0:
print('rate_zram должен быть положительным') print('rate_zram должен быть положительным\nExit')
exit() exit()
else: else:
print('rate_zram not in config, exit!') print('rate_zram not in config\nExit')
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:
print('mem_min_sigterm not in config, exit!') print('mem_min_sigterm not in config\nExit')
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:
print('mem_min_sigkill not in config, exit!') print('mem_min_sigkill not in config\nExit')
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:
print('swap_min_sigterm not in config, exit!') print('swap_min_sigterm not in config\nExit')
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:
print('swap_min_sigkill not in config, exit!') print('swap_min_sigkill not in config\nExit')
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:
print('zram_max_sigterm not in config, exit!') print('zram_max_sigterm not in config\nExit')
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:
print('zram_max_sigkill not in config, exit!') print('zram_max_sigkill not in config\nExit')
exit() exit()
if 'min_delay_after_sigterm' in config_dict: if 'min_delay_after_sigterm' in config_dict:
min_delay_after_sigterm = float(config_dict['min_delay_after_sigterm']) min_delay_after_sigterm = string_to_float_convert_test(config_dict['m' \
else: 'in_delay_after_sigterm'])
print('min_delay_after_sigterm not in config, exit!') if min_delay_after_sigterm is None:
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:
print('min_delay_after_sigterm должен быть неотрицательным\nExit')
exit()
else:
print('min_delay_after_sigterm not in config\nExit')
exit()
if 'min_delay_after_sigkill' in config_dict: if 'min_delay_after_sigkill' in config_dict:
min_delay_after_sigkill = float(config_dict['min_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()
min_delay_after_sigkill = float(min_delay_after_sigkill)
if min_delay_after_sigkill < 0:
print('min_delay_after_sigkill должен быть неотрицательным\nExit')
exit()
else: else:
print('min_delay_after_sigkill not in config, exit!') print('min_delay_after_sigkill not in config\nExit')
exit() exit()
if 'oom_score_min' in config_dict: if 'oom_score_min' in config_dict:
oom_score_min = int(config_dict['oom_score_min']) oom_score_min = config_dict['oom_score_min']
else: if string_to_int_convert_test(oom_score_min) is None:
print('oom_score_min not in config, exit!') print('Invalid oom_score_min value, not integer\nExit')
exit() 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()
else:
print('oom_score_min not in config\nExit')
exit()
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']
@ -442,32 +552,50 @@ if 'decrease_oom_score_adj' in config_dict:
decrease_oom_score_adj = False decrease_oom_score_adj = False
else: else:
print( print(
'invalid decrease_oom_score_adj value {} (should be True or False), exit!'.format( 'invalid decrease_oom_score_adj value {} (should be Tru' \
'e or False)\nExit'.format(
decrease_oom_score_adj decrease_oom_score_adj
) )
) )
exit() exit()
else: else:
print('decrease_oom_score_adj not in config, exit!') print('decrease_oom_score_adj not in config\nExit')
exit() exit()
if 'oom_score_adj_before' in config_dict: if 'oom_score_adj_before' in config_dict:
oom_score_adj_before = int(config_dict['oom_score_adj_before']) oom_score_adj_before = config_dict['oom_score_adj_before']
else: if string_to_int_convert_test(oom_score_adj_before) is None:
print('oom_score_adj_before not in config, exit!') print('Invalid oom_score_adj_before value, not integer\nExit')
exit() 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: if 'oom_score_adj_after' in config_dict:
oom_score_adj_after = config_dict['oom_score_adj_after'] 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')
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: else:
print('oom_score_adj_after not in config, exit!') print('oom_score_adj_after not in config\nExit')
exit() exit()
################################################################################
########################################################################################### ### получение уровней в кибибайтах
# - получение уровней в килобайтах
def sig_level_to_kb(string): def sig_level_to_kb(string):
@ -480,7 +608,9 @@ def sig_level_to_kb(string):
elif string.endswith('G'): elif string.endswith('G'):
return float(string[:-1].strip()) * 1048576 return float(string[:-1].strip()) * 1048576
else: else:
print('Конфиг инвалид, где-то неверно указаны единицы измерения') print(
'Конфиг инвалид, где-то неверно указаны единицы измерения\nExit'
)
exit() exit()
@ -504,7 +634,7 @@ def sig_level_to_kb_swap(string):
elif string.endswith('G'): elif string.endswith('G'):
return float(string[:-1].strip()) * 1048576 return float(string[:-1].strip()) * 1048576
else: else:
print('Конфиг инвалид, где-то неверно указаны единицы измерения') print('Конфиг инвалид, где-то неверно указаны единицы измерения\nExit')
exit() exit()
@ -528,9 +658,9 @@ else:
########################################################################################### ################################################################################
# - самозащита и печать конфига ### самозащита и печать конфига
# повышаем приоритет # повышаем приоритет
@ -574,63 +704,77 @@ else:
if print_config: if print_config:
print('\nI. VERBOSITY')
print('print_config: {}'.format(print_config)) print('print_config: {}'.format(print_config))
print('print_mem_check_results: {}'.format(print_mem_check_results)) print('print_mem_check_results: {}'.format(print_mem_check_results))
print('print_sleep_pediods: {}'.format(print_sleep_pediods)) print('print_sleep_periods: {}'.format(print_sleep_periods))
print('\nII. SELF PROTECTION')
print('mlockall: {} ({})'.format(mlockall, mla_res)) print('mlockall: {} ({})'.format(mlockall, mla_res))
print('self_nice: {} ({})'.format(self_nice, self_nice_result)) print('self_nice: {} ({})'.format(
print('self_oom_score_adj: {} ({})'.format(self_oom_score_adj, self_oom_score_adj_result)) 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)) print('rate_mem: {}'.format(rate_mem))
print('rate_swap: {}'.format(rate_swap)) print('rate_swap: {}'.format(rate_swap))
print('rate_zram: {}'.format(rate_zram)) print('rate_zram: {}'.format(rate_zram))
print('\nIV. ПОРОГИ ДЛЯ ОТПРАВКИ СИГНАЛОВ')
print('mem_min_sigterm: {}'.format(mem_min_sigterm)) print('mem_min_sigterm: {}'.format(mem_min_sigterm))
print('mem_min_sigkill: {}'.format(mem_min_sigkill)) print('mem_min_sigkill: {}'.format(mem_min_sigkill))
print('swap_min_sigterm: {}'.format(swap_min_sigterm)) print('swap_min_sigterm: {}'.format(swap_min_sigterm))
print('swap_min_sigkill: {}'.format(swap_min_sigkill)) print('swap_min_sigkill: {}'.format(swap_min_sigkill))
print('zram_max_sigterm: {}'.format(zram_max_sigterm)) print('zram_max_sigterm: {}'.format(zram_max_sigterm))
print('zram_max_sigkill: {}'.format(zram_max_sigkill)) print('zram_max_sigkill: {}'.format(zram_max_sigkill))
print('\nV. ПРЕДОТВРАЩЕНИЕ УБИЙСТВ НЕВИНОВНЫХ')
print('min_delay_after_sigterm: {}'.format(min_delay_after_sigterm)) print('min_delay_after_sigterm: {}'.format(min_delay_after_sigterm))
print('min_delay_after_sigkill: {}'.format(min_delay_after_sigkill)) print('min_delay_after_sigkill: {}'.format(min_delay_after_sigkill))
print('oom_score_min: {}'.format(oom_score_min)) print('oom_score_min: {}'.format(oom_score_min))
# False (OK) - OK не нужен когда фолс # False (OK) - OK не нужен когда фолс
print('decrease_oom_score_adj: {} ({})'.format(decrease_oom_score_adj, decrease_res)) 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_before: {}'.format(oom_score_adj_before))
print('oom_score_adj_after: {}'.format(oom_score_adj_after)) print('oom_score_adj_after: {}'.format(oom_score_adj_after))
# для рассчета ширины столбцов при печати mem и zram # для рассчета ширины столбцов при печати mem и zram
mem_len = len(str(round(mem_total / 1024.0))) mem_len = len(str(round(mem_total / 1024.0)))
########################################################################################### print('\nStart monitoring...')
# - цикл проверки уровней доступной памяти ################################################################################
### цикл проверки уровней доступной памяти
print('Start monitoring...')
# рабочий цикл
while True: while True:
#decrease_oom_score_adj(oom_score_adj_before, oom_score_adj_after) #decrease_oom_score_adj(oom_score_adj_before, oom_score_adj_after)
# находим mem_available, swap_total, swap_free # находим mem_available, swap_total, swap_free
with open('/proc/meminfo') as f: with open('/proc/meminfo') as f:
for n, line in enumerate(f): for n, line in enumerate(f):
if n == 2: if n is 2:
mem_available = int(line.split(':')[1].split(' ')[-2]) mem_available = int(line.split(':')[1].split(' ')[-2])
continue continue
if n == swap_total_index: if n is swap_total_index:
swap_total = int(line.split(':')[1].split(' ')[-2]) swap_total = int(line.split(':')[1].split(' ')[-2])
continue continue
if n == swap_free_index: if n is swap_free_index:
swap_free = int(line.split(':')[1].split(' ')[-2]) swap_free = int(line.split(':')[1].split(' ')[-2])
break break
# если swap_min_sigkill задан в процентах # если swap_min_sigkill задан в процентах
if swap_kill_is_percent: if swap_kill_is_percent:
swap_min_sigkill_kb = swap_total * swap_min_sigkill_percent / 100.0 swap_min_sigkill_kb = swap_total * swap_min_sigkill_percent / 100.0
@ -656,16 +800,36 @@ while True:
swap_len = len(str(round(swap_total / 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: if print_mem_check_results:
print( print(
'MemAvail: {}M {}% | SwapFree: {}M {}% | MemUsedZram: {}M {}%'.format( 'MemAvail: {} M, {} %'.format(
human(mem_available, mem_len), human(mem_available, mem_len),
just_percent(mem_available / mem_total), just_percent_mem(mem_available / mem_total)
)
)
elif swap_total > 0 and mem_used_zram == 0:
if print_mem_check_results:
print(
'MemAvail: {} M, {} % | SwapFree: {} M, {} %'.format(
human(mem_available, mem_len),
just_percent_mem(mem_available / mem_total),
human(swap_free, swap_len), human(swap_free, swap_len),
just_percent(swap_free / (swap_total + 0.0001)), just_percent_swap(swap_free / (swap_total + 0.0001))
)
)
else:
if print_mem_check_results:
print(
'MemAvail: {} M, {} % | SwapFree: {} M, {} % | MemUsed' \
'Zram: {} M, {} %'.format(
human(mem_available, mem_len),
just_percent_mem(mem_available / mem_total),
human(swap_free, swap_len),
just_percent_swap(swap_free / (swap_total + 0.0001)),
human(mem_used_zram, mem_len), human(mem_used_zram, mem_len),
just_percent(mem_used_zram / mem_total) just_percent_mem(mem_used_zram / mem_total)
) )
) )
@ -685,8 +849,8 @@ while True:
# MEM SWAP KILL # MEM SWAP KILL
if mem_available <= mem_min_sigkill_kb and swap_free <= swap_min_sigkill_kb: if mem_available <= mem_min_sigkill_kb and swap_free <= swap_min_sigkill_kb:
print( print(
'+ MemAvail ({}M, {}%) < mem_min_sigkill ({}M, {}%)\n SwapFree' \ '+ MemAvail ({} M, {} %) < mem_min_sigkill ({} M, {} %)\n S' \
' ({}M, {}%) < swap_min_sigkill ({}M, {}%)'.format( 'wapFree ({} M, {} %) < swap_min_sigkill ({} M, {} %)'.format(
kib_to_mib(mem_available), kib_to_mib(mem_available),
percent(mem_available / mem_total), percent(mem_available / mem_total),
kib_to_mib(mem_min_sigkill_kb), kib_to_mib(mem_min_sigkill_kb),
@ -701,10 +865,10 @@ while True:
continue continue
# MEM ZRAM KILL # ZRAM KILL
if mem_used_zram >= zram_max_sigkill_kb: if mem_used_zram >= zram_max_sigkill_kb:
print( print(
'+ MemUsedZram ({}M, {}%) > zram_max_sigkill ({}M, {}%)'.format( '+ MemUsedZram ({} M, {} %) > zram_max_sigkill ({} M, {} %)'.format(
kib_to_mib(mem_used_zram), 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), kib_to_mib(zram_max_sigkill_kb),
@ -718,8 +882,8 @@ while True:
# MEM SWAP TERM # MEM SWAP TERM
if mem_available <= mem_min_sigterm_kb and swap_free <= swap_min_sigterm_kb: if mem_available <= mem_min_sigterm_kb and swap_free <= swap_min_sigterm_kb:
print( print(
'+ MemAvail ({}M, {}%) < mem_min_sigterm ({}M, {}%)\n SwapFree' \ '+ MemAvail ({} M, {} %) < mem_min_sigterm ({} M, {} %)\n SwapFree' \
' ({}M, {}%) < swap_min_sigterm ({}M, {}%)'.format( ' ({} M, {} %) < swap_min_sigterm ({} M, {} %)'.format(
kib_to_mib(mem_available), kib_to_mib(mem_available),
percent(mem_available / mem_total), percent(mem_available / mem_total),
kib_to_mib(mem_min_sigterm_kb), kib_to_mib(mem_min_sigterm_kb),
@ -733,10 +897,10 @@ while True:
find_victim_and_send_signal(15) find_victim_and_send_signal(15)
# MEM ZRAM TERM # ZRAM TERM
if mem_used_zram >= zram_max_sigterm_kb: if mem_used_zram >= zram_max_sigterm_kb:
print( print(
'+ MemUsedZram ({}M, {}%) > zram_max_sigterm ({}M, {}%)'.format( '+ MemUsedZram ({} M, {} %) > zram_max_sigterm ({} M, {} %)'.format(
kib_to_mib(mem_used_zram), 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), kib_to_mib(zram_max_sigterm_kb),
@ -746,7 +910,6 @@ 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 / 1000000.0 / rate_mem
@ -765,8 +928,8 @@ while True:
t = t_mem_zram t = t_mem_zram
try: try:
if print_sleep_pediods: if print_sleep_periods:
print('sleep', round(t, 2)) print('sleep', round(t, 3))
sleep(t) sleep(t)
except KeyboardInterrupt: except KeyboardInterrupt:
exit() exit()

View File

@ -23,11 +23,11 @@ print_mem_check_results = True
сигналов. Можно установить в значение True для дебага. сигналов. Можно установить в значение True для дебага.
Допустимые значения: True и False Допустимые значения: True и False
print_sleep_pediods = False print_sleep_periods = False
##################################################################### #####################################################################
II. САМОЗАЩИТА ПРОЦЕССА II. SELF PROTECTION
True - заблокировать процесс в памяти для запрета его своппинга. True - заблокировать процесс в памяти для запрета его своппинга.
False - не блокировать. Значения чувствительны к регистру! False - не блокировать. Значения чувствительны к регистру!
@ -68,7 +68,7 @@ self_oom_score_adj = -1000
памяти. памяти.
rate_mem = 6 rate_mem = 6
rate_swap = 0.2 rate_swap = 0.5
rate_zram = 1 rate_zram = 1
##################################################################### #####################################################################
@ -107,7 +107,7 @@ zram_max_sigkill = 60 %
##################################################################### #####################################################################
VI. ПРЕДОТВРАЩЕНИЕ ИЗБЫТОЧНЫХ УБИЙСТВ V. ПРЕДОТВРАЩЕНИЕ УБИЙСТВ НЕВИНОВНЫХ
Минимальное значение oom_score, которым должен обладать Минимальное значение oom_score, которым должен обладать
процесс для того, чтобы ему был отправлен сигнал. процесс для того, чтобы ему был отправлен сигнал.
@ -125,10 +125,6 @@ oom_score_min = 15
min_delay_after_sigterm = 0.1 min_delay_after_sigterm = 0.1
min_delay_after_sigkill = 3 min_delay_after_sigkill = 3
#####################################################################
VII. ЗАЩИТА ПРОЦЕССОВ CHROMIUM ОТ СМЕРТИ ПО ЧУЖОЙ ВИНЕ
Процессы браузера chromium обычно имеют oom_score_adj Процессы браузера chromium обычно имеют oom_score_adj
200 или 300. Это приводит к тому, что процессы хрома умирают 200 или 300. Это приводит к тому, что процессы хрома умирают
первыми вместо действительно тяжелых процессов. первыми вместо действительно тяжелых процессов.

View File

@ -6,7 +6,6 @@ Documentation=man:nohang(1) https://github.com/hakavlad/nohang
[Service] [Service]
Type=simple Type=simple
PIDFile=/run/nohang.pid
Restart=always Restart=always
ExecStart=/usr/local/bin/nohang ExecStart=/usr/local/bin/nohang