Skip to content


Как прибить процесс средствами одного только bash

Однажды одно криво написанное java-приложение серьёзно поднапрягло сервер - почти на любую команду в консоли вылазила такая вот ошибка:

"bash: fork: Resource temporarily unavailable"

Конечно, первым желанием было запустить top или ps чтобы вычислить и пристрелить зажравшийся процесс, но, вот незадача, консоль всё тупо продолжала гнуть свою линию в виде "bash: fork: Resource temporarily unavailable" на все мои попытки запустить какую-либо внешнюю (не из списка bash builtins) команду... Я сразу заподозрил, что зажравшимся процессом, скорей всего, является java. Но вот как прибить оный, не зная его PID-а? И тут я вспомнил, про файловую систему /proc... Затем после пары минут экспериментов родился такой вот скрипт, который успешно позволил решить мне задачу нахождения PID-а процесса java, используя только встроенные в коммандный интерпретатор (bash) команды:

#!/bin/bash
for p in /proc/[0-9]* ; do
  exec 9< $p/cmdline
  read -u 9 line
  [[ $line =~ java ]] && echo "|$p|: |$line|"
  exec 9<&-
done

В строке 3 мы назначаем файловому дескриптору 9 псевдо-файл /proc/<pid>/cmdline, далее читаем из него в переменную line, потом сравниваем эту переменную со строкой "java" и в случае совпадения выводим имя файла, имя которого соответствует PID-у процесса. В строке 6 освобождаем файловый дескриптор 9. Затем оставалось только набрать команду kill с найденным таким образом вожделенным PID-ом (к счастью, kill является встроенной в bash). И, о чудо, консоль снова стала себя адекватно вести, LA упало, память освободилась. Уже потом, по графикам cacti стало понятно, что причиной такого безобразия стало неимоверно большое количество потоков (около 70000), которое успело наплодить вышеупомянутое криво написанное java-приложение.

А вот если бы мне не удалось прибить процесс, тогда, пожалуй, пришлось бы сервер перезагружать. Поскольку стоит он далеко, за морями-океанами, то нажать кнопочку reset на корпусе тут не получилось бы. Тогда бы я просто набрал в консоли

echo s > /proc/sysrq-trigger
echo u > /proc/sysrq-trigger
echo b > /proc/sysrq-trigger

Что привело бы к сбросу дисковых буферов (строка 1), перемонтированию файловых систем в режим read-only (строка 2) и немедленной перезагрузке (строка 3). Если же к консоли всё таки имеется непосредственный доступ (физически или через IP-KVM), то следует предусмотрительно в /etc/sysctl.conf добавить строчку

kernel.sysrq = 1

Тогда будет работать волшебная комбинация клавиш AltGr+PrtScr+<command>, где <command> - однобуквенная команда, посылаемая ядру. Список всех поддерживаемых команд с их очень кратким описанием можно получить на консоли (и в /var/log/messages) если нажать комбинацию AltGr+PrtScr+h или выполнить команду:

echo h > /proc/sysrq-trigger

Полный список магических буковок можно найти тут.

Posted in *nix, Howto.

Tagged with , , , .


4 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

  1. Rasul says

    Так тоже работает. Не нужно открывать другие дескрипторы.
    for p in /proc/[0-9]* ; do
    read line < $p/cmdline
    [[ $line =~ chrome ]] && echo "|$p|: |$line|"
    done

  2. Admin says

    Гм, действительно. Так как-то изящнее и проще для понимания выходит.

  3. Admin says

    Тут раньше первыми двумя командами было "echo e > /proc/sysrq-trigger" и "echo i > /proc/sysrq-trigger". Но как показала практика, это была плохая идея :) Ибо после выполнения команды "e", запущенной в ssh-сессии, прибиваются все процессы, включая ssh-демон :) И дальше уже ничего сделать нельзя, кроме как перегрузить сервер по питанию.

  4. Rasul says

    Можно написать echo $(( 2+4+8+16+32+128 )) > /proc/sys/kernel/sysrq , тем самым отключить возможность посылать сигналы процессам средствами sysrq. Удобно в том случае если не планируется иметь консольное подключение к серверу.

You must be logged in to post a comment.