nohang/nohang

1014 lines
31 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# Nohang - The No Hang Daemon for Linux
################################################################################
### импорты
import os
from ctypes import CDLL
from operator import itemgetter
from time import sleep
from argparse import ArgumentParser
################################################################################
### задание констант
version = 'unknown'
sig_dict = {9:'SIGKILL', 15:'SIGTERM'}
# директория, в которой запущен скрипт
cd = os.getcwd()
# где искать конфиг, если не указан через опцию -c/--config CONFIG
default_configs = (
cd + '/nohang.conf',
'/etc/nohang/nohang.conf'
)
# универсальное сообщение при инвалидном конфиге
conf_err_mess = "\nSet up the path to the valid config file w' \
'ith -c/--config CONFIG option!\nExit"
# означает, что при задани zram disksize = 10000M доступная память
# уменьшится на 42M
# найден экспериментально, требует уточнения с разными ядрами и архитектурами
# на небольших дисксайзах (до гигабайта) может быть больше, до 0.0045
# создатель модуля zram утверждает, что zram_disksize_factor доожен быть 0.001
# ("zram uses about 0.1% of the size of the disk"
# - https://www.kernel.org/doc/Documentation/blockdev/zram.txt),
# но это утверждение противоречит опытным данным
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):
# цикл для наполнения oom_list
for i in os.listdir('/proc'):
# пропускаем элементы, не состоящие только из цифр
if i.isdigit() is not True:
continue
try:
oom_score_adj = int(rline1('/proc/' + i + '/oom_score_adj'))
if oom_score_adj > oom_score_adj_before:
write(
'/proc/' + i + '/oom_score_adj',
oom_score_adj_after + '\n'
)
except FileNotFoundError:
pass
except ProcessLookupError:
pass
# чтение первой строки файла
def rline1(path):
with open(path) as f:
for line in f:
return line[:-1]
# запись в файл
def write(path, string):
with open(path, 'w') as f:
f.write(string)
def kib_to_mib(num):
return round(num / 1024.0)
def percent(num):
return round(num * 100, 1)
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, ' ')
# K -> M, выравнивание по правому краю
def human(num, lenth):
return str(round(num / 1024)).rjust(lenth, ' ')
# возвращает disksize и mem_used_total по zram id
def zram_stat(zram_id):
try:
disksize = rline1('/sys/block/' + zram_id + '/disksize')
except FileNotFoundError:
return '0', '0'
if disksize == ['0\n']:
return '0', '0'
try:
mm_stat = rline1('/sys/block/' + zram_id + '/mm_stat').split(' ')
mm_stat_list = []
for i in mm_stat:
if i != '':
mm_stat_list.append(i)
mem_used_total = mm_stat_list[2]
except FileNotFoundError:
mem_used_total = rline1('/sys/block/' + zram_id + '/mem_used_total')
return disksize, mem_used_total # BYTES, str
# имя через пид
def pid_to_name(pid):
try:
with open('/proc/' + pid + '/status') as f:
for line in f:
return line[:-1].split('\t')[1]
except FileNotFoundError:
return '<unknown>'
except ProcessLookupError:
return '<unknown>'
def find_victim_and_send_signal(signal):
if decrease_oom_score_adj and root:
func_decrease_oom_score_adj(
oom_score_adj_before, oom_score_adj_after
)
oom_list = []
for i in os.listdir('/proc'):
if i.isdigit() is not True:
continue
try:
oom_score = int(rline1('/proc/' + i + '/oom_score'))
except FileNotFoundError:
oom_score = 0
except ProcessLookupError:
oom_score = 0
oom_list.append((i, oom_score))
# получаем список пар (pid, oom_score)
pid_tuple_list = sorted(oom_list, key=itemgetter(1), reverse=True)[0]
oom_score = pid_tuple_list[1]
# посылаем сигнал
if oom_score >= oom_score_min:
pid = pid_tuple_list[0]
name = pid_to_name(pid)
print(
' Try to send the {} signal to {}, Pid {}, oom_score {}'.format(
sig_dict[signal], name, pid, oom_score
)
)
try:
os.kill(int(pid), signal)
print(' Success')
if desktop_notifications:
info = '"The {} signal has been sent to <b>{}</b>,\n<i>Pid:</i> <b>{}</b>, <i>oom_score:</i> <b>{}</b>" &'.format(sig_dict[signal], name, pid, oom_score)
if root:
# получаем множество залогиненных юзеров из вывода команды users
users_set = set(str(
Popen('users', shell=True, stdout=PIPE).communicate()[0][0:-1]
)[2:-1].split(' '))
# отправляем уведомление всем залогиненным юзерам
for i in users_set:
root_notify_command = 'DISPLAY=:0.0 sudo -u {} {} -i dialog-warnig "Nohang tried to prevent OOM" -t {} -u critical '.format(
i, notify_command, notify_time
)
os.system(root_notify_command + info)
else:
user_notify_command =(
'{} -i dialog-warnig "Nohang tried to prevent OOM" -t {} -u critical '.format(
notify_command, notify_time
)
)
os.system(user_notify_command + info)
except ProcessLookupError:
print(' No such process')
except PermissionError:
print(' Operation not permitted')
else:
print(' oom_score {} < oom_score_min {}'.format(
oom_score, oom_score_min)
)
# спать всегда или только при успешной отправке сигнала?
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)
################################################################################
### поиск позиций
# ищем позиции
with open('/proc/meminfo') as file:
mem_list = file.readlines()
mem_list_names = []
for s in mem_list:
mem_list_names.append(s.split(':')[0])
if mem_list_names[2] != 'MemAvailable':
print('Your Linux kernel is too old, 3.14+ requie\nExit')
exit()
swap_total_index = mem_list_names.index('SwapTotal')
swap_free_index = swap_total_index + 1
mem_total = int(mem_list[0].split(':')[1].split(' ')[-2])
# еще найти позиции VmRSS & VmSwap
################################################################################
### получение пути к конфигу
# парсинг аргументов командной строки
parser = ArgumentParser()
parser.add_argument(
'-c',
'--config',
help="""path to the config file, default values:
./nohang.conf, /etc/nohang/nohang.conf""",
default=None,
type=str
)
arg_config = parser.parse_args().config
if arg_config is None:
config = None
for i in default_configs:
if os.path.exists(i):
config = i
break
if config is None:
print('По дефолтным путям конфиг не найден\nExit', conf_err_mess)
exit()
else:
if os.path.exists(arg_config):
config = arg_config
else:
print("File {} doesn't exists{}".format(arg_config, conf_err_mess))
exit()
print('The path to the config file to be used:')
print(config)
################################################################################
### парсинг конфига с получением словаря параметров
try:
with open(config) as f:
config_dict = dict()
for line in f:
a = line.startswith('#')
b = line.startswith('\n')
c = line.startswith('\t')
d = line.startswith(' ')
if not a and not b and not c and not d:
a = line.split('=')
config_dict[a[0].strip()] = a[1].strip()
except PermissionError:
print('PermissionError', conf_err_mess)
exit()
except UnicodeDecodeError:
print('UnicodeDecodeError', conf_err_mess)
exit()
except IsADirectoryError:
print('IsADirectoryError', conf_err_mess)
exit()
except IndexError:
print('IndexError', conf_err_mess)
exit()
################################################################################
### извлечение параметров из словаря
### проверка наличия всех необходимых параметров
### валидация всех параметров
if 'print_config' in config_dict:
print_config = config_dict['print_config']
if print_config == 'True':
print_config = True
elif print_config == 'False':
print_config = False
else:
print(
'Invalid print_config value {} (should be True or Fa' \
'lse)\nExit'.format(
print_config
)
)
exit()
else:
print('Print_config not in config\nExit')
exit()
if 'print_mem_check_results' in config_dict:
print_mem_check_results = config_dict['print_mem_check_results']
if print_mem_check_results == 'True':
print_mem_check_results = True
elif print_mem_check_results == 'False':
print_mem_check_results = False
else:
print(
'Invalid print_mem_check_results value {} (should be True o' \
'r False)\nExit'.format(
print_mem_check_results
)
)
exit()
else:
print('print_mem_check_results not in config\nExit')
exit()
if 'print_sleep_periods' in config_dict:
print_sleep_periods = config_dict['print_sleep_periods']
if print_sleep_periods == 'True':
print_sleep_periods = True
elif print_sleep_periods == 'False':
print_sleep_periods = False
else:
print(
'Invalid print_sleep_periods value {} (should be Tru' \
'e or False)\nExit'.format(
print_sleep_periods
)
)
exit()
else:
print('print_sleep_periods not in config\nExit')
exit()
if 'mlockall' in config_dict:
mlockall = config_dict['mlockall']
if mlockall == 'True':
mlockall = True
elif mlockall == 'False':
mlockall = False
else:
print(
'Invalid mlockall value {} (should be True or False)\nExit'.format(
mlockall
)
)
exit()
else:
print('mlockall not in config\nExit')
exit()
if 'self_nice' in config_dict:
self_nice = config_dict['self_nice']
if string_to_int_convert_test(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()
else:
print('self_nice not in config\nExit')
exit()
if 'self_oom_score_adj' in config_dict:
self_oom_score_adj = config_dict['self_oom_score_adj']
self_oom_score_adj = string_to_int_convert_test(self_oom_score_adj)
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()
else:
print('self_oom_score_adj not in config\nExit')
exit()
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()
else:
print('rate_mem not in config\nExit')
exit()
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()
else:
print('rate_swap not in config\nExit')
exit()
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()
else:
print('rate_zram not in config\nExit')
exit()
if 'mem_min_sigterm' in config_dict:
mem_min_sigterm = config_dict['mem_min_sigterm']
else:
print('mem_min_sigterm not in config\nExit')
exit()
if 'mem_min_sigkill' in config_dict:
mem_min_sigkill = config_dict['mem_min_sigkill']
else:
print('mem_min_sigkill not in config\nExit')
exit()
if 'swap_min_sigterm' in config_dict:
swap_min_sigterm = config_dict['swap_min_sigterm']
else:
print('swap_min_sigterm not in config\nExit')
exit()
if 'swap_min_sigkill' in config_dict:
swap_min_sigkill = config_dict['swap_min_sigkill']
else:
print('swap_min_sigkill not in config\nExit')
exit()
if 'zram_max_sigterm' in config_dict:
zram_max_sigterm = config_dict['zram_max_sigterm']
else:
print('zram_max_sigterm not in config\nExit')
exit()
if 'zram_max_sigkill' in config_dict:
zram_max_sigkill = config_dict['zram_max_sigkill']
else:
print('zram_max_sigkill not in config\nExit')
exit()
if 'min_delay_after_sigterm' in config_dict:
min_delay_after_sigterm = string_to_float_convert_test(config_dict['m' \
'in_delay_after_sigterm'])
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()
else:
print('min_delay_after_sigterm not in config\nExit')
exit()
if 'min_delay_after_sigkill' in config_dict:
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:
print('min_delay_after_sigkill not in config\nExit')
exit()
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:
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()
else:
print('oom_score_min not in config\nExit')
exit()
if 'decrease_oom_score_adj' in config_dict:
decrease_oom_score_adj = config_dict['decrease_oom_score_adj']
if decrease_oom_score_adj == 'True':
decrease_oom_score_adj = True
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
)
)
exit()
else:
print('decrease_oom_score_adj not in config\nExit')
exit()
if 'oom_score_adj_before' in config_dict:
oom_score_adj_before = config_dict['oom_score_adj_before']
if string_to_int_convert_test(oom_score_adj_before) is None:
print('Invalid oom_score_adj_before value, not integer\nExit')
exit()
else:
oom_score_adj_before = int(oom_score_adj_before)
if oom_score_adj_before < 0 or oom_score_adj_before > 1000:
print('Недопустимое значение oom_score_adj_before\nExit')
exit()
else:
print('oom_score_adj_before not in config\nExit')
exit()
if 'oom_score_adj_after' in config_dict:
oom_score_adj_after = config_dict['oom_score_adj_after']
if string_to_int_convert_test(oom_score_adj_after) is None:
print('Invalid oom_score_adj_after value, not integer\nExit')
exit()
else:
oom_score_adj_after = int(oom_score_adj_after)
if oom_score_adj_after < 0 or oom_score_adj_after > 1000:
print('Недопустимое значение oom_score_adj_after\nExit')
exit()
else:
print('oom_score_adj_after not in config\nExit')
exit()
if 'desktop_notifications' in config_dict:
desktop_notifications = config_dict['desktop_notifications']
if desktop_notifications == 'True':
desktop_notifications = True
from subprocess import Popen, PIPE
elif desktop_notifications == 'False':
desktop_notifications = False
else:
print(
'Invalid desktop_notifications value {} (should be True or False)\nExit'.format(
desktop_notifications
)
)
exit()
else:
print('desktop_notifications not in config\nExit')
exit()
if 'notify_command' in config_dict:
notify_command = config_dict['notify_command'].strip()
else:
print('notify_command not in config\nExit')
exit()
if 'notify_time' in config_dict:
notify_time = config_dict['notify_time']
notify_time = string_to_float_convert_test(notify_time)
if notify_time is None:
print('Invalid notify_time value, not float\nExit')
exit()
if notify_time <= 0:
print('Недопустимое значение notify_time\nExit')
exit()
else:
notify_time = int(notify_time * 1000)
else:
print('notify_time not in config\nExit')
exit()
################################################################################
### получение уровней в кибибайтах
def sig_level_to_kb(string):
if string.endswith('%'):
return float(string[:-1].strip()) / 100 * mem_total
elif string.endswith('K'):
return float(string[:-1].strip())
elif string.endswith('M'):
return float(string[:-1].strip()) * 1024
elif string.endswith('G'):
return float(string[:-1].strip()) * 1048576
else:
print(
'Конфиг инвалид, где-то неверно указаны единицы измерения\nExit'
)
exit()
mem_min_sigterm_kb = sig_level_to_kb(mem_min_sigterm)
mem_min_sigkill_kb = sig_level_to_kb(mem_min_sigkill)
zram_max_sigterm_kb = sig_level_to_kb(zram_max_sigterm)
zram_max_sigkill_kb = sig_level_to_kb(zram_max_sigkill)
# возвращает число килобайт при задании в конфиге абсолютного значения,
# или кортеж с числом процентов
def sig_level_to_kb_swap(string):
if string.endswith('%'):
return float(string[:-1].strip()), True
elif string.endswith('K'):
return float(string[:-1].strip())
elif string.endswith('M'):
return float(string[:-1].strip()) * 1024
elif string.endswith('G'):
return float(string[:-1].strip()) * 1048576
else:
print('Конфиг инвалид, где-то неверно указаны единицы измерения\nExit')
exit()
# получаем число килобайт или кортеж с процентами
swap_min_sigterm_swap = sig_level_to_kb_swap(swap_min_sigterm)
swap_min_sigkill_swap = sig_level_to_kb_swap(swap_min_sigkill)
if type(swap_min_sigterm_swap) is tuple:
swap_term_is_percent = True
swap_min_sigterm_percent = swap_min_sigterm_swap[0]
else:
swap_term_is_percent = False
swap_min_sigterm_kb = swap_min_sigterm_swap
if type(swap_min_sigkill_swap) is tuple:
swap_kill_is_percent = True
swap_min_sigkill_percent = swap_min_sigkill_swap[0]
else:
swap_kill_is_percent = False
swap_min_sigkill_kb = swap_min_sigkill_swap
################################################################################
### самозащита и печать конфига
# повышаем приоритет
try:
os.nice(self_nice)
self_nice_result = 'OK'
except PermissionError:
self_nice_result = 'Fail'
pass
# возможность запрета самоубийства
try:
with open('/proc/self/oom_score_adj', 'w') as file:
file.write('{}\n'.format(self_oom_score_adj))
self_oom_score_adj_result = 'OK'
except PermissionError:
pass
self_oom_score_adj_result = 'Fail'
except OSError:
self_oom_score_adj_result = 'Fail'
pass
# запрет своппинга процесса
if mlockall:
result = CDLL('libc.so.6', use_errno=True).mlockall(3)
if result is 0:
mla_res = 'OK'
else:
mla_res = 'Fail'
else:
mla_res = ''
if os.geteuid() == 0:
root = True
decrease_res = 'OK'
else:
root = False
decrease_res = 'Impossible'
if print_config:
print('\nI. VERBOSITY')
print('print_config: {}'.format(print_config))
print('print_mem_check_results: {}'.format(print_mem_check_results))
print('print_sleep_periods: {}'.format(print_sleep_periods))
print('\nII. SELF PROTECTION')
print('mlockall: {} ({})'.format(mlockall, mla_res))
print('self_nice: {} ({})'.format(
self_nice, self_nice_result
))
print('self_oom_score_adj: {} ({})'.format(
self_oom_score_adj, self_oom_score_adj_result
))
print('\nIII. ИНТЕНСИВНОСТЬ МОНИТОРИНГА')
print('rate_mem: {}'.format(rate_mem))
print('rate_swap: {}'.format(rate_swap))
print('rate_zram: {}'.format(rate_zram))
print('\nIV. ПОРОГИ ДЛЯ ОТПРАВКИ СИГНАЛОВ')
print('mem_min_sigterm: {}'.format(mem_min_sigterm))
print('mem_min_sigkill: {}'.format(mem_min_sigkill))
print('swap_min_sigterm: {}'.format(swap_min_sigterm))
print('swap_min_sigkill: {}'.format(swap_min_sigkill))
print('zram_max_sigterm: {}'.format(zram_max_sigterm))
print('zram_max_sigkill: {}'.format(zram_max_sigkill))
print('\nV. THE PREVENTION OF KILLING INNOCENT VICTIMS')
print('min_delay_after_sigterm: {}'.format(min_delay_after_sigterm))
print('min_delay_after_sigkill: {}'.format(min_delay_after_sigkill))
print('oom_score_min: {}'.format(oom_score_min))
# False (OK) - OK не нужен когда фолс
print('decrease_oom_score_adj: {} ({})'.format(
decrease_oom_score_adj, decrease_res
))
if decrease_oom_score_adj:
print('oom_score_adj_before: {}'.format(oom_score_adj_before))
print('oom_score_adj_after: {}'.format(oom_score_adj_after))
print('\nVI. DESKTOP NOTIFICATIONS')
print('desktop_notifications: {}'.format(desktop_notifications))
if desktop_notifications:
print('notify_command: {}'.format(notify_command))
print('notify_time: {}'.format(round(notify_time / 1000, 1)))
# для рассчета ширины столбцов при печати mem и zram
mem_len = len(str(round(mem_total / 1024.0)))
print('\nStart monitoring...')
################################################################################
### цикл проверки уровней доступной памяти
while True:
#decrease_oom_score_adj(oom_score_adj_before, oom_score_adj_after)
# находим mem_available, swap_total, swap_free
with open('/proc/meminfo') as f:
for n, line in enumerate(f):
if n is 2:
mem_available = int(line.split(':')[1].split(' ')[-2])
continue
if n is swap_total_index:
swap_total = int(line.split(':')[1].split(' ')[-2])
continue
if n is swap_free_index:
swap_free = int(line.split(':')[1].split(' ')[-2])
break
# если swap_min_sigkill задан в процентах
if swap_kill_is_percent:
swap_min_sigkill_kb = swap_total * swap_min_sigkill_percent / 100.0
if swap_term_is_percent:
swap_min_sigterm_kb = swap_total * swap_min_sigterm_percent / 100.0
# находим MemUsedZram
disksize_sum = 0
mem_used_total_sum = 0
for dev in os.listdir('/sys/block'):
if dev.startswith('zram'):
stat = zram_stat(dev)
disksize_sum += int(stat[0])
mem_used_total_sum += int(stat[1])
mem_used_zram = (
mem_used_total_sum + disksize_sum * zram_disksize_factor
) / 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:
print(
'MemAvail: {} M, {} %'.format(
human(mem_available, mem_len),
just_percent_mem(mem_available / mem_total)
)
)
elif swap_total > 0 and mem_used_zram == 0:
if print_mem_check_results:
print(
'MemAvail: {} M, {} % | SwapFree: {} 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))
)
)
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),
just_percent_mem(mem_used_zram / mem_total)
)
)
# если swap_min_sigkill задан в абсолютной величине и Swap_total = 0
if swap_total > swap_min_sigkill_kb:
swap_sigkill_pc = percent(swap_min_sigkill_kb / (swap_total + 1))
else:
swap_sigkill_pc = '-'
if swap_total > swap_min_sigterm_kb:
swap_sigterm_pc = percent(swap_min_sigterm_kb / (swap_total + 1))
else:
swap_sigterm_pc = '-'
# MEM SWAP KILL
if mem_available <= mem_min_sigkill_kb and swap_free <= swap_min_sigkill_kb:
print(
'+ MemAvail ({} M, {} %) < mem_min_sigkill ({} M, {} %)\n S' \
'wapFree ({} M, {} %) < swap_min_sigkill ({} M, {} %)'.format(
kib_to_mib(mem_available),
percent(mem_available / mem_total),
kib_to_mib(mem_min_sigkill_kb),
percent(mem_min_sigkill_kb / mem_total),
kib_to_mib(swap_free),
percent(swap_free / (swap_total + 0.0001)),
kib_to_mib(swap_min_sigkill_kb),
swap_sigkill_pc
)
)
find_victim_and_send_signal(9)
continue
# ZRAM KILL
if mem_used_zram >= zram_max_sigkill_kb:
print(
'+ MemUsedZram ({} M, {} %) > zram_max_sigkill ({} M, {} %)'.format(
kib_to_mib(mem_used_zram),
percent(mem_used_zram / mem_total),
kib_to_mib(zram_max_sigkill_kb),
percent(zram_max_sigkill_kb / mem_total)
)
)
find_victim_and_send_signal(9)
continue
# MEM SWAP TERM
if mem_available <= mem_min_sigterm_kb and swap_free <= swap_min_sigterm_kb:
print(
'+ MemAvail ({} M, {} %) < mem_min_sigterm ({} M, {} %)\n SwapFree' \
' ({} M, {} %) < swap_min_sigterm ({} M, {} %)'.format(
kib_to_mib(mem_available),
percent(mem_available / mem_total),
kib_to_mib(mem_min_sigterm_kb),
percent(mem_min_sigterm_kb / mem_total),
kib_to_mib(swap_free),
percent(swap_free / (swap_total + 0.0001)),
kib_to_mib(swap_min_sigterm_kb),
swap_sigterm_pc
)
)
find_victim_and_send_signal(15)
# ZRAM TERM
if mem_used_zram >= zram_max_sigterm_kb:
print(
'+ MemUsedZram ({} M, {} %) > zram_max_sigterm ({} M, {} %)'.format(
kib_to_mib(mem_used_zram),
percent(mem_used_zram / mem_total),
kib_to_mib(zram_max_sigterm_kb),
percent(zram_max_sigterm_kb / mem_total)
)
)
find_victim_and_send_signal(15)
# задание периода в зависимости от рейтов и уровней доступной памяти
t_mem = mem_available / 1000000.0 / rate_mem
t_swap = swap_free / 10000000.0 / rate_swap
t_zram = (mem_total * 0.8 - mem_used_zram) / 1000000.0 / rate_zram
if t_zram < 0.01:
t_zram = 0.01
t_mem_swap = t_mem + t_swap
t_mem_zram = t_mem + t_zram
if t_mem_swap <= t_mem_zram:
t = t_mem_swap
else:
t = t_mem_zram
try:
if print_sleep_periods:
print('sleep', round(t, 3))
sleep(t)
except KeyboardInterrupt:
exit()