Я исследовал другой вопрос , когда понял, что не понимаю, что происходит под капотом, что это за /dev/fd/*
файлы и как их могут открывать дочерние процессы.
bash
process-substitution
х-юри
источник
источник
Ответы:
Ну, в этом есть много аспектов.
Файловые дескрипторы
Для каждого процесса ядро поддерживает таблицу открытых файлов (ну, это может быть реализовано по-разному, но, поскольку вы все равно не можете ее увидеть, вы можете просто предположить, что это простая таблица). Эта таблица содержит информацию о том, в каком файле он находится / где его можно найти, в каком режиме вы его открыли, в какой позиции вы сейчас читаете / записываете и что еще нужно для фактического выполнения операций ввода-вывода с этим файлом. Теперь процессу никогда не удается прочитать (или даже записать) эту таблицу. Когда процесс открывает файл, он возвращает так называемый дескриптор файла. Что просто указатель в таблицу.
Каталог
/dev/fd
и его содержаниеВ Linux
dev/fd
это символическая ссылка/proc/self/fd
./proc
это псевдофайловая система, в которой ядро отображает несколько внутренних структур данных, к которым осуществляется доступ с помощью файлового API (поэтому они просто выглядят как обычные файлы / каталоги / символические ссылки на программы). Особенно есть информация обо всех процессах (именно это и дало название). Символическая ссылка/proc/self
всегда относится к каталогу, связанному с текущим запущенным процессом (то есть процессом, запрашивающим его; поэтому разные процессы будут видеть разные значения). В каталоге процесса есть подкаталогfd
который для каждого открытого файла содержит символическую ссылку, имя которой является просто десятичным представлением дескриптора файла (индекс в таблице файлов процесса, см. предыдущий раздел), и чьей целью является файл, которому он соответствует.Файловые дескрипторы при создании дочерних процессов
Дочерний процесс создается
fork
. Afork
создает копию файловых дескрипторов, что означает, что созданный дочерний процесс имеет тот же список открытых файлов, что и родительский процесс. Таким образом, пока один из открытых файлов не будет закрыт дочерним процессом, доступ к унаследованному дескриптору файла в дочернем элементе будет обращаться к тому же файлу, что и к исходному дескриптору файла в родительском процессе.Обратите внимание, что после разветвления у вас изначально есть две копии одного и того же процесса, которые отличаются только возвращаемым значением от вызова fork (родительский элемент получает PID дочернего элемента, дочерний - 0). Обычно после разветвления следует a,
exec
чтобы заменить одну из копий другим исполняемым файлом. Дескрипторы открытого файла переживают это exec. Также обратите внимание, что перед exec процесс может выполнять другие манипуляции (например, закрытие файлов, которые новый процесс не должен получить, или открытие других файлов).Безымянные трубы
Безымянный канал - это просто пара файловых дескрипторов, созданных по запросу ядра, так что все, что записано в первый файловый дескриптор, передается второму. Наиболее часто используется для труб конструкции
foo | bar
изbash
, где стандартный выводfoo
заменяется на запись части трубы, а стандартный ввод заменяет по считанной части. Стандартный ввод и стандартный вывод - это только первые две записи в таблице файлов (записи 0 и 1; 2 - стандартная ошибка), и поэтому замена их означает просто переписать эту запись таблицы с данными, соответствующими другому дескриптору файла (опять же, фактическая реализация может отличаться). Поскольку процесс не может получить доступ к таблице напрямую, для этого есть функция ядра.Процесс замещения
Теперь у нас есть все вместе, чтобы понять, как работает процесс замены:
echo
процесса. Дочерний процесс (который является точной копией исходногоbash
процесса) закрывает конец чтения канала и заменяет свой собственный стандартный вывод концом записи канала. Учитывая, чтоecho
это встроенная оболочка, онаbash
может сэкономитьexec
вызов, но в любом случае это не имеет значения (встроенная оболочка также может быть отключена, в этом случае она исполняется/bin/echo
).<(echo 1)
псевдо-файловой/dev/fd
ссылкой на конец чтения безымянного канала./dev/fd/
. Поскольку соответствующий дескриптор файла все еще открыт, он все еще соответствует концу чтения канала. Поэтому, если программа PHP открывает данный файл для чтения, она фактически создаетsecond
дескриптор файла для конца чтения безымянного канала. Но это не проблема, это можно прочитать с любого.echo
команды, которая идет в конец записи того же канала.источник
php
сценарии, ноphp
плохо обращаетесь с трубами . Также, учитывая командуcat <(echo test)
, странная вещь в том, чтоbash
разветвляется один разcat
, но дваждыecho test
.Заимствование из
celtschk
ответа,/dev/fd
является символической ссылкой на/proc/self/fd
. И/proc
это псевдофайловая система, которая представляет информацию о процессах и другую системную информацию в виде иерархической файловой структуры. Файлы/dev/fd
соответствуют файлам, которые открываются процессом и имеют дескриптор файла в качестве своих имен, а сами файлы - их цели. Открытие файла/dev/fd/N
эквивалентно дублированию дескриптораN
(при условии, что дескрипторN
открыт).И вот результаты моего исследования того, как это работает (
strace
вывод избавлен от ненужных деталей и изменен, чтобы лучше выразить, что происходит):По сути,
bash
создает канал и передает его концы своим дочерним элементам как дескрипторы файлов (конец чтения и конец1.out
записи2.out
). И передает read end как параметр командной строки1.out
(/dev/fd/63
). Этот способ1.out
способен открыть/dev/fd/63
.источник