From 696eae2dc7951b794cc3bf9df6534c509cf08035 Mon Sep 17 00:00:00 2001 From: Alexey Avramov Date: Sat, 16 Jun 2018 00:03:52 +0900 Subject: [PATCH] =?UTF-8?q?=D0=BE=D0=B1=D0=B5=D1=81=D0=BF=D0=B5=D1=87?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=B4=D0=B5=D1=81=D0=BA=D1=82=D0=BE=D0=BF=D0=BD?= =?UTF-8?q?=D1=8B=D1=85=20=D1=83=D0=B2=D0=B5=D0=B4=D0=BE=D0=BC=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- nohang | 93 ++++++++++++++++++++++++++++++++++++++++++++++++----- nohang.conf | 33 +++++++++++++++---- 3 files changed, 114 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 70b82b5..7b42636 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ https://2ch.hk/s/res/2310304.html#2311483, https://archive.li/idixk ### Некоторые особенности - задача - препятствовать зависанию системы при нехватке доступной памяти, а также корректное завершение процессов с целью увеличения объема доступной памяти -- демон на Python 3, VmRSS не более 13 MiB +- демон на Python 3, VmRSS от 10 до 14 MiB в зависимости от настроек - требуется ядро `Linux 3.14` или новее - периодически проверяет объем доступной памяти, при дефиците памяти отправляет `SIGKILL` или `SIGTERM` процессу с наибольшим `oom_score` - поддержка работы со `zram`, возможность реакции на `mem_used_total` @@ -50,6 +50,7 @@ https://2ch.hk/s/res/2310304.html#2311483, https://archive.li/idixk - возможность задания `oom_score_min` для предотвращения убийства невиновных - verbosity: опциональность печати параметров конфига при старте программы, опциональность печати результатов проверки памяти и времени между проверками памяти - возможность предотвращения избыточного убийства процессов с помощью задания миниального `oom_score` для убиваемых процессов и установка минимальной задержки просле отправки сигналов (параметры конфига `min_delay_after_sigkill` и `min_delay_after_sigterm`) +- возможность показа десктопных уведомлений - наличие `man` страницы - наличие установщика для пользователей `systemd` - протестировано на `Debian 9 x86_64`, `Debian 8 i386`, `Fedora 28 x86_64` diff --git a/nohang b/nohang index b0ecc4a..ca5bad7 100755 --- a/nohang +++ b/nohang @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -# Nohang - No Hang Daemon - +# Nohang - The No Hang Daemon for Linux ################################################################################ @@ -17,6 +16,10 @@ from argparse import ArgumentParser ### задание констант +version = 'unknown' + +sig_dict = {9:'SIGKILL', 15:'SIGTERM'} + # директория, в которой запущен скрипт cd = os.getcwd() @@ -174,14 +177,39 @@ def find_victim_and_send_signal(signal): name = pid_to_name(pid) print( - ' Try to send signal {} to {}, Pid {}, oom_score {}'.format( - signal, name, pid, oom_score + ' 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 {},\nPid: {}, oom_score: {}" &'.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: @@ -326,6 +354,7 @@ else: 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': @@ -345,6 +374,7 @@ else: exit() + if 'print_sleep_periods' in config_dict: print_sleep_periods = config_dict['print_sleep_periods'] if print_sleep_periods == 'True': @@ -400,11 +430,11 @@ else: if 'self_oom_score_adj' in config_dict: self_oom_score_adj = config_dict['self_oom_score_adj'] - if string_to_int_convert_test(self_oom_score_adj) is None: + 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: - 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() @@ -593,6 +623,49 @@ else: 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() + + ################################################################################ ### получение уровней в кибибайтах @@ -732,7 +805,7 @@ if print_config: print('zram_max_sigterm: {}'.format(zram_max_sigterm)) print('zram_max_sigkill: {}'.format(zram_max_sigkill)) - print('\nV. ПРЕДОТВРАЩЕНИЕ УБИЙСТВ НЕВИНОВНЫХ') + 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)) @@ -745,6 +818,11 @@ if print_config: 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 @@ -757,7 +835,6 @@ print('\nStart monitoring...') ### цикл проверки уровней доступной памяти - while True: #decrease_oom_score_adj(oom_score_adj_before, oom_score_adj_after) diff --git a/nohang.conf b/nohang.conf index 7c7a7d0..f1aa80d 100644 --- a/nohang.conf +++ b/nohang.conf @@ -88,11 +88,11 @@ rate_zram = 1 mem_min_sigterm = 0.5 G swap_min_sigkill = 200 M -mem_min_sigterm = 8% -mem_min_sigkill = 4% +mem_min_sigterm = 8 % +mem_min_sigkill = 4 % -swap_min_sigterm = 8% -swap_min_sigkill = 4% +swap_min_sigterm = 8 % +swap_min_sigkill = 4 % Задание общей доли zram в памяти, при превышении которой происходит отправка соответствующих сигналов. @@ -107,7 +107,7 @@ zram_max_sigkill = 60 % ##################################################################### - V. ПРЕДОТВРАЩЕНИЕ УБИЙСТВ НЕВИНОВНЫХ + V. THE PREVENTION OF KILLING INNOCENT VICTIMS Минимальное значение oom_score, которым должен обладать процесс для того, чтобы ему был отправлен сигнал. @@ -122,7 +122,7 @@ oom_score_min = 15 для предотвращения риска убийства сразу множества процессов. Должно быть неотрицательным числом. -min_delay_after_sigterm = 0.1 +min_delay_after_sigterm = 0.2 min_delay_after_sigkill = 3 Процессы браузера chromium обычно имеют oom_score_adj @@ -145,3 +145,24 @@ decrease_oom_score_adj = False oom_score_adj_before = 10 oom_score_adj_after = 5 +##################################################################### + + VI. DESKTOP NOTIFICATIONS + + Эта возможность требует наличия notify-send в системе. + В Debian/Ubuntu это обеспечивается установкой пакета + libnotify-bin. Также требуется наличие notification-daemon. + Допустимые значения: True и False + +desktop_notifications = False + + Может использоваться другая команда, если ее синтаксис совместим + с синтаксисом notify-send + +notify_command = notify-send + + Время показа уведомлений в секундах. + Должно быть положительным числом. + +notify_time = 15 +