Сообщить об ошибке.

Функция fork() модуля os в Python.

Создание дочернего процесса.

Синтаксис:

import os

os.fork()

Параметры:

  • Нет

Описание:

Функция fork() модуля os создает клон текущего процесса как дочерний процесс. Возвращает 0 в дочернем процессе и идентификатор дочернего процесса в родительском элементе. Если возникает ошибка, то возникает исключение OSError.

Обратите внимание, что некоторые платформы, включая FreeBSD <= 6.3 и Cygwin, имеют известные проблемы при использовании os.fork() из потока.

Вызывает событие аудита os.fork без аргументов.

Много литературы написано о надежном использовании системных вызовов fork() и exec() в Mac OS X, Linux и других вариантах Unix, поэтому советуем почитать немного дополнительной литературы.

Примеры использования создания дочерних процессов:

После разветвления, два процесса выполняют один и тот же код. Чтобы программа узнала, в каком она находится, она должна проверить возвращаемое значение os.fork(). Если значение равно 0, текущий процесс является дочерним. Если это не 0, программа выполняется в родительском процессе, а возвращаемое значение является идентификатором процесса дочернего процесса.

# test_fork.py
import os

pid = os.fork()

if pid:
    print('Child process id:', pid)
else:
    print('I am the child')

# $ python3 test_fork.py
# Child process id: 20131
# I am the child

Родитель может отправлять сигналы дочернему процессу, используя функцию os.kill() используя модуль signal. В этом примере используется короткая пауза, чтобы дать дочернему процессу время для настройки обработчика сигнала. Реальное приложение, не будет нуждаться в вызове time.sleep(). В дочернем процессе установим обработчик сигнала и сделаем небольшую паузу, чтобы предоставить родительскому процессу достаточно времени для отправки сигнала.

# test_kill.py
import os
import signal
import time

def signal_usr1(signum, frame):
    "Обратный вызов вызывается при получении сигнала"
    pid = os.getpid()
    print(f'Получен USR1 в процессе {pid}')

print('Forking...')
child_pid = os.fork()
if child_pid:
    print('PARENT: Пауза перед отправкой сигнала ...')
    time.sleep(1)
    print(f'PARENT: передача сигналов {child_pid}')
    os.kill(child_pid, signal.SIGUSR1)
else:
    print('CHILD: Настройка обработчика сигнала')
    signal.signal(signal.SIGUSR1, signal_usr1)
    print('CHILD: Пауза в ожидании сигнала')
    time.sleep(5)

# $ python3 test_kill.py
# Forking...
# PARENT: Пауза перед отправкой сигнала ...
# CHILD: Настройка обработчика сигнала
# CHILD: Пауза в ожидании сигнала
# PARENT: передача сигналов 21168
# Получен USR1 в процессе 21168

Простой способ обработать отдельное поведение в дочернем процессе - проверить возвращаемое значение os.fork() и branch. Более сложное поведение может потребовать большего разделения кода, чем простая ветвь. В других случаях может существовать программа, которую необходимо обернуть. Для обеих этих ситуаций можно использовать серию функций os.exec*() для запуска другой программы.

# test_exec.py
import os

child_pid = os.fork()
if child_pid:
    os.waitpid(child_pid, 0)
else:
    os.execlp('pwd', 'pwd', '-P')

# $ python3 test_exec.py
# /home/docs-python

Когда программа запускается функцией os.exec(), код из этой программы заменяет код из существующего процесса.

Примеры использования ожидания дочерних процессов:

Много ресурсоемких программ, используется несколько процессов, чтобы обойти ограничения многопоточности в Python и глобальную блокировку интерпретатора GIL. При запуске нескольких процессов для выполнения отдельных задач мастеру нужно будет дождаться завершения одного или нескольких из них, прежде чем запускать новые, чтобы избежать перегрузки сервера. Существует несколько различных способов сделать это с помощью функции os.wait() и связанных с ней функций.

Когда не имеет значения, какой дочерний процесс может завершиться первым, используйте os.wait(). Он возвращается, как только завершается любой дочерний процесс.

# test_wait.py
import os
import sys
import time

for i in range(2):
    print(f'PARENT {os.getpid()}: Forking {i}')
    worker_pid = os.fork()
    if not worker_pid:
        print(f'WORKER {i}: Starting')
        time.sleep(2 + i)
        print(f'WORKER {i}: Finishing')
        sys.exit(i)

for i in range(2):
    print(f'PARENT: Waiting for {i}')
    done = os.wait()
    print(f'PARENT: Child done: {done}')

# $ python3 test_wait.py 
# PARENT 24512: Forking 0
# PARENT 24512: Forking 1
# WORKER 0: Starting
# PARENT: Waiting for 0
# WORKER 1: Starting
# WORKER 0: Finishing
# PARENT: Child done: (24513, 0)
# PARENT: Waiting for 1
# WORKER 1: Finishing
# PARENT: Child done: (24514, 256)

Функция os.wait() возвращает идентификатор процесса и код завершения, упакованный в 16-битовое значение. Младший байт представляет номер сигнала, прекратившего выполнение процесса, а старший - код состояния, возвращенный процессом по его завершении.

Чтобы дождаться определенного процесса, используйте функцию os.waitpid().

Функция os.waitpid(), которой передан идентификатор целевого процесса, будет блокироваться до тех пор, пока процесс не завершится.

# test_waitpid.py
import os
import sys
import time

workers = []
for i in range(2):
    print(f'PARENT {os.getpid()}: Forking {i}')
    worker_pid = os.fork()
    if not worker_pid:
        print(f'WORKER {i}: Starting')
        time.sleep(2 + i)
        print(f'WORKER {i}: Finishing')
        sys.exit(i)
    workers.append(worker_pid)

for pid in workers:
    print(f'PARENT: Waiting for {pid}')
    done = os.waitpid(pid, 0)
    print(f'PARENT: Child done: {done}')

# $ python3 test_waitpid.py 
# PARENT 25144: Forking 0
# PARENT 25144: Forking 1
# WORKER 0: Starting
# PARENT: Waiting for 25145
# WORKER 1: Starting
# WORKER 0: Finishing
# PARENT: Child done: (25145, 0)
# PARENT: Waiting for 25146
# WORKER 1: Finishing
# PARENT: Child done: (25146, 256)