Примечание: этот вопрос изначально был задан здесь, но время награды истекло, хотя на самом деле приемлемый ответ не был найден. Я повторно задаю этот вопрос, включая все детали, указанные в исходном вопросе.
Сценарий python запускает набор функций класса каждые 60 секунд с использованием модуля sched :
# sc is a sched.scheduler instance
sc.enter(60, 1, self.doChecks, (sc, False))
Сценарий выполняется как демонизированный процесс с использованием приведенного здесь кода .
Ряд методов класса, которые вызываются как часть doChecks, используют модуль subprocess для вызова системных функций с целью получения системной статистики:
ps = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE).communicate()[0]
Это работает нормально в течение определенного периода времени, прежде чем весь скрипт выйдет из строя со следующей ошибкой:
File "/home/admin/sd-agent/checks.py", line 436, in getProcesses
File "/usr/lib/python2.4/subprocess.py", line 533, in __init__
File "/usr/lib/python2.4/subprocess.py", line 835, in _get_handles
OSError: [Errno 12] Cannot allocate memory
Результат команды free -m на сервере после сбоя сценария:
$ free -m
total used free shared buffers cached
Mem: 894 345 549 0 0 0
-/+ buffers/cache: 345 549
Swap: 0 0 0
На сервере работает CentOS 5.3. Я не могу воспроизвести на своих ящиках CentOS или с другими пользователями, сообщающими о той же проблеме.
Я пробовал несколько вещей, чтобы отладить это, как было предложено в исходном вопросе:
Регистрация вывода команды free -m до и после вызова Popen. Нет значительных изменений в использовании памяти, т.е. память не используется постепенно по мере выполнения сценария.
Я добавил close_fds = True к вызову Popen, но это не имело значения - скрипт по-прежнему вылетал с той же ошибкой. Предлагается здесь и здесь .
Я проверил rlimits, которые показали (-1, -1) как для RLIMIT_DATA, так и для RLIMIT_AS, как предлагается здесь .
Статья предложила, не имеющее пространство подкачки не может быть причиной , но замена на самом деле предоставляется по запросу ( в зависимости от веба - хостинга) , и это также было предложено в качестве поддельных привести здесь .
Процессы закрываются, потому что это поведение использования .communicate (), которое поддерживается исходным кодом Python и комментариями здесь .
Все проверки можно найти на GitHub здесь с функцией getProcesses, определенной в строке 442. Она вызывается doChecks (), начиная со строки 520.
Скрипт был запущен с помощью strace до сбоя со следующим выводом:
recv(4, "Total Accesses: 516662\nTotal kBy"..., 234, 0) = 234
gettimeofday({1250893252, 887805}, NULL) = 0
write(3, "2009-08-21 17:20:52,887 - checks"..., 91) = 91
gettimeofday({1250893252, 888362}, NULL) = 0
write(3, "2009-08-21 17:20:52,888 - checks"..., 74) = 74
gettimeofday({1250893252, 888897}, NULL) = 0
write(3, "2009-08-21 17:20:52,888 - checks"..., 67) = 67
gettimeofday({1250893252, 889184}, NULL) = 0
write(3, "2009-08-21 17:20:52,889 - checks"..., 81) = 81
close(4) = 0
gettimeofday({1250893252, 889591}, NULL) = 0
write(3, "2009-08-21 17:20:52,889 - checks"..., 63) = 63
pipe([4, 5]) = 0
pipe([6, 7]) = 0
fcntl64(7, F_GETFD) = 0
fcntl64(7, F_SETFD, FD_CLOEXEC) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb7f12708) = -1 ENOMEM (Cannot allocate memory)
write(2, "Traceback (most recent call last"..., 35) = 35
open("/usr/bin/sd-agent/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/bin/sd-agent/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python24.zip/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/plat-linux2/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python2.4/lib-tk/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/lib-dynload/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/site-packages/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
write(2, " File \"/usr/bin/sd-agent/agent."..., 52) = 52
open("/home/admin/sd-agent/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/bin/sd-agent/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python24.zip/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/plat-linux2/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python2.4/lib-tk/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/lib-dynload/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/site-packages/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
write(2, " File \"/home/admin/sd-agent/dae"..., 60) = 60
open("/usr/bin/sd-agent/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/bin/sd-agent/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python24.zip/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/plat-linux2/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python2.4/lib-tk/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/lib-dynload/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/site-packages/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
write(2, " File \"/usr/bin/sd-agent/agent."..., 54) = 54
open("/usr/lib/python2.4/sched.py", O_RDONLY|O_LARGEFILE) = 8
write(2, " File \"/usr/lib/python2.4/sched"..., 55) = 55
fstat64(8, {st_mode=S_IFREG|0644, st_size=4054, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7d28000
read(8, "\"\"\"A generally useful event sche"..., 4096) = 4054
write(2, " ", 4) = 4
write(2, "void = action(*argument)\n", 25) = 25
close(8) = 0
munmap(0xb7d28000, 4096) = 0
open("/usr/bin/sd-agent/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/bin/sd-agent/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python24.zip/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/plat-linux2/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python2.4/lib-tk/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/lib-dynload/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/site-packages/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
write(2, " File \"/usr/bin/sd-agent/checks"..., 60) = 60
open("/usr/bin/sd-agent/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/bin/sd-agent/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python24.zip/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/plat-linux2/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python2.4/lib-tk/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/lib-dynload/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/site-packages/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
write(2, " File \"/usr/bin/sd-agent/checks"..., 64) = 64
open("/usr/lib/python2.4/subprocess.py", O_RDONLY|O_LARGEFILE) = 8
write(2, " File \"/usr/lib/python2.4/subpr"..., 65) = 65
fstat64(8, {st_mode=S_IFREG|0644, st_size=39931, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7d28000
read(8, "# subprocess - Subprocesses with"..., 4096) = 4096
read(8, "lso, the newlines attribute of t"..., 4096) = 4096
read(8, "code < 0:\n print >>sys.st"..., 4096) = 4096
read(8, "alse does not exist on 2.2.0\ntry"..., 4096) = 4096
read(8, " p2cread\n # c2pread <-"..., 4096) = 4096
write(2, " ", 4) = 4
write(2, "errread, errwrite)\n", 19) = 19
close(8) = 0
munmap(0xb7d28000, 4096) = 0
open("/usr/lib/python2.4/subprocess.py", O_RDONLY|O_LARGEFILE) = 8
write(2, " File \"/usr/lib/python2.4/subpr"..., 71) = 71
fstat64(8, {st_mode=S_IFREG|0644, st_size=39931, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7d28000
read(8, "# subprocess - Subprocesses with"..., 4096) = 4096
read(8, "lso, the newlines attribute of t"..., 4096) = 4096
read(8, "code < 0:\n print >>sys.st"..., 4096) = 4096
read(8, "alse does not exist on 2.2.0\ntry"..., 4096) = 4096
read(8, " p2cread\n # c2pread <-"..., 4096) = 4096
read(8, "table(self, handle):\n "..., 4096) = 4096
read(8, "rrno using _sys_errlist (or siml"..., 4096) = 4096
read(8, " p2cwrite = None, None\n "..., 4096) = 4096
write(2, " ", 4) = 4
write(2, "self.pid = os.fork()\n", 21) = 21
close(8) = 0
munmap(0xb7d28000, 4096) = 0
write(2, "OSError", 7) = 7
write(2, ": ", 2) = 2
write(2, "[Errno 12] Cannot allocate memor"..., 33) = 33
write(2, "\n", 1) = 1
unlink("/var/run/sd-agent.pid") = 0
close(3) = 0
munmap(0xb7e0d000, 4096) = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x589978}, {0xb89a60, [], SA_RESTORER, 0x589978}, 8) = 0
brk(0xa022000) = 0xa022000
exit_group(1) = ?
/var/log/messages
, илиdmesg
командовать.Ответы:
По общему правилу (т.е. в ваниль семечек),
fork
/clone
сбои сENOMEM
происходят конкретно из - либо честный к Богу вне памяти состояния (dup_mm
,dup_task_struct
,alloc_pid
,mpol_dup
, иmm_init
т.д. каркать), или потому , чтоsecurity_vm_enough_memory_mm
не удалось вам , а исполнение в overcommit политику .Начните с проверки vmsize процесса, который не удалось выполнить вилку, во время попытки вилки, а затем сравните ее с объемом свободной памяти (физической и подкачки), поскольку он связан с политикой переопределения (введите числа).
В вашем конкретном случае обратите внимание, что ПК-Р-Виртуализация имеет дополнительные проверки при принудительном превышении лимита . Более того, я не уверен, насколько у вас действительно есть контроль изнутри вашего контейнера над конфигурацией swap и overcommit (для того, чтобы повлиять на результат принудительного исполнения).
Теперь, чтобы действительно двигаться вперед, я бы сказал, что у вас осталось два варианта :
ОБРАТИТЕ ВНИМАНИЕ, что усилия по кодированию могут оказаться напрасными, если окажется, что это не вы, а какой-то другой парень, размещенный в другом экземпляре на том же сервере, что и вы, запущенный из-под контроля.
Что
subprocess.Popen
касаетсяfork
clone
памяти, мы уже знаем, что он использует / под капотом , что означает, что каждый раз, когда вы вызываете его, вы запрашиваете еще раз столько памяти, сколько Python уже съедает , то есть в сотнях дополнительных МБ, и все для того, чтобы затемexec
крошечный исполняемый файл размером 10 КБ, такой какfree
илиps
. В случае невыгодной политики чрезмерных ограничений вы скоро увидитеENOMEM
.Альтернативы тому,
fork
что не имеют этой таблицы родительских страниц и т. Д., Проблема копирования естьvfork
иposix_spawn
. Но если вам не хочется переписывать фрагментыsubprocess.Popen
в терминахvfork
/posix_spawn
, рассмотрите возможность использованияsuprocess.Popen
только один раз, в начале вашего скрипта (когда объем памяти Python минимален), чтобы создать скрипт оболочки, который затем запускаетfree
/ps
/sleep
и все остальное в цикл параллельно вашему скрипту; опрашивать вывод скрипта или читать его синхронно, возможно, из отдельного потока, если у вас есть другие вещи, которые нужно позаботиться асинхронно - обработайте ваши данные в Python, но оставьте разветвление подчиненному процессу.ОДНАКО , в вашем конкретном случае вы можете пропустить вызов
ps
иfree
вообще; эта информация легко доступна вам в Python напрямуюprocfs
, независимо от того, хотите ли вы получить к ней доступ самостоятельно или через существующие библиотеки и / или пакеты . Если быps
иfree
были единственными утилитами, которые вы использовали, то вы можете полностью отказаться от нихsubprocess.Popen
.Наконец, что бы вы ни делали
subprocess.Popen
, если ваш скрипт утекает память, вы все равно в конечном итоге столкнетесь с проблемой. Следите за ним и проверяйте наличие утечек памяти .источник
gc.collect()
непосредственно перед запускомsubprocess.Popen
помогает в тех случаях, когда сборщик мусора некоторое время не запускался./proc/fd/maps
чтобы определить, действительно ли проблемаГлядя на результат,
free -m
мне кажется, что на самом деле у вас нет доступной памяти подкачки. Я не уверен, что в Linux своп всегда будет доступен автоматически по запросу, но у меня была та же проблема, и ни один из ответов здесь мне не помог. Однако добавление некоторой памяти подкачки устранило проблему в моем случае, поэтому, поскольку это может помочь другим людям, столкнувшимся с той же проблемой, я публикую свой ответ о том, как добавить подкачку 1 ГБ (в Ubuntu 12.04, но он должен работать аналогично для других дистрибутивов).Сначала вы можете проверить, включена ли какая-либо память подкачки.
если он пуст, это означает, что у вас не включен обмен. Чтобы добавить своп размером 1 ГБ:
Добавьте следующую строку в,
fstab
чтобы сделать обмен постоянным.Источник и дополнительную информацию можно найти здесь .
источник
своп не может быть отвлекающим маневром, предложенным ранее. Насколько велик рассматриваемый процесс python непосредственно перед
ENOMEM
?В ядре 2.6
/proc/sys/vm/swappiness
контролирует, насколько активно ядро будет переключаться на подкачку, иovercommit*
сколько файлов и насколько точно ядро может распределять память, подмигивая и кивая. Как и ваш статус отношений в Facebook, это сложно .но не в соответствии с выводом вашей
free(1)
команды, который показывает, что пространство подкачки не распознается вашим экземпляром сервера. Ваш веб-хост, безусловно, может знать об этой теме гораздо больше, чем я, но виртуальные системы RHEL / CentOS, которые я использовал, сообщали о подкачке, доступной для гостевой ОС.Адаптация статьи 15252 Red Hat KB :
Сравните ваши
/proc/sys/vm
настройки с простой установкой CentOS 5.3. Добавьте файл подкачки. Попробуйswappiness
и посмотри, живешь ли ты дольше.источник
ps -o user,pid,vsz="Mem(Kb)" -o cmd $PYTHON_PID
, или top (1), должно это сделать.Для простого исправления вы можете
если вы уверены, что в вашей системе достаточно памяти. См. Linux по эвристике фиксации .
источник
Я продолжаю подозревать, что у вашего клиента / пользователя загружен какой-то модуль ядра или драйвер, который мешает
clone()
системному вызову (возможно, какое-то неясное улучшение безопасности, что-то вроде LIDS, но более неясное?) Или каким-то образом заполняет некоторые структуры данных ядра, которые необходимы дляfork()
/clone()
для работы (таблица процессов, таблицы страниц, таблицы дескрипторов файлов и т. д.).Вот соответствующая часть
fork(2)
справочной страницы:Я предлагаю пользователю попробовать это после загрузки стандартного общего ядра и с минимальным набором модулей и загруженных драйверов (минимально необходимым для запуска вашего приложения / скрипта). Оттуда, если он работает в этой конфигурации, они могут выполнить бинарный поиск между этой конфигурацией и конфигурацией, в которой обнаружена проблема. Это стандартное решение 101 для устранения неполадок системного администратора.
Соответствующая строка в вашем
strace
:... Я знаю, что другие говорили о подкачке и доступности памяти (и я бы порекомендовал вам создать хотя бы небольшой раздел подкачки, по иронии судьбы, даже если он находится на RAM-диске ... пути кода через ядро Linux, когда оно даже крошечный бит доступного свопа использовался гораздо шире, чем те (пути обработки исключений), в которых нет доступного свопа.
Однако я подозреваю, что это все еще отвлекающий маневр.
Тот факт, что
free
кэш и буферы использует 0 (НОЛЬ) памяти, очень тревожат. Я подозреваю, чтоfree
вывод ... и, возможно, проблема с вашим приложением здесь вызваны каким-то проприетарным модулем ядра, который каким-то образом мешает распределению памяти.Согласно страницам руководства для fork () / clone () системный вызов fork () должен возвращать EAGAIN, если ваш вызов вызовет нарушение ограничения ресурсов (RLIMIT_NPROC) ... однако он не говорит, должен ли возвращаться EAGAIN другими нарушениями RLIMIT *. В любом случае, если ваша цель / хост имеет какие-то странные параметры Vormetric или другие параметры безопасности (или даже если ваш процесс работает под какой-то странной политикой SELinux), это может вызвать сбой -ENOMEM.
Маловероятно, что это будет обычная проблема Linux / UNIX. У вас там происходит что-то нестандартное.
источник
Вы пробовали использовать:
Я думал, что это решило для меня ту же проблему. Но потом мой процесс закончился тем, что вместо того, чтобы не появиться, мой процесс был убит, что еще хуже ...
После некоторого тестирования я обнаружил, что это происходило только в более старых версиях python: это происходит с 2.6.5, но не с 2.7.2.
Мой поиск привел меня сюда python-close_fds-issue , но отключение closed_fds не решило проблему. Это все еще стоит прочитать.
Я обнаружил, что в Python произошла утечка файловых дескрипторов, просто следя за ним:
Как и вы, я хочу зафиксировать вывод команды, и я хочу избежать ошибок OOM ... но похоже, что единственный способ для людей - использовать менее глючную версию Python. Не идеально ...
источник
Я видел неряшливый код, который выглядит так:
Вы должны проверить, происходит ли это в коде Python. Errno допустимо только в случае сбоя системного вызова.
Отредактировано для добавления:
Вы не говорите, сколько длится этот процесс. Возможные потребители памяти
источник
errno
сбрасывается много раз в процессе.Может ты просто можешь
В моем случае это работает.
Ссылка: https://github.com/openai/gym/issues/110#issuecomment-220672405
источник