18

Мы все это испытали - какую-то программу просят сделать что-то, что требует огромного количества памяти. Он покорно пытается выделить всю эту память, и система немедленно начинает биться, бесконечно менять местами и становиться вялой или не реагирующей.

Я недавно испытал это на своем ноутбуке Ubuntu из-за сценария Matlab, пытающегося выделить смехотворно огромную матрицу. После ~ 5+ минут разгрома я смог Ctrl-F1 к консоли и убить Matlab. Я бы предпочел иметь какую-нибудь горячую клавишу, которая сразу же дала бы мне контроль над системой и позволила бы мне убить процесс оскорбления; или, может быть, просто молча откажитесь выделить такой большой буфер.

  1. Каков самый быстрый способ восстановить контроль над системой Linux, которая перестала отвечать или стала очень вялой из-за чрезмерной подкачки?

  2. Существует ли эффективный способ предотвратить такой обмен в первую очередь, например, путем ограничения объема памяти, который процессу разрешено пытаться выделить?

6 ответов6

11

Нажмите Alt-SysRq-F, чтобы завершить процесс, используя больше памяти:

  • Клавиша SysRq обычно сопоставляется клавише Print.
  • Если вы используете графический рабочий стол, вам может потребоваться нажать Ctrl-Alt-SysRq-F в случае, если нажатие Alt-SysRq запускает другое действие (например, программу моментального снимка).
  • Если вы используете ноутбук, вам, возможно, придется нажать функциональную клавишу.
  • Для получения дополнительной информации прочитайте статью в Википедии.
4

Я сделал скрипт для этой цели - https://github.com/tobixen/thrash-protect

Этот сценарий успешно работает на производственных серверах, рабочих станциях и ноутбуках. Этот сценарий не убивает процессы, но временно приостанавливает их - позже у меня было несколько ситуаций, в которых я совершенно уверен, что потерял контроль из-за перебора, если бы не этот простой сценарий. В "худшем" случае нарушающий процесс будет сильно замедлен и, в конце концов, будет убит ядром (OOM), в "лучшем" случае вызывающий процесс действительно завершится ... в любом случае, на сервере или рабочей станции останется относительно отзывчивым, чтобы можно было легко исследовать ситуацию.

Конечно, "купить больше памяти" или "не использовать своп" - это два альтернативных, более традиционных ответа на вопрос «как избежать побоев?", но в целом они, как правило, работают не так хорошо (установка большего объема памяти может быть нетривиальной задачей, мошеннический процесс может поглотить всю память, независимо от того, сколько она установлена, и могут возникнуть проблемы с перебрасыванием даже без обмена) когда недостаточно памяти для буферизации / кеширования). Я рекомендую защиту от трэша плюс много места подкачки.

3
  1. Каков самый быстрый способ восстановить контроль над системой Linux, которая перестала отвечать или стала очень вялой из-за чрезмерной подкачки?

Уже ответил выше с Alt-SysRq-F

  1. Существует ли эффективный способ предотвратить такой обмен в первую очередь, например, путем ограничения объема памяти, который процессу разрешено пытаться выделить?

Я отвечаю на эту 2-ю часть. Да, ulimit все еще работает достаточно хорошо, чтобы ограничить один процесс. Вы можете:

  • установить мягкий предел для процесса, который, как вы знаете, скорее всего выйдет из-под контроля
  • установить жесткий лимит для всех процессов, если вы хотите дополнительную страховку

Также, как кратко сказано:

Вы можете использовать CGroups для ограничения использования ресурсов и предотвращения таких проблем

Действительно, cgroups предлагают более продвинутый контроль, но в настоящее время, по моему мнению, его сложнее настроить.

Старая школа ulimit

Один раз

Вот простой пример:

$ bash
$ ulimit -S -v $((1*2**20))
$ r2(){r2 $@$@;};r2 r2
bash: xmalloc: .././subst.c:3550: cannot allocate 134217729 bytes (946343936 bytes allocated)

Это:

  • Устанавливает мягкое ограничение общего использования памяти в 1 ГБ (ulimit предполагает ограничение в единицах кБ)
  • Запускает рекурсивный вызов функции bash r2(){ r2 $@$@;};r2 r2 , который экспоненциально жует ЦП и ОЗУ, бесконечно удваивая себя при запросе стековой памяти.

Как видите, он остановился при попытке запросить более 1 ГБ.

Обратите внимание, -v работает с распределением виртуальной памяти (всего, то есть физического + подкачка).

Постоянная защита

Для того, чтобы ограничить виртуальное выделение памяти, as эквивалент -v для limits.conf

Я делаю следующее, чтобы защитить себя от любого неправильного поведения:

  • Установите жесткое ограничение адресного пространства для всех процессов.
  • address space limit = <physical memory> - 256MB .
  • Поэтому ни один процесс с использованием жадной памяти или активной петли и утечки памяти не может потреблять ВСЕ физическую память.
  • Запас 256 Мбайт для существенной обработки с помощью SSH или консоли.

