diff --git a/nohang b/nohang index ee8a502..bb16866 100755 --- a/nohang +++ b/nohang @@ -194,7 +194,6 @@ def pid_to_name(pid): return '' - def pid_to_cmdline(pid): """ Get process cmdline by pid. @@ -209,11 +208,11 @@ def pid_to_cmdline(pid): return '' - - - - - +def pid_to_uid(pid): + with open('/proc/' + pid + '/status') as f: + for n, line in enumerate(f): + if n is uid_index: + return line.split('\t')[1] def send_notify_warn(): @@ -324,13 +323,6 @@ def sleep_after_send_signal(signal): sleep(min_delay_after_sigterm) - - - - - - - def find_victim_and_send_signal(signal): """ Find victim with highest badness and send SIGTERM/SIGKILL @@ -338,10 +330,11 @@ def find_victim_and_send_signal(signal): pid_badness_list = [] for pid in os.listdir('/proc'): - # только директории, имена которых состоят только из цифр, за исключением /proc/1/ + # only directories whose names consist only of numbers, except /proc/1/ if pid[0].isdecimal() is False or pid == '1': continue + # find and modify badness (if it needs) try: badness = int(rline1('/proc/' + pid + '/oom_score')) @@ -354,13 +347,13 @@ def find_victim_and_send_signal(signal): name = pid_to_name(pid) cmdline = pid_to_cmdline(pid) - #uid = pid_to_uid(pid) + uid = pid_to_uid(pid) # skip kthreads if cmdline == '': continue - #print([pid], [name], [cmdline]) + #print([uid], [name], [cmdline]) if search(avoid_regex, name) is not None: badness = int(badness / avoid_factor) @@ -382,6 +375,16 @@ def find_victim_and_send_signal(signal): print(' Cmdline matches with prefer_re_cmdline \033[33m{}\033[0m: \033[33m{}\033[0m, Name: {}'.format( prefer_re_cmdline, cmdline, name)) + if search(avoid_re_uid, uid) is not None: + badness = int(badness / avoid_uid_factor) + print(' UID matches with avoid_re_uid \033[33m{}\033[0m: \033[33m{}\033[0m, Name: {}'.format( + avoid_re_uid, uid, name)) + + if search(prefer_re_uid, uid) is not None: + badness = int((badness + 1) * prefer_cmd_factor) + print(' UID matches with prefer_re_uid \033[33m{}\033[0m: \033[33m{}\033[0m, Name: {}'.format( + prefer_re_uid, uid, name)) + except FileNotFoundError: badness = 0 except ProcessLookupError: @@ -449,11 +452,9 @@ def find_victim_and_send_signal(signal): except ValueError: pass - if detailed_rss: print('anon file shmem, MiB:', anon_rss, file_rss, shmem_rss) - if execute_the_command and signal is SIGTERM and name in etc_dict: command = etc_dict[name] exit_status = os.system(etc_dict[name]) @@ -523,7 +524,6 @@ def find_victim_and_send_signal(signal): print(victim_badness_is_too_small) - sleep_after_send_signal(signal) @@ -664,18 +664,6 @@ def calculate_percent(arg_key): ########################################################################## - - - - - - - - - - - - # find mem_total # find positions of SwapFree and SwapTotal in /proc/meminfo @@ -778,7 +766,6 @@ print('The path to the config:', config) ########################################################################## - # parsing the config with obtaining the parameters dictionary # conf_parameters_dict @@ -846,18 +833,94 @@ mlockall = conf_parse_bool('mlockall') gui_low_memory_warnings = conf_parse_bool('gui_low_memory_warnings') gui_notifications = conf_parse_bool('gui_notifications') decrease_oom_score_adj = conf_parse_bool('decrease_oom_score_adj') + regex_matching = conf_parse_bool('regex_matching') +if regex_matching: + from re import search + import sre_constants + execute_the_command = conf_parse_bool('execute_the_command') prefer_regex = conf_parse_string('prefer_regex') +if prefer_regex == '': + print('Invalid prefer_regex value, ' \ + 'regex pattern must not be empty') + exit() +# RE pattern validation +try: + search(prefer_regex, '') +except sre_constants.error: + print('Invalid prefer_regex value, ' \ + 'invalid RE pattern: {}'.format(prefer_regex)) + exit() + avoid_regex = conf_parse_string('avoid_regex') +if avoid_regex == '': + print('Invalid avoid_regex value, ' \ + 'regex pattern must not be empty') + exit() +# RE pattern validation +try: + search(avoid_regex, '') +except sre_constants.error: + print('Invalid avoid_regex value, ' \ + 'invalid RE pattern: {}'.format(avoid_regex)) + exit() prefer_re_cmdline = conf_parse_string('prefer_re_cmdline') +if prefer_re_cmdline == '': + print('Invalid prefer_re_cmdline value, ' \ + 'regex pattern must not be empty') + exit() +# RE pattern validation +try: + search(prefer_re_cmdline, '') +except sre_constants.error: + print('Invalid prefer_re_cmdline value, ' \ + 'invalid RE pattern: {}'.format(prefer_re_cmdline)) + exit() + + avoid_re_cmdline = conf_parse_string('avoid_re_cmdline') +if avoid_re_cmdline == '': + print('Invalid avoid_re_cmdline value, ' \ + 'regex pattern must not be empty') + exit() +# RE pattern validation +try: + search(avoid_re_cmdline, '') +except sre_constants.error: + print('Invalid avoid_re_cmdline value, ' \ + 'invalid RE pattern: {}'.format(avoid_re_cmdline)) + exit() +prefer_re_uid = conf_parse_string('prefer_re_uid') +if prefer_re_uid == '': + print('Invalid prefer_re_uid value, ' \ + 'regex pattern must not be empty') + exit() +# RE pattern validation +try: + search(prefer_re_uid, '') +except sre_constants.error: + print('Invalid prefer_re_uid value, ' \ + 'invalid RE pattern: {}'.format(prefer_re_uid)) + exit() +avoid_re_uid = conf_parse_string('avoid_re_uid') +if avoid_re_uid == '': + print('Invalid avoid_re_uid value, ' \ + 'regex pattern must not be empty') + exit() +# RE pattern validation +try: + search(avoid_re_uid, '') +except sre_constants.error: + print('Invalid avoid_re_uid value, ' \ + 'invalid RE pattern: {}'.format(avoid_re_uid)) + exit() mem_min_sigterm_kb, mem_min_sigterm_mb, mem_min_sigterm_percent = calculate_percent( @@ -1059,15 +1122,6 @@ else: exit() - - - - - - - - - if 'prefer_cmd_factor' in config_dict: prefer_cmd_factor = string_to_float_convert_test(config_dict['prefer_cmd_factor']) if prefer_cmd_factor is None: @@ -1094,20 +1148,30 @@ else: exit() +if 'prefer_uid_factor' in config_dict: + prefer_uid_factor = string_to_float_convert_test(config_dict['prefer_uid_factor']) + if prefer_uid_factor is None: + print('Invalid prefer_uid_factor value, not float\nExit') + exit() + if prefer_uid_factor < 1 and prefer_uid_factor > 1000: + print('prefer_uid_factor value out of range [1; 1000]\nExit') + exit() +else: + print('prefer_uid_factor not in config\nExit') + exit() - - - - - - - - - - - - +if 'avoid_uid_factor' in config_dict: + avoid_uid_factor = string_to_float_convert_test(config_dict['avoid_uid_factor']) + if avoid_uid_factor is None: + print('Invalid avoid_uid_factor value, not float\nExit') + exit() + if avoid_uid_factor < 1 and avoid_uid_factor > 1000: + print('avoid_uid_factor value out of range [1; 1000]\nExit') + exit() +else: + print('avoid_uid_factor not in config\nExit') + exit() if 'min_time_between_warnings' in config_dict: @@ -1329,16 +1393,11 @@ if print_config: # for calculating the column width when printing mem and zram mem_len = len(str(round(mem_total / 1024.0))) - if gui_notifications or gui_low_memory_warnings: from subprocess import Popen, PIPE notify_sig_dict = {SIGKILL: 'Killing', SIGTERM: 'Terminating'} -if regex_matching: - from re import search - - rate_mem = rate_mem * 1048576 rate_swap = rate_swap * 1048576 rate_zram = rate_zram * 1048576 diff --git a/nohang.conf b/nohang.conf index 228cb1e..bf98e64 100644 --- a/nohang.conf +++ b/nohang.conf @@ -108,10 +108,11 @@ min_delay_after_sigkill = 3 oom_score_adj_max значение oom_score_adj будет опущено до oom_score_adj_max перед поиском жертвы. + Enabling the option requires root privileges. Valid values are True and False. Values are case sensitive. -decrease_oom_score_adj = False +decrease_oom_score_adj = True Valid values are integers from the range [0; 1000]. @@ -141,13 +142,15 @@ regex_matching = False RE pattern must not be empty! - Matching with process names + Matching process names with RE patterns + + Прблема в том, что если включил регекс, то нельзя выключить ненужный мэтчинг. Можно только установить фактор в единицу. prefer_regex = foo Valid values are floating-point numbers from the range [1; 1000]. -prefer_factor = 3 +prefer_factor = 2 Badness of processes whose names correspond to avoid_regex will be calculated by the following formula: @@ -155,22 +158,41 @@ prefer_factor = 3 # Need more examples -avoid_regex = ^(sshd|Xorg)$ +avoid_regex = foo Valid values are floating-point numbers from the range [1; 1000]. -avoid_factor = 3 +avoid_factor = 2 - Matching with cmdlines + Matching cmlines with RE patterns -prefer_re_cmdline = ^/usr/lib/firefox +# re_match_cmdline = True -avoid_re_cmdline = ^/usr/lib/virtualbox +prefer_re_cmdline = voo + +avoid_re_cmdline = foo + +prefer_cmd_factor = 20 + +avoid_cmd_factor = 2 + + + Matching UIDs with RE patterns + + Note that UID=0 итак избегается для убийства. + +# re_match_uid = True + +prefer_re_uid = ^(0)$ + +avoid_re_uid=foo + +prefer_uid_factor = 200 + +avoid_uid_factor = 2 -prefer_cmd_factor = 3 -avoid_cmd_factor = 3 #####################################################################