Почему функция fork () была разработана для возврата файлового дескриптора?


16

На его веб - странице о с трюком собственной трубы , Dan Bernstein объясняет состояние гонки с select()и сигналами, предлагает обходной путь и приходит к выводу , что

Конечно, правильно было бы fork()возвращать дескриптор файла, а не идентификатор процесса.

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


Эта статья смешивает ввод и вывод, или я не читаю это правильно?
— Ctrl-Alt-Delor

Вы можете запросить сигналы для доставки по трубам. Это то, что я делаю.
— ctrl-alt-delor

@ ctrl-alt-delor, да, он, кажется, немного странно использует "конвейерный ввод / вывод", но я думаю, что понятно, где он пишет и где читает из канала. Этот текст написан в 2003 году, и я не уверен, что signalfdи так было тогда?
— ilkkachu

5
Дэн знает, о чем говорит, хотя может быть немного провокационным. Если бы я был намеренно провокационным, я бы сказал, что, конечно, правильно было бы избавиться от SIGCHLD.
— Стив Саммит

1
@mosvy Я немного преувеличиваю, но у каждой программы и каждого программиста, которого я когда-либо видел, кто пытался использовать SIGCHLD, были проблемы с этим. Это состояние гонки, которое должно произойти. Когда все, что у нас было, блокировало wait(), были вещи, которые вы не могли сделать, поэтому кто-то изобрел SIGCHLD, но это была плохая работа. По моему опыту, и теперь, когда они существуют, посыпая красиво, блокирующая wait3(), wait4()и / или waitpid()звонки в ключевых местах (возможно , ваш основной цикл событий) является гораздо лучшей альтернативой.
— Стив Саммит

Ответы:


14

Проблема описана там, в вашем источнике, select()должна прерываться подобными сигналами SIGCHLD, но в некоторых случаях она работает не так хорошо. Таким образом, обходной путь должен иметь запись сигнала в канал, который затем отслеживается select(). Просмотр файловых дескрипторов предназначен select()для решения этой проблемы.

Обходной путь по существу превращает событие сигнала в событие дескриптора файла. Если бы fork()просто вернул fd во-первых, обходной путь не потребовался бы, так как этот fd, вероятно, мог бы затем использоваться непосредственно с select().

Так что да, ваше описание в последнем абзаце мне кажется правильным.


Другая причина того, что fd (или какой-либо другой вид дескриптора ядра) будет лучше, чем простой номер идентификатора процесса, заключается в том, что идентификаторы PID могут быть повторно использованы после смерти процесса. Это может быть проблемой в некоторых случаях при отправке сигналов процессам, возможно, невозможно будет точно знать, что процесс - это то, что вы думаете, а не другой, повторно использующий тот же PID. (Хотя я думаю, что это не должно быть проблемой при отправке сигналов дочернему процессу, так как родитель должен запустить дочерний процесс wait()для освобождения его PID.)


Тем не менее, я не помню точно случаи, когда я читал о том, что повторное использование PID является проблемой, поэтому, если кто-то хочет уточнить или прояснить это, или даже отредактировать вышеупомянутое, не стесняйтесь делать это.
— ilkkachu

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

Это так называемые процессы-зомби : «процесс, который завершил выполнение, но все еще имеет запись в таблице процессов: это процесс в« завершенном состоянии ». Это происходит для дочерних процессов, где запись все еще необходима для разрешения родительского процесса. процесс, чтобы прочитать статус выхода своего ребенка "
— Ласси

6
Стоит отметить, что теперь Linux может возвращать файловый дескриптор (pidfd) clone, который является фактическим системным вызовом, который вызывает fork в LInux. Флаг для включения этого называется CLONE_PIDFD- см. Например lwn.net/Articles/784831 .
— К.Дж. Цанакцидис

1
@Lie Ryan, Re « Наличие fork () возвращает глобальный дескриптор, а не локальный дескриптор, концептуально более правильно, потому что », Windows использует дескрипторы процесса. Код pid и выхода остается неизменным до тех пор, пока все дескрипторы процесса не будут закрыты (вместо того, чтобы ждать, пока родитель получит результат), избегая условий гонки, распространенных в системах Unix. Когда дескрипторы поддерживают процесс в рабочем состоянии, гораздо важнее, чтобы они были локальными, а не глобальными.
— Ikegami

9

Это просто размышления о том, что «было бы здорово, если бы Unix был спроектирован иначе, чем он есть».

Проблема с PID состоит в том, что они живут в глобальном пространстве имен, где их можно повторно использовать для другого процесса, и было бы неплохо, если бы fork()в родительском элементе возвращался какой-то дескриптор, который гарантированно всегда будет ссылаться на дочерний процесс, и что он может передаваться другим процессам через наследование или сокеты unix / SCM_RIGHTS[1].

Смотрите также обсуждение здесь для недавней попытки "исправить" это в Linux, включая добавление флага, clone()который заставит его возвращать pid-fd вместо PID.

Но даже в этом случае это не устранит необходимость в этом хакерстве [2] или более совершенных интерфейсах, поскольку сигналы, уведомляющие родительский процесс о состоянии дочернего элемента, не единственные, которые вы хотели бы обработать в основном цикле. программы. К сожалению, такие вещи, как epoll(7) + signalfd(2)в Linux или kqueue(2)в BSD, не являются стандартными - единственный стандартный интерфейс (но не поддерживается в старых системах) намного хуже pselect(2).

[1] Предотвращение повторного цикла PID к тому времени, когда waitpid()системный вызов возвратился и его возвращаемое значение использовалось, вероятно, может быть достигнуто в более новых системах с помощью waitid(.., WNOWAIT)взамен.

[2] Я бы не стал комментировать заявление ди-джея Бернштейна о том, что он его придумал (извините за апофазис ;-)).


8

Бернштейн не дает большого контекста для этого замечания «Правильное дело», но я рискну предположить: наличие fork (2), возвращающего PID, несовместимо с open (2), creat (2) и т. Д., Возвращающими файловые дескрипторы. Остальная часть системы Unix могла бы выполнять манипулирование процессом с помощью файлового дескриптора, представляющего процесс, вместо PID. Существует системный вызов signalfd (2) , который позволяет несколько лучше взаимодействовать между сигналами и файловыми дескрипторами и показывает, что файловый дескриптор, представляющий процесс, может сработать.


signalfd (2) выглядит потрясающе, спасибо, что упомянули об этом! Жаль, что это только для Linux.
— Ласси

1
Об этом также говорили pidfd_openв Linux, см., Например, lwn.net/Articles/789023
— dhag
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.