Один лайнер:

$ sudo bash -c "echo -e \"*\thard\tas\t$(($(grep -E 'MemTotal' /proc/meminfo | grep -oP '(?<=\s)\d+(?=\skB$)') - 256*2**10))\" > /etc/security/limits.d/mem.conf"

Для проверки это приводит к следующему (например, в системе 16 ГБ):

$ cat /etc/security/limits.d/mem.conf
*   hard    as      16135196
$ ulimit -H -v
161351960

Заметки:

  • Только смягчает против одного процесса, идущего за борт с использованием памяти.
  • Не предотвратит многопроцессорную рабочую нагрузку с большой нагрузкой на память, вызывающей побои (тогда ответом будет cgroups).
  • Не используйте опцию rss в limit.conf. Это не соблюдается новыми ядрами.
  • Это консервативно.
    • Теоретически, процесс может спекулятивно запрашивать много памяти, но только активно использовать подмножество (меньшее использование рабочего набора / резидентная память).
    • Вышеуказанное жесткое ограничение приведет к тому, что такие процессы будут прерваны (даже если бы они могли нормально работать, учитывая, что Linux допускает перегрузку адресного пространства виртуальной памяти).

Более новые CGroups

Предлагает больше контроля, но в настоящее время более сложный в использовании:

  • Улучшает предложение ulimit.
    • memory.max_usage_in_bytes может учитывать и ограничивать физическую память отдельно.
    • Принимая во внимание, что ulimit -m и / или rss в limits.conf должны предлагать аналогичную функциональность, но это не работает с ядра Linux 2.4.30!
  • Необходимо включить некоторые флаги cgroup ядра в загрузчике: cgroup_enable=memory swapaccount=1 .
    • Это не произошло по умолчанию с Ubuntu 16.04.
    • Вероятно, из-за некоторых последствий для производительности дополнительных накладных расходов.
  • Материал cgroup / systemd является относительно новым и немного меняющимся, поэтому поток вверх по течению подразумевает, что производители дистрибутивов Linux еще не сделали его простым в использовании. Между 14.04LTS и 16.04LTS изменился инструментарий пользовательского пространства для использования cgroups.
    • cgm теперь кажется официально поддерживаемым инструментом для пользователей.
    • Похоже, что в системных файлах юнитов пока нет предопределенных значений по умолчанию "vendor / distro", чтобы расставить приоритеты для важных сервисов, таких как ssh.

Например, чтобы проверить текущие настройки:

$ echo $(($(cat /sys/fs/cgroup/memory/memory.max_usage_in_bytes) / 2**20)) MB
11389 MB
$ cat /sys/fs/cgroup/memory/memory.stat
...

Например, чтобы ограничить память одного процесса:

$ cgm create memory mem_1G
$ cgm setvalue memory mem_1G memory.limit_in_bytes $((1*2**30))
$ cgm setvalue memory mem_1G memory.memsw.limit_in_bytes $((1*2**30))
$ bash
$ cgm movepid memory mem_1G $$
$ r2(){ r2 $@$@;};r2 r2
Killed

Чтобы увидеть это в действии, пережевывающее ОЗУ как фоновый процесс и затем убитое:

$ bash -c 'cgm movepid memory mem_1G $$; r2(){ r2 $@$@;};r2 r2' & while [ -e /proc/$! ]; do ps -p $! -o pcpu,pmem,rss h; sleep 1; done
[1] 3201
 0.0  0.0  2876
 102  0.2 44056
 103  0.5 85024
 103  1.0 166944
 ...
98.9  5.6 920552
99.1  4.3 718196
[1]+  Killed                  bash -c 'cgm movepid memory mem_1G $$; r2(){ r2 $@$@;};r2 r2'

Обратите внимание на экспоненциальный (степень 2) рост запросов памяти.

Будем надеяться, что в будущем «дистрибутив / вендоры» предварительно сконфигурируют приоритеты и ограничения cgroup (через модули systemd) для таких важных вещей, как SSH и графический стек, чтобы они никогда не испытывали недостатка памяти.

2

Вы можете нажать Ctrl - Z, чтобы приостановить программу. Затем вы можете kill %1 (или любой другой номер задания, или вы можете использовать PID).

Вы можете использовать команду ulimit чтобы попытаться ограничить объем памяти, доступный для процесса.

1

Вы можете использовать CGroups для ограничения использования ресурсов и предотвращения таких проблем:https://en.wikipedia.org/wiki/Cgroups

0

Было бы неплохо, если бы в графическом интерфейсе была бы горячая клавиша для отправки SIGSTOP в любое приложение, на которое была сфокусирована!

Всегда есть классическая команда xkill (из xorg-x11-apps-7.4-14.fc14.src.rpm в моей системе). Я предполагаю, что не должно быть слишком сложно создать клон, который посылает SIGSTOP вместо того, чтобы убивать целевое окно.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .