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


16

На своїй веб-сторінці про трюк з самовідводу Дан Бернштейн пояснює стан перегонів select()та сигналів, пропонує вирішення та робить висновок, що

Звичайно, правильною річчю було б fork()повернути дескриптор файлу, а не ідентифікатор процесу.

Що він має на увазі під цим - це щось про те, щоб дозволити select()дочірнім процесам обробляти зміни свого стану, а не використовувати обробник сигналів, щоб отримувати повідомлення про ці зміни у стані?


Чи змішується ця стаття введенням і виведенням, чи я не читаю це правильно?
ctrl-alt-delor

Ви можете попросити подати сигнали по трубах. Це я і роблю.
ctrl-alt-delor

@ ctrl-alt-delor, так, він, здається, використовує "введення / виведення труб" трохи дивним чином, але я думаю, що зрозуміло, де він пише і де читає з труби. Цей текст з 2003 року, і я не впевнений, signalfdщо такі речі були тоді?
ількакачу

5
Ден знає, про що говорить, хоча може бути трохи навмисно провокаційним. Якби я був навмисно провокаційним, я б вважав, що, звичайно, правильною річчю було б позбутися ЗІГЛЯ.
Стів Саміт

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
Як ви вже заявляли, немає жодного приводу для того, щоб батьки знайшли власний дитячий під, він повторно використовувався. Він повністю контролює цю ситуацію, тому що саме той, хто дзвонить wait().
Джошуа

Вони називаються зомбі-процесами : "процес, який завершив виконання, але все ще має запис у таблиці процесів: це процес у" припиненому стані ". Це відбувається для дочірніх процесів, де запис все ще потрібен, щоб дозволити батькові процес зчитування статусу виходу своєї дитини "
Лассі

6
Варто зазначити, що тепер Linux може повернути дескриптор файлу (pidfd) з clone, який є фактичним системним викликом, який вилка викликає в LInux. Прапор, щоб увімкнути це, називається CLONE_PIDFD- Дивіться, наприклад, lwn.net/Articles/784831 .
KJ Tsanaktsidis

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] Запобігання повторному waitpid()циклу PID до моменту повернення syscall та використання його значення, можливо, можна досягти в нових системах, використовуючи waitid(.., WNOWAIT)замість цього.

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


8

Бернштейн не дає великого контексту для цього зауваження "Правильна річ", але я загрожу здогадкою: наявність вилки (2) повернути PID не відповідає відкритим (2), create (2) і т.д. поверненням дескрипторів файлів. Решта системи Unix могла б здійснити маніпуляцію процесу за допомогою дескриптора файлів, що представляє процес, а не PID. Існує сигнальний виклик системного виклику (2) , який дозволяє дещо кращу взаємодію між сигналами та дескрипторами файлів і показує, що файл-дескриптор, що представляє процес, може вийти.


signalfd (2) виглядає приголомшливо, дякую, що згадуєте про це! Шкода, що це лише для Linux.
Лассі

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