Почему функция 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.