косметические правки

This commit is contained in:
Alexey Avramov 2018-06-12 01:02:26 +09:00
parent 989336f622
commit 40fa3257ea

565
nohang
View File

@ -2,12 +2,21 @@
# Nohang - No Hang Daemon # Nohang - No Hang Daemon
###########################################################################################
# - импорты
import os import os
from ctypes import CDLL from ctypes import CDLL
from operator import itemgetter from operator import itemgetter
from time import sleep from time import sleep
from argparse import ArgumentParser from argparse import ArgumentParser
###########################################################################################
# - задание констант
# найден экспериментально, требует уточнения с разными ядрами и архитектурами # найден экспериментально, требует уточнения с разными ядрами и архитектурами
zram_disksize_factor = 0.0042 zram_disksize_factor = 0.0042
@ -16,6 +25,167 @@ default_configs = ('./nohang.conf', '/etc/nohang/nohang.conf')
err_mess = '\nSet up path to the valid config file with -c/--config CONFIG option!\nexit' err_mess = '\nSet up path to the valid config file with -c/--config CONFIG option!\nexit'
###########################################################################################
# - задание функций
def 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(n):
return round(n * 100, 1)
def just_percent(num):
return str(round(num * 100, 1)).rjust(5, ' ')
# K -> M, выравнивание по правому краю
def human(num):
return str(round(num / 1024)).rjust(5, ' ')
# возвращает 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:
decrease_oom_score_adj(oom_score_adj_before, oom_score_adj_after)
#print('Find victim...')
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
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 signal {} to {}, Pid {}, oom_score {}'.format(
signal, name, pid, oom_score
)
)
try:
os.kill(int(pid), signal)
print(' Success')
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))
###########################################################################################
# - поиск позиций
# ищем позиции
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), bye!')
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 = ArgumentParser()
@ -30,7 +200,6 @@ parser.add_argument(
arg_config = parser.parse_args().config arg_config = parser.parse_args().config
if arg_config is None: if arg_config is None:
# print('конфиг не задан через опцию -с/--config, берем его из дефолтных путей') # print('конфиг не задан через опцию -с/--config, берем его из дефолтных путей')
@ -59,6 +228,11 @@ else:
print('Path to nohang config file:', config) print('Path to nohang config file:', config)
###########################################################################################
# - парсинг конфига с получением словаря параметров
try: try:
with open(config) as f: with open(config) as f:
config_dict = dict() config_dict = dict()
@ -84,11 +258,10 @@ except IndexError:
exit() exit()
########################################################################################### ###########################################################################################
# - извлечение параметров из словаря, проверка наличия всех необходимых параметров
# проверка наличия параметров в словаре, их извречение из словаря
if 'print_config' in config_dict: if 'print_config' in config_dict:
@ -236,21 +409,21 @@ else:
print('oom_score_min not in config, exit!') print('oom_score_min not in config, exit!')
exit() exit()
if 'decrease_oom_score_adj_enable' in config_dict: if 'decrease_oom_score_adj' in config_dict:
decrease_oom_score_adj_enable = config_dict['decrease_oom_score_adj_enable'] decrease_oom_score_adj = config_dict['decrease_oom_score_adj']
if decrease_oom_score_adj_enable == 'True': if decrease_oom_score_adj == 'True':
decrease_oom_score_adj_enable = True decrease_oom_score_adj = True
elif decrease_oom_score_adj_enable == 'False': elif decrease_oom_score_adj == 'False':
decrease_oom_score_adj_enable = False decrease_oom_score_adj = False
else: else:
print( print(
'invalid decrease_oom_score_adj_enable value {} (should be True or False), exit!'.format( 'invalid decrease_oom_score_adj value {} (should be True or False), exit!'.format(
decrease_oom_score_adj_enable decrease_oom_score_adj
) )
) )
exit() exit()
else: else:
print('decrease_oom_score_adj_enable not in config, exit!') print('decrease_oom_score_adj not in config, exit!')
exit() exit()
if 'oom_score_adj_before' in config_dict: if 'oom_score_adj_before' in config_dict:
@ -266,8 +439,74 @@ else:
exit() 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('Конфиг инвалид, где-то неверно указаны единицы измерения')
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('Конфиг инвалид, где-то неверно указаны единицы измерения')
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: try:
@ -310,249 +549,34 @@ else:
if print_config: if print_config:
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('mlockall: {} ({})'.format(mlockall, mla_res)) print('mlockall: {} ({})'.format(mlockall, mla_res))
print('self_nice: {} ({})'.format(self_nice, self_nice_result)) 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('self_oom_score_adj: {} ({})'.format(self_oom_score_adj, self_oom_score_adj_result))
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('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('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))
print('decrease_oom_score_adj_enable: {} ({})'.format(decrease_oom_score_adj_enable, decrease_res))
print('oom_score_adj_before: {}'.format(oom_score_adj_before))
print('oom_score_adj_after: {}'.format(oom_score_adj_after))
# False (OK) - OK не нужен когда фолс
print('decrease_oom_score_adj: {} ({})'.format(decrease_oom_score_adj, decrease_res))
print('oom_score_adj_before: {}'.format(oom_score_adj_before))
print('oom_score_adj_after: {}'.format(oom_score_adj_after))
########################################################################################### ###########################################################################################
# - цикл проверки уровней доступной памяти
def decrease_oom_score_adj(oom_score_adj_before, oom_score_adj_after):
#print('Decrease oom_score_adj...')
# цикл для наполнения 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 percent(n):
return round(n * 100, 1)
def just_percent(num):
return str(round(num * 100, 1)).rjust(5, ' ')
# K -> M, выравнивание по правому краю
def human(num):
return str(round(num / 1024)).rjust(5, ' ')
# возвращает 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_enable and root:
decrease_oom_score_adj(oom_score_adj_before, oom_score_adj_after)
#print('Find victim...')
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
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 signal {} to {}, Pid {}, oom_score {}'.format(
signal, name, pid, oom_score
)
)
try:
os.kill(int(pid), signal)
print(' Success')
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))
###########################################################################################
# START
# ищем позиции
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), bye!')
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])
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('Конфиг инвалид, где-то неверно указаны единицы измерения')
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('Конфиг инвалид, где-то неверно указаны единицы измерения')
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
def kib_to_mib(num):
return round(num / 1024.0)
###########################################################################################
print('Start monitoring...') print('Start monitoring...')
@ -574,7 +598,7 @@ while True:
break break
# если 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
@ -582,7 +606,6 @@ while True:
swap_min_sigterm_kb = swap_total * swap_min_sigterm_percent / 100.0 swap_min_sigterm_kb = swap_total * swap_min_sigterm_percent / 100.0
# находим MemUsedZram # находим MemUsedZram
disksize_sum = 0 disksize_sum = 0
mem_used_total_sum = 0 mem_used_total_sum = 0
@ -596,23 +619,7 @@ while True:
) / 1024.0 ) / 1024.0
# печать результатов проверк доступной памяти
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
if print_mem_check_results: if print_mem_check_results:
print( print(
'MemAvail: {}M {}%, SwapFree: {}M {}%, MemUsedZram: {}M {}%'.format( 'MemAvail: {}M {}%, SwapFree: {}M {}%, MemUsedZram: {}M {}%'.format(
@ -626,7 +633,7 @@ while True:
) )
# если swap_min_sigkill задан в абсолютной величине и Swap_total = 0
if swap_total > swap_min_sigkill_kb: if swap_total > swap_min_sigkill_kb:
swap_sigkill_pc = percent(swap_min_sigkill_kb / (swap_total + 1)) swap_sigkill_pc = percent(swap_min_sigkill_kb / (swap_total + 1))
else: else:
@ -657,6 +664,7 @@ while True:
sleep(min_delay_after_sigkill) sleep(min_delay_after_sigkill)
continue continue
# MEM ZRAM KILL # MEM ZRAM KILL
if mem_used_zram >= zram_max_sigkill_kb: if mem_used_zram >= zram_max_sigkill_kb:
print( print(
@ -671,6 +679,7 @@ while True:
sleep(min_delay_after_sigkill) sleep(min_delay_after_sigkill)
continue continue
# 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(
@ -689,6 +698,7 @@ while True:
find_victim_and_send_signal(15) find_victim_and_send_signal(15)
sleep(min_delay_after_sigterm) sleep(min_delay_after_sigterm)
# MEM ZRAM TERM # MEM ZRAM TERM
if mem_used_zram >= zram_max_sigterm_kb: if mem_used_zram >= zram_max_sigterm_kb:
print( print(
@ -702,6 +712,23 @@ while True:
find_victim_and_send_signal(15) find_victim_and_send_signal(15)
sleep(min_delay_after_sigterm) sleep(min_delay_after_sigterm)
# задание периода в зависимости от рейтов и уровней доступной памяти
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: try:
sleep(t) sleep(t)
except KeyboardInterrupt: except KeyboardInterrupt: