Что такое fork процесса
Что проверяют
Модель процессов в Unix и то, чем fork отличается от потока и от запуска свежего интерпретатора. У питониста вопрос почти всегда ведёт к multiprocessing: интервьюер смотрит, откуда, по-вашему, берутся воркеры, почему они иногда зависают намертво и почему рабочий на Linux код ломается на macOS.
Как отвечать
os.fork() создаёт процесс-копию: вызов один, возврата два. Родитель получает идентификатор ребёнка, ребёнок ноль. Память копируется лениво, по copy-on-write, дескрипторы наследуются, выполнение в обоих продолжается со следующей строки.
В прикладном коде голый вызов нужен редко, нужен multiprocessing и его способы старта. Fork копирует уже прогретый интерпретатор и стартует мгновенно. Spawn поднимает чистый интерпретатор и заново импортирует модуль, поэтому требует if __name__ == "__main__": и сериализуемых пиклом аргументов. Forkserver форкает заранее подготовленный чистый процесс, совмещая скорость с безопасностью. На macOS и Windows форк недоступен или небезопасен, поэтому по умолчанию там spawn, а в современных версиях Python форк из многопоточного процесса признан небезопасным.
Закончить стоит мотивом: процессы обходят GIL и дают параллелизм на счётных задачах, платой идут сериализация аргументов и память под каждый воркер.
Где спотыкаются
Считают, что ребёнок получает все потоки родителя. Он получает только вызвавший, а блокировки, захваченные в момент форка другими потоками, остаются захваченными навсегда. Классика это ребёнок, зависший на локе логгера или пула соединений; лечится форком до создания потоков либо обработчиками os.register_at_fork.
Рассчитывают сэкономить память на copy-on-write: загрузить большой словарь до форка и поделить на всех. CPython трогает счётчик ссылок при каждом обращении к объекту, страница становится грязной и всё равно копируется. Частично помогает gc.freeze() перед стартом воркеров.
Наследуют живые ресурсы. Сокет или соединение с базой, открытые до форка, оказываются одним дескриптором на двоих, и протокол рассыпается на первом же параллельном запросе. Соединения открывают в ребёнке, буферы вывода сбрасывают до форка, иначе непустой буфер напечатается дважды.