Понимание ошибок вилки Python и памяти

У меня есть приложение Python с интенсивной памятью (от сотни МБ до нескольких ГБ).
У меня есть несколько ОЧЕНЬ МАЛЕНЬКИХ Linux-исполняемых файлов, которые нужно запустить главному приложению, например.

child = Popen("make html", cwd = r'../../docs', stdout = PIPE, shell = True)
child.wait()

Когда я запускаю эти внешние утилиты (один раз, в конце длинного основного процесса), используя subprocess.Popen, я иногда получаю OSError: [Errno 12] Cannot allocate memory.
Я не понимаю, почему... Запрошенный процесс крошечный!
В системе достаточно памяти для многих других оболочек.

Я использую Linux (Ubuntu 12.10, 64 бит), поэтому я думаю, что подпроцесс вызывает Fork.
И Fork разворачивает мой существующий процесс, тем самым удваивая количество потребляемой памяти и терпя неудачу?
Что случилось с копией на запись?

Можно ли создать новый процесс без вилки (или, по крайней мере, без копирования памяти - начиная с нуля)?

по теме:

Разница между fork(), vfork(), exec() и clone()

fork() и поведение распределения памяти

Подпроцесс Python.Popen с помощью OSError: [Errno 12] Невозможно выделить память через период времени

Ошибка выделения памяти Python с помощью подпроцесса. Открытие

Ответ 1

Не похоже, что появится реальное решение (т.е. альтернативная реализация подпроцесса, использующего vfork). Так как насчет симпатичного взлома? В начале вашего процесса создайте ведомое устройство, которое висит вокруг с небольшим размером памяти, готовым к появлению ваших подпроцессов и поддерживая открытую связь с ним на протяжении всего жизненного цикла основного процесса.

Вот пример использования rfoo (http://code.google.com/p/rfoo/) с именованным сокетом unix, называемым rfoosocket (вы, очевидно, могли бы использовать другие типы соединений, поддерживающие rfoo, или другая библиотека RPC):

Сервер:

import rfoo
import subprocess

class MyHandler(rfoo.BaseHandler):
    def RPopen(self, cmd):
        c = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
        c.wait()
        return c.stdout.read()

rfoo.UnixServer(MyHandler).start('rfoosocket')

Клиент:

import rfoo

# Waste a bunch of memory before spawning the child. Swap out the RPC below
# for a straight popen to show it otherwise fails. Tweak to suit your
# available system memory.
mem = [x for x in range(100000000)]

c = rfoo.UnixConnection().connect('rfoosocket')

print rfoo.Proxy(c).RPopen('ls -l')

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

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