Skip to content


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

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

  1. "bash: fork: Resource temporarily unavailable"

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

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

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

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

  1. echo s > /proc/sysrq-trigger
  2. echo u > /proc/sysrq-trigger
  3. echo b > /proc/sysrq-trigger

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

  1. kernel.sysrq = 1

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

  1. echo h > /proc/sysrq-trigger

Размещено в категории *nix, Howto. Теги: , , , .

Комментариев: 4

Чтобы быть всегда в курсе здесь происходящего, Вы можете подписаться на RSS feed для комментариев на эту заметку.

  1. Rasul said

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

  2. Admin said

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

  3. Admin said

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

  4. Rasul said

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

Some HTML is OK

(required)

(required, but never shared)

, или ответить через trackback.

Страница 1 из 11