Подпроцесс Python. Открытое сообщение об ошибке с OSError: [Errno 12] Не удается выделить память по истечении определенного периода времени - PullRequest
9 голосов
/ 01 августа 2009

Примечание : этот вопрос был повторен с кратким изложением всех попыток отладки здесь .


У меня есть сценарий Python, который выполняется как фоновый процесс, выполняющийся каждые 60 секунд. Частично это является вызовом subprocess.Popen , чтобы получить вывод ps .

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 на сервере:

$ free -m
                  total       used       free     shared     buffers    cached
Mem:                894        345        549          0          0          0
-/+ buffers/cache:  345        549
Swap:                 0          0          0

Я искал проблему и нашел эту статью , в которой говорится:

Решение состоит в том, чтобы добавить больше пространства подкачки на ваш сервер. Когда ядро ​​пытается запустить моделлер или процесс обнаружения, оно сначала обеспечивает достаточно места в хранилище подкачки для нового процесса, если это необходимо.

Хочу заметить, что в свободном выводе нет доступных свопов. Это может быть проблемой и / или какими могут быть другие решения?

Обновление 13 августа 09 Приведенный выше код вызывается каждые 60 секунд в рамках серии функций мониторинга. Процесс демонизируется, и проверка запланирована с использованием sched . Конкретный код для вышеуказанной функции:

def getProcesses(self):
    self.checksLogger.debug('getProcesses: start')

    # Memory logging (case 27152)
    if self.agentConfig['debugMode'] and sys.platform == 'linux2':
        mem = subprocess.Popen(['free', '-m'], stdout=subprocess.PIPE).communicate()[0]
        self.checksLogger.debug('getProcesses: memory before Popen - ' + str(mem))

    # Get output from ps
    try:
        self.checksLogger.debug('getProcesses: attempting Popen')

        ps = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE).communicate()[0]

    except Exception, e:
        import traceback
        self.checksLogger.error('getProcesses: exception = ' + traceback.format_exc())
        return False

    self.checksLogger.debug('getProcesses: Popen success, parsing')

    # Memory logging (case 27152)
    if self.agentConfig['debugMode'] and sys.platform == 'linux2':
        mem = subprocess.Popen(['free', '-m'], stdout=subprocess.PIPE).communicate()[0]
        self.checksLogger.debug('getProcesses: memory after Popen - ' + str(mem))

    # Split out each process
    processLines = ps.split('\n')

    del processLines[0] # Removes the headers
    processLines.pop() # Removes a trailing empty line

    processes = []

    self.checksLogger.debug('getProcesses: Popen success, parsing, looping')

    for line in processLines:
        line = line.split(None, 10)
        processes.append(line)

    self.checksLogger.debug('getProcesses: completed, returning')

    return processes

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

Весь класс проверок можно найти в http://github.com/dmytton/sd-agent/blob/82f5ff9203e54d2adeee8cfed704d09e3f00e8eb/checks.py с функцией getProcesses, определенной в строке 442. Это вызывается doChecks (), начиная со строки 520.

Ответы [ 9 ]

5 голосов
/ 13 августа 2009

Возможно, вы получили утечку памяти, ограниченную некоторым ограничением ресурса (RLIMIT_DATA, RLIMIT_AS?), Унаследованным вашим скриптом Python. Проверьте свои * ulimit (1) * перед запуском сценария и настройте профиль использования памяти сценарием, как предлагали другие.

Что вы делаете с переменной ps после фрагмента кода, который вы нам показываете? Сохраняете ли вы ссылку на нее, чтобы никогда не освобождаться? Цитирование subprocess модуля документации :

Примечание: Считанные данные буферизируются в памяти, поэтому не используйте это метод, если размер данных большой или неограниченный.

... и ps aux могут быть многословными в занятой системе ...

Обновление

Вы можете проверить rlimits с помощью своего скрипта Python, используя модуль resource :

import resource
print resource.getrlimit(resource.RLIMIT_DATA) # => (soft_lim, hard_lim)
print resource.getrlimit(resource.RLIMIT_AS)

Если они возвращают «неограниченное» - (-1, -1) - тогда моя гипотеза неверна, и вы можете двигаться дальше!

См. Также resource.getrusage, особенно поля ru_??rss, которые могут помочь вам использовать инструмент для потребления памяти с помощью скрипта Python, не передавая внешнюю программу.

3 голосов
/ 19 августа 2009

когда вы используете popen, вам нужно передать close_fds = True, если вы хотите, чтобы он закрывал дополнительные файловые дескрипторы.

Создание нового канала, которое происходит в функции _get_handles из обратной трассировки, создает 2 файловых дескриптора, но ваш текущий код никогда не закрывает их, и вы в конечном итоге достигаете максимального предела fd вашей системы.

Не уверен, почему полученная ошибка указывает на нехватку памяти: это должна быть ошибка дескриптора файла, поскольку возвращаемое значение pipe() имеет код ошибки для этой проблемы.

3 голосов
/ 13 августа 2009

Этот ответ подкачки является поддельным. Исторически системы Unix хотели, чтобы доступное пространство подкачки было таким, но они больше не работают таким образом (и Linux никогда не работал таким образом). У вас даже близко нет недостатка памяти, поэтому проблема вряд ли в этом - у вас заканчивается какой-то другой ограниченный ресурс.

Учитывая, где происходит ошибка (_get_handles вызывает os.pipe () для создания каналов дочернему элементу), единственная реальная проблема, с которой вы можете столкнуться, - это нехватка свободных файловых дескрипторов. Вместо этого я бы искал незакрытые файлы (lsof -p для PID процесса, выполняющего popen). Если вашей программе действительно нужно держать много файлов открытыми одновременно, увеличьте пользовательский лимит и / или системный лимит для дескрипторов открытых файлов.

2 голосов
/ 14 августа 2009

Если вы запускаете фоновый процесс, есть вероятность, что вы перенаправили ваши процессы stdin / stdout / stderr.

В этом случае добавьте параметр «close_fds = True» к вашему вызову Popen, который не позволит дочернему процессу наследовать ваш перенаправленный вывод. Это может быть предел, в который вы врезаетесь.

1 голос
/ 01 августа 2009

Возможно, вы захотите дождаться завершения всех этих процессов PS, прежде чем добавлять пространство подкачки.

Не совсем понятно, что означает «запуск в качестве фонового процесса, выполняемого каждые 60 секунд».

Но ваш вызов подпроцесса. Каждый раз при открытии запускается новый процесс.

Обновление .

Я предполагаю, что вы как-то оставляете все эти процессы запущенными или зависшими в состоянии зомби. Однако метод communicate должен очистить порожденные подпроцессы.

0 голосов
/ 02 марта 2012

Виртуальная память имеет значение !!!

Я столкнулся с той же проблемой, прежде чем добавить своп в мою ОС. Формула для виртуальной памяти обычно имеет вид: SwapSize + 50% * PhysicalMemorySize. Я наконец-то решил эту проблему, либо добавив больше физической памяти, либо добавив диск подкачки. close_fds не будет работать в моем случае.

0 голосов
/ 13 августа 2009

Вам нужно

ps = subprocess.Popen(["sleep", "1000"])
os.waitpid(ps.pid, 0)

на свободные ресурсы.

Примечание: это не работает в Windows.

0 голосов
/ 13 августа 2009

Вы наблюдали за процессом с течением времени?

  • lsof
  • ps -aux | grep -i pname
  • сверху

Все должны дать интересную информацию. Я думаю, что процесс связывает ресурсы, которые должны быть освобождены. Есть ли вероятность, что он связывает дескрипторы ресурсов (блоки памяти, потоки, файловые дескрипторы, потоки или дескрипторы процессов)? stdin, stdout, stderr из порожденных "пс". Память обрабатывает, ... от множества небольших добавочных выделений. Мне было бы очень интересно посмотреть, что показывают вышеупомянутые команды для вашего процесса, когда он только что закончил запуск и запуск в первый раз и после 24 часов «сидения» там, регулярно запуская подпроцесс.

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

Jacob

0 голосов
/ 01 августа 2009

Я не думаю, что обстоятельства, приведенные в статье Zenoss, на которую вы ссылаетесь, являются единственной причиной этого сообщения, поэтому пока не ясно, является ли проблема с пространством подкачки. Я бы посоветовал регистрировать некоторую дополнительную информацию даже об успешных вызовах, чтобы вы могли видеть состояние свободной памяти каждый раз перед выполнением вызова ps.

Еще одна вещь - если вы укажете shell=True в вызове Popen, видите ли вы другое поведение?

Обновление: Если не память, следующий возможный преступник - действительно файловые дескрипторы. Я бы посоветовал запустить сбойную команду под strace, чтобы точно определить, какие системные вызовы не выполняются.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...