Как организовать взаимодействие между процессами в Unix
Что проверяют
Знаете ли вы механизмы IPC и умеете ли подобрать их под задачу, а не просто перечислить. Для Python-разработчика следом идёт практика: как это устроено в multiprocessing и subprocess и зачем процессы вместо потоков.
Как отвечать
Механизмы с назначением:
- pipe: однонаправленный байтовый поток между родственными процессами,
os.pipe()илиsubprocess.Popen(stdout=subprocess.PIPE), то же, что|в шелле; - FIFO:
os.mkfifo(), именованный канал в файловой системе, через него общаются и неродственные процессы; - сигналы:
signal.signal(signal.SIGTERM, handler), уведомление без данных. Так сервер узнаёт, что пора завершиться; - Unix-сокеты:
socket.AF_UNIX, двунаправленные и быстрее TCP на одной машине. Через них nginx ходит в gunicorn, а клиенты в PostgreSQL и Docker; - разделяемая память:
mmapилиmultiprocessing.shared_memory, самый быстрый путь без копирования, но синхронизация целиком ваша; - очереди сообщений POSIX и System V: в стандартной библиотеке их нет, нужны сторонние пакеты;
- файл и блокировка:
fcntl.flock(), простой способ не запустить два экземпляра задачи по крону.
В multiprocessing поверх этого есть Queue, Pipe и Manager. Объекты передаются через pickle, и на больших данных сериализация становится узким местом. Процессы нужны из-за GIL: CPU-нагрузку потоки обычного CPython не распараллеливают. Способ старта тоже стоит знать: в Python 3.14 на Linux по умолчанию forkserver вместо fork, на macOS давно spawn.
Между машинами это уже не IPC, а сеть: HTTP, gRPC, брокеры вроде RabbitMQ или Redis.
Где спотыкаются
Не закрывают ненужный конец pipe. Читатель не получит EOF, пока открыт хотя бы один пишущий дескриптор, и висит вечно.
Вызывают Popen.wait() при stdout=PIPE. Буфер канала на Linux 64 КБ: дочерний процесс блокируется на записи, родитель ждёт его завершения, и оба стоят. Нужен communicate().
Работают с shared_memory и забывают unlink(), сегмент остаётся в /dev/shm после выхода. И пишут в общую память из нескольких процессов без Lock.