Как избежать проблем с памятью fork() при запуске внешних команд из большого процесса PythonPython

Программы на Python
Ответить
Anonymous
 Как избежать проблем с памятью fork() при запуске внешних команд из большого процесса Python

Сообщение Anonymous »

Я поддерживаю приложение Python, интенсивно использующее память, которому иногда необходимо выполнять внешние команды (например, ls -l) и захватывать их выходные данные. В устаревшем коде для этой цели используется subprocess.Popen. Однако я заметил, что иногда выполнение команды завершается неудачей, и я подозреваю, что это связано с тем, что Popen использует fork() под капотом в системах Linux.
Поскольку fork() дублирует пространство памяти родительского процесса. Когда родительский процесс велик, я подозреваю, что это может привести к сбоям при выделении памяти, даже если дочернему процессу не нужна вся эта память.
Чтобы решить эту проблему, я исследовал, как выполнять внешние команды. без дублирования всей памяти родительского процесса, сохраняя при этом вывод команды. Я придумал два альтернативных метода, которые используют одну и ту же сигнатуру функции run_command и используют одну и ту же функцию main для последовательного сравнения.
Метод 1: Использование многопроцессорной обработки с «spawn»

Код: Выделить всё

import multiprocessing

def _internal_run_command(cmd):
import subprocess
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, text=True)
output, _ = process.communicate()
return output

def run_command(cmd):
multiprocessing.set_start_method("spawn", force=True)
with multiprocessing.Pool(1) as pool:
result = pool.apply(_internal_run_command, (cmd,))
return result
Метод 2: использование os.posix_spawnp()

Код: Выделить всё

import os

def run_command(cmd):
r, w = os.pipe()
try:
pid = os.posix_spawnp(
cmd[0],            # File to execute
cmd,               # argv
os.environ.copy(), # env
file_actions=[
(os.POSIX_SPAWN_DUP2, w, 1),  # Redirect stdout to write end of pipe
(os.POSIX_SPAWN_CLOSE, r),    # Close read end in child
],
)
os.close(w)  # Close write end in parent

output = b""
while True:
chunk = os.read(r, 1024)
if not chunk:
break
output += chunk
os.close(r)

os.waitpid(pid, 0)
return output.decode()
except Exception as e:
print(e)
Основная функция (общая для обоих методов):

Код: Выделить всё

if __name__ == "__main__":
output = run_command(["ls", "-l"])
print(output)
Сравнение методов:



Аспект
Метод 1: многопроцессорная обработка с "порождением"
Метод 2: Использование os.posix_spawnp()




Кроссплатформенная совместимость
Да (работает в Linux, macOS, Windows)
Нет (только POSIX: Linux, macOS)


Сложность реализации
Использует модули высокого уровня (

Код: Выделить всё

multiprocessing
, подпроцесс); меньше ручного кода
Требуется ручное управление каналами и файловыми дескрипторами


Производительность
Затраты на запуск нового интерпретатора Python
Эффективное прямое выполнение без дополнительных затрат
< /tr>


Мои опасения:
Это довольно низкоуровневый инфраструктурный код, который реализуется вручную. и может привести к неожиданным проблемам. Меня беспокоит то, что, вручную обрабатывая процессы, каналы и файловые дескрипторы, мы заново изобретаем велосипед для решения проблемы, с которой, по-видимому, мы сталкиваемся не первыми.
Мои вопросы:
  • Есть ли лучший способ выполнения внешних команд из устройства с интенсивным использованием памяти Процесс Python в Linux, который позволяет избежать fork() и позволяет мне захватывать вывод?
    • Например, есть ли способ использовать подпроцесс или другой модуль стандартной библиотеки, который не использует fork ()?
  • Есть ли еще плюсы и минусы, которые мне следует учитывать между эти два метода?
    • Если нет лучших альтернатив, я бы хотелось бы узнать больше о возможных компромиссах.
Дополнительно Информация:
  • В настоящее время мы работаем в системах Linux, поэтому приемлемы решения только для POSIX.
    < li>Основная цель — избежать сбоев при выделении памяти из-за того, что fork() дублирует большое пространство памяти родительского элемента. процесс.
  • Захват вывода внешней команды важен для функциональности нашего приложения.
Отказ от ответственности:
Я пытался выяснить, противоречит ли это правилам, читая мета-вопросы, но не был уверен, поэтому добавляю этот раздел явно.
  • Я использовал ChatGPT для создания альтернатив (но проверял их перед публикацией и это обе альтернативы).
  • Я использовал ChatGPT, чтобы формализовать вопрос, но все детали стали точными после многих итераций результатов.


Подробнее здесь: https://stackoverflow.com/questions/793 ... rge-python
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»