обеспечена поддержка десктопных уведомлений

This commit is contained in:
Alexey Avramov 2018-06-16 00:03:52 +09:00
parent b0805dc450
commit 696eae2dc7
3 changed files with 114 additions and 15 deletions

View File

@ -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`

93
nohang
View File

@ -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 <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:
@ -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)

View File

@ -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