From 7647a1fd7f8b20d3b4aac7ff0d5a2bcd777182b8 Mon Sep 17 00:00:00 2001 From: Alexey Avramov Date: Mon, 11 Jun 2018 16:08:28 +0900 Subject: [PATCH] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20verbosity=20=D0=BE=D0=BF=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 13 +++- nohang | 193 +++++++++++++++++++++++++--------------------------- nohang.conf | 50 ++++++++------ 3 files changed, 135 insertions(+), 121 deletions(-) diff --git a/README.md b/README.md index ff7c7fa..24ffcb5 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ https://2ch.hk/s/res/2310304.html#2311483, https://archive.li/idixk - по умолчанию высокий приоритет процесса `nice -20`, может регулироваться через конфиг - предотвращение самоубийства с помощью `self_oom_score_adj = -1000` - возможность задания `oom_score_min` для предотвращения убийства невиновных -- опциональность печати результатов проверки памяти +- verbosity: опциональность печати параметров конфига при старте программы, опциональность печати результатов проверки памяти - `min_delay_after_sigkill` для предотвращения массовых убийств - наличие `man` страницы - наличие установщика для пользователей `systemd` @@ -90,5 +90,16 @@ sudo ./uninstall.sh ``` К опциям прилагается описание. Отредактируйте значения параметров в соответствии с вашими предпочтениями и перезапустите сервис командой `sudo systemctl restart nohang`. + +### Почему Python, а не C? + +- Скорость разработки на Python значительно выше. Больше фич за приемлемое время. +- Практически единственный минус разработки на Python - большее потребление памяти. + + ### Известные баги В рабочем алгоритме известных нет, если найдете - пишите в [Issues](https://github.com/hakavlad/nohang/issues). + + + + diff --git a/nohang b/nohang index d8d3291..151e85b 100755 --- a/nohang +++ b/nohang @@ -56,12 +56,7 @@ else: exit() - - -print('path to config:', config) - - - +print('Path to nohang config file:', config) try: @@ -90,6 +85,29 @@ except IndexError: +########################################################################################### + + +# проверка наличия параметров в словаре, их извречение из словаря + + +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 False), exit!'.format( + print_config + ) + ) + exit() +else: + print('print_config not in config, exit!') + exit() + if 'print_mem_check_results' in config_dict: print_mem_check_results = config_dict['print_mem_check_results'] @@ -99,17 +117,15 @@ if 'print_mem_check_results' in config_dict: print_mem_check_results = False else: print( - 'invalid mlockall value {} (should be True or False), exit!'.format( + 'invalid print_mem_check_results value {} (should be True or False), exit!'.format( print_mem_check_results ) ) exit() - print('print_mem_check_results: {}'.format(print_mem_check_results)) else: print('print_mem_check_results not in config, exit!') exit() - if 'mlockall' in config_dict: mlockall = config_dict['mlockall'] if mlockall == 'True': @@ -123,133 +139,103 @@ if 'mlockall' in config_dict: ) ) exit() - print('mlockall: {}'.format(mlockall)) else: print('mlockall not in config, exit!') exit() - if 'self_nice' in config_dict: self_nice = int(config_dict['self_nice']) - print('self_nice: {}'.format(self_nice)) else: print('self_nice not in config, exit!') exit() - if 'self_oom_score_adj' in config_dict: self_oom_score_adj = int(config_dict['self_oom_score_adj']) - print('self_oom_score_adj: {}'.format(self_oom_score_adj)) else: print('self_oom_score_adj not in config, exit!') exit() - if 'rate_mem' in config_dict: rate_mem = float(config_dict['rate_mem']) if rate_mem <= 0: print('rate_mem должен быть положительным') exit() - print('rate_mem: {}'.format(rate_mem)) else: print('rate_mem not in config, exit!') exit() - if 'rate_swap' in config_dict: rate_swap = float(config_dict['rate_swap']) if rate_swap <= 0: print('rate_swap должен быть положительным') exit() - print('rate_swap: {}'.format(rate_swap)) else: print('rate_swap not in config, exit!') exit() - if 'rate_zram' in config_dict: rate_zram = float(config_dict['rate_zram']) if rate_zram <= 0: print('rate_zram должен быть положительным') exit() - print('rate_zram: {}'.format(rate_zram)) else: print('rate_zram not in config, exit!') exit() - if 'mem_min_sigterm' in config_dict: mem_min_sigterm = config_dict['mem_min_sigterm'] - print('mem_min_sigterm: {}'.format(mem_min_sigterm)) else: print('mem_min_sigterm not in config, exit!') exit() - if 'mem_min_sigkill' in config_dict: mem_min_sigkill = config_dict['mem_min_sigkill'] - print('mem_min_sigkill: {}'.format(mem_min_sigkill)) else: print('mem_min_sigkill not in config, exit!') exit() - if 'swap_min_sigterm' in config_dict: swap_min_sigterm = config_dict['swap_min_sigterm'] - print('swap_min_sigterm: {}'.format(swap_min_sigterm)) else: print('swap_min_sigterm not in config, exit!') exit() - if 'swap_min_sigkill' in config_dict: swap_min_sigkill = config_dict['swap_min_sigkill'] - print('swap_min_sigkill: {}'.format(swap_min_sigkill)) else: print('swap_min_sigkill not in config, exit!') exit() - if 'zram_max_sigterm' in config_dict: zram_max_sigterm = config_dict['zram_max_sigterm'] - print('zram_max_sigterm: {}'.format(zram_max_sigterm)) else: print('zram_max_sigterm not in config, exit!') exit() - if 'zram_max_sigkill' in config_dict: zram_max_sigkill = config_dict['zram_max_sigkill'] - print('zram_max_sigkill: {}'.format(zram_max_sigkill)) else: print('zram_max_sigkill not in config, exit!') exit() - if 'min_delay_after_sigterm' in config_dict: min_delay_after_sigterm = float(config_dict['min_delay_after_sigterm']) - print('min_delay_after_sigterm: {}'.format(min_delay_after_sigterm)) else: print('min_delay_after_sigterm not in config, exit!') exit() - if 'min_delay_after_sigkill' in config_dict: min_delay_after_sigkill = float(config_dict['min_delay_after_sigkill']) - print('min_delay_after_sigkill: {}'.format(min_delay_after_sigkill)) else: print('min_delay_after_sigkill not in config, exit!') exit() - if 'oom_score_min' in config_dict: oom_score_min = int(config_dict['oom_score_min']) - print('oom_score_min: {}'.format(oom_score_min)) else: print('oom_score_min not in config, exit!') exit() - if 'decrease_oom_score_adj_enable' in config_dict: decrease_oom_score_adj_enable = config_dict['decrease_oom_score_adj_enable'] if decrease_oom_score_adj_enable == 'True': @@ -258,33 +244,93 @@ if 'decrease_oom_score_adj_enable' in config_dict: decrease_oom_score_adj_enable = False else: print( - 'invalid mlockall value {} (should be True or False), exit!'.format( + 'invalid decrease_oom_score_adj_enable value {} (should be True or False), exit!'.format( decrease_oom_score_adj_enable ) ) exit() - print('decrease_oom_score_adj_enable: {}'.format(decrease_oom_score_adj_enable)) else: print('decrease_oom_score_adj_enable not in config, exit!') exit() - if 'oom_score_adj_before' in config_dict: oom_score_adj_before = int(config_dict['oom_score_adj_before']) - print('oom_score_adj_before: {}'.format(oom_score_adj_before)) else: print('oom_score_adj_before not in config, exit!') exit() - if 'oom_score_adj_after' in config_dict: oom_score_adj_after = config_dict['oom_score_adj_after'] - print('oom_score_adj_after: {}'.format(oom_score_adj_after)) else: print('oom_score_adj_after not in config, exit!') exit() +########################################################################################### + + +# повышаем приоритет +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('print_config: {}'.format(print_config)) + print('print_mem_check_results: {}'.format(print_mem_check_results)) + 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('rate_mem: {}'.format(rate_mem)) + print('rate_swap: {}'.format(rate_swap)) + print('rate_zram: {}'.format(rate_zram)) + 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('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)) + 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)) + @@ -294,11 +340,6 @@ else: - - - - - def decrease_oom_score_adj(oom_score_adj_before, oom_score_adj_after): #print('Decrease oom_score_adj...') # цикл для наполнения oom_list @@ -461,8 +502,6 @@ def sig_level_to_kb(string): 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) @@ -507,53 +546,6 @@ else: swap_min_sigkill_kb = swap_min_sigkill_swap -# print('\nswap term is percent:', swap_term_is_percent) -# print('swap kill is percent:', swap_kill_is_percent) - - - - -print("\ncurrent process's effective user id", os.geteuid()) - -if os.geteuid() == 0: - root = True -else: - root = False - -print() - -# lock all memory for prevent swapping -if mlockall: - print('mlockall = True') - print('try to lock memory...') - result = CDLL('libc.so.6', use_errno=True).mlockall(3) - if result is 0: - print('memory locked!', 'result', result) - else: - print('cannot lock memory!', 'result', result) -else: - print('mlockall != True') - - -# повышаем приоритет -try: - os.nice(self_nice) - print('self_nice = {}'.format(self_nice)) -except PermissionError: - pass - - -# запрещаем самоубийство по возможности -try: - with open('/proc/self/oom_score_adj', 'w') as file: - file.write('{}\n'.format(self_oom_score_adj)) - print('self_oom_score_adj = {}'.format(self_oom_score_adj)) -except PermissionError: - pass -except OSError: - pass - - def kib_to_mib(num): return round(num / 1024.0) @@ -562,7 +554,6 @@ def kib_to_mib(num): - print('Start monitoring...') # рабочий цикл @@ -582,6 +573,8 @@ while True: swap_free = int(line.split(':')[1].split(' ')[-2]) break + + if swap_kill_is_percent: swap_min_sigkill_kb = swap_total * swap_min_sigkill_percent / 100.0 diff --git a/nohang.conf b/nohang.conf index 28a1c9d..fedd863 100644 --- a/nohang.conf +++ b/nohang.conf @@ -1,14 +1,19 @@ Nohang config file -##################################################################### - Комментариями являются строки, начинающиеся с решёток, пробелов и табуляций. Инлайновые комментарии запрещены. Пробелы допустиы внутри строк в любом количестве. ##################################################################### + I. VERBOSITY + + Печатать параметров конфига при запуске программы. + Допустимые значения: True и False + +print_config = True + Печатать ли результаты измерения доступной памяти. Допустимые значения: True и False @@ -16,31 +21,31 @@ print_mem_check_results = True ##################################################################### + II. САМОЗАЩИТА ПРОЦЕССА + True - заблокировать процесс в памяти для запрета его своппинга. False - не блокировать. Значения чувствительны к регистру! - Требует рут прав. mlockall = True -##################################################################### + Установка отрицательных значений self_nice и self_oom_score_adj + требует наличия root прав. Повысить приоритет процесса, установив niceness -20 Допустимые значения - целые числа из диапазона [-20; 19] - Требует рут прав. self_nice = -20 -##################################################################### - Задать oom_score_adj для процесса. - Задание значения -1000 запретит самоубийство. + Установка значения -1000 запретит самоубийство. Допустимые значения - целые числа из диапазона [-1000; 1000] - Требует рут прав. self_oom_score_adj = -1000 ##################################################################### + III. ИНТЕНСИВНОСТЬ МОНИТОРИНГА + Коэффициенты, влияющие на интенсивность мониторинга. Допустимыми значениями являются положительные числа. Уменьшение коэффициентов способно снизить нагрузку на @@ -53,7 +58,7 @@ self_oom_score_adj = -1000 и тем самым снизить нагрузку на процессор. В дефолтных настройках на данной интенсивности демон работает - очень хорошо, успешно справляясь с резкими скачками потребления + достаточно хорошо, успешно справляясь с резкими скачками потребления памяти. rate_mem = 6 @@ -62,6 +67,8 @@ rate_zram = 1 ##################################################################### + IV. ПОРОГИ ДЛЯ ОТПРАВКИ СИГНАЛОВ + Задание уровней доступной памяти, ниже которых происходит отправка сигналов SIGTERM или SIGKILL. @@ -75,11 +82,11 @@ rate_zram = 1 mem_min_sigterm = 0.5 G swap_min_sigkill = 200 M -mem_min_sigterm = 4% -mem_min_sigkill = 2% +mem_min_sigterm = 8% +mem_min_sigkill = 4% -swap_min_sigterm = 4% -swap_min_sigkill = 2% +swap_min_sigterm = 8% +swap_min_sigkill = 4% Задание общей доли zram в памяти, при превышении которой происходит отправка соответствующих сигналов. @@ -89,21 +96,22 @@ swap_min_sigkill = 2% отзывчивость системы. Может также задаваться в %, K, M, G -zram_max_sigterm = 60 % -zram_max_sigkill = 65 % +zram_max_sigterm = 55 % +zram_max_sigkill = 60 % ##################################################################### + VI. ПРЕДОТВРАЩЕНИЕ ИЗБЫТОЧНЫХ УБИЙСТВ + Минимальное значение oom_score, которым должен обладать процесс для того, чтобы ему был отправлен сигнал. Позволяет предотвратить убийство невиновных если что-то пойдет не так. + Значение должно быть целым числом из диапазона [0; 1000] oom_score_min = 15 -##################################################################### - Мнинмальная задержка после отправки соответствующих сигналов для предотвращения риска убийства сразу множества процессов. Должно быть неотрицательным числом. @@ -113,6 +121,8 @@ min_delay_after_sigkill = 3 ##################################################################### + VII. ЗАЩИТА ПРОЦЕССОВ CHROMIUM ОТ СМЕРТИ ПО ЧУЖОЙ ВИНЕ + Процессы браузера chromium обычно имеют oom_score_adj 200 или 300. Это приводит к тому, что процессы хрома умирают первыми вместо действительно тяжелых процессов. @@ -124,9 +134,9 @@ min_delay_after_sigkill = 3 False - не изменять oom_score_adj процессов перед поиском жертвы. Значения чувствительны к регистру! - Требует рут прав. + Требует root прав. -decrease_oom_score_adj_enable = True +decrease_oom_score_adj_enable = False oom_score_adj_before = 50 oom_score_adj_after = 10