[117814]Ну, в этом есть много аспектов.[12190]Файловые дескрипторы[12191]Для каждого процесса ядро поддерживает таблицу открытых файлов (ну, это может быть реализовано по-другому, но так как вы все равно не можете ее увидеть, можно просто предположить, что это простая таблица). Эта таблица содержит информацию о том, какой файл она есть/где ее можно найти, в каком режиме вы ее открыли, в какой позиции вы в настоящее время читаете/записываете, и что еще необходимо для выполнения операций ввода/вывода этого файла. Теперь процесс никогда не сможет прочитать (или даже записать) эту таблицу. Когда процесс открывает файл, он получает обратно так называемый дескриптор файла. Каталог [118467]/dev/fd[118468] и его содержимое [12193] В Linux [118324]dev/fd[118325] на самом деле является символической ссылкой на [118326]/proc/self/fd[118327]. [118328]/proc[118329] - это псевдо-файловая система, в которой ядро отображает с помощью файлового API несколько внутренних структур данных для доступа к ним (поэтому они выглядят как обычные файлы/директории/символы ссылок на программы). Особенно это касается информации обо всех процессах (что и дало ей имя). Символическая ссылка [118330]/proc/self[118331] всегда относится к каталогу, связанному с выполняющимся в данный момент процессом (т.е. к запрашивающему его процессу; поэтому разные процессы будут видеть разные значения). В директории процесса есть подкаталог [118332]fd[118333], который для каждого открытого файла содержит символическую связь, имя которой является десятичным представлением дескриптора файла (индекс в файловой таблице процесса, см. предыдущий раздел), и целью которого является файл, которому он соответствует.[12194]Файловые дескрипторы при создании дочерних процессов[12195]Дочерний процесс создается развилкой [118336]fork[118337]. Вилка [118338]fork[118339] делает копию дескрипторов файлов, что означает, что созданный дочерний процесс имеет тот же самый список открытых файлов, что и родительский процесс. Таким образом, если дочерний процесс не закроет один из открытых файлов, то доступ к унаследованному дескриптору файла в дочернем процессе получит тот же самый файл, что и доступ к исходному дескриптору файла в родительском процессе.[12196]Обратите внимание, что после вилки у вас изначально две копии одного и того же процесса, которые отличаются только возвращаемым значением от вызова вилки (родитель получает PID дочернего процесса, а дочерний процесс - 0). Обычно за вилкой следует [118340]exec[118341] для замены одной из копий на другую. Открытые файловые дескрипторы выживают в этом исполняющем. Заметим также, что перед выполнением процесс может выполнять и другие манипуляции (например, закрывать файлы, которые новый процесс не должен получить, или открывать другие файлы).[12197]Безымянные каналы[12198]Безымянный канал - это всего лишь пара файловых дескрипторов, созданная по запросу ядра, так что все, что написано в первом файловом дескрипторе, передается во втором. Наиболее часто используется трубная конструкция [118344]foo | bar[118345] из [118346]bash[118347], где стандартный вывод [118348]foo[118349] заменяется на запись в трубку, а стандартный ввод - на чтение. Стандартный вход и стандартный выход - это только первые две записи в файловой таблице (запись 0 и 1; 2 - стандартная ошибка), и поэтому замена ее означает просто перезапись этой записи таблицы данными, соответствующими другому файловому дескриптору (опять же, реальная реализация может отличаться). Так как процесс не может получить прямой доступ к таблице, то для этого существует функция ядра.[12199]Подстановка процесса[12200]Теперь у нас есть все вместе, чтобы понять, как работает подстановка процесса:[12201]Бэш-процесс создает неназванную трубку для связи между двумя процессами, созданными позже.[12202]Бэш-вилки для процесса [118469]echo[118470]. Детский процесс (который является точной копией оригинального процесса [118471]bash[118472]) закрывает читающий конец трубы и заменяет свой собственный стандартный выход на пишущий конец трубы. Учитывая, что [118473]echo[118474] является оболочкой, [118475]bash[118476] может избавить себя от вызова [118477]exec[118478], но это не имеет значения в любом случае (оболочка также может быть отключена, и в этом случае она выполняет [118479]/bin/echo[118480]). [12203]Bash (оригинальный, родительский) заменяет выражение [118481]<(echo 1)[118482] на ссылку на псевдо-файл в [118483]/dev/fd[118484], относящуюся к читающему концу безымянной трубы.[12204]Bash выполняет для PHP-процесса (заметьте, что после развилки мы все еще внутри [копии] bash). Новый процесс закрывает унаследованный конец записи безымянной трубы (и делает некоторые другие подготовительные шаги), но оставляет открытым прочитанный конец. Затем выполняется PHP.[12205]Программа PHP получает имя в [118485]/dev/fd/[118486]. Так как соответствующий файловый дескриптор все еще открыт, он все еще соответствует читающему концу канала. Поэтому, если PHP-программа открывает данный файл для чтения, то на самом деле она создает дескриптор файла [118487]/dev/fd/[118486] для читающего конца безымянной трубы. Но это не проблема, она может читать и с того, и с другого.[12206]Теперь PHP-программа может читать читающий конец трубы через новый файловый дескриптор, и таким образом получать стандартный вывод команды [118489]echo[118490], которая идет к пишущему концу той же самой трубы.[12207]
27.01.2020, 19:54
Ссылка