Badałem drugie pytanie , kiedy zdałem sobie sprawę, że nie rozumiem, co dzieje się pod maską, jakie są te /dev/fd/*
pliki i jak mogą je otwierać procesy potomne.
bash
process-substitution
x-yuri
źródło
źródło
Odpowiedzi:
Jest wiele aspektów.
Deskryptory plików
Dla każdego procesu jądro utrzymuje tabelę otwartych plików (cóż, może być zaimplementowane inaczej, ale ponieważ i tak nie możesz go zobaczyć, możesz po prostu założyć, że jest to prosta tabela). Ta tabela zawiera informacje o tym, który plik / gdzie można go znaleźć, w jakim trybie go otworzyłeś, w jakiej pozycji aktualnie czytasz / piszesz i cokolwiek innego, co jest potrzebne do faktycznego wykonania operacji we / wy na tym pliku. Teraz proces nigdy nie może odczytać (ani nawet napisać) tej tabeli. Gdy proces otwiera plik, otrzymuje tak zwany deskryptor pliku. Który jest po prostu indeksem w tabeli.
Katalog
/dev/fd
i jego zawartośćW systemie Linux
dev/fd
jest faktycznie dowiązaniem symbolicznym/proc/self/fd
./proc
to pseudo system plików, w którym jądro mapuje kilka wewnętrznych struktur danych, do których można uzyskać dostęp za pomocą interfejsu API plików (więc wyglądają jak zwykłe pliki / katalogi / dowiązania symboliczne do programów). Zwłaszcza są informacje o wszystkich procesach (co nadało mu nazwę). Łącze symboliczne/proc/self
zawsze odnosi się do katalogu związanego z aktualnie uruchomionym procesem (czyli procesem go żądającym; dlatego różne procesy będą widzieć różne wartości). W katalogu procesu znajduje się podkatalogfd
który dla każdego otwartego pliku zawiera dowiązanie symboliczne, którego nazwa jest po prostu dziesiętną reprezentacją deskryptora pliku (indeks do tabeli plików procesu, patrz poprzednia sekcja), a którego celem jest plik, do którego odpowiada.Deskryptory plików podczas tworzenia procesów potomnych
Proces potomny jest tworzony przez
fork
. Afork
tworzy kopię deskryptorów plików, co oznacza, że utworzony proces potomny ma tę samą listę otwartych plików, co proces macierzysty. Tak więc, dopóki jeden z otwartych plików nie zostanie zamknięty przez dziecko, dostęp do odziedziczonego deskryptora pliku w dziecku uzyska dostęp do tego samego pliku, co dostęp do oryginalnego deskryptora pliku w procesie nadrzędnym.Zauważ, że po rozwidleniu początkowo masz dwie kopie tego samego procesu, które różnią się tylko wartością zwracaną z wywołania rozwidlenia (rodzic otrzymuje PID dziecka, dziecko dostaje 0). Zwykle po rozwidleniu następuje
exec
zamiana jednej z kopii na inny plik wykonywalny. Otwarte deskryptory plików przetrwają to exec. Zauważ też, że przed wykonaniem proces może wykonywać inne manipulacje (takie jak zamykanie plików, których nowy proces nie powinien otrzymać, lub otwieranie innych plików).Nienazwane rury
Potok bez nazwy to tylko para deskryptorów plików utworzonych na żądanie przez jądro, dzięki czemu wszystko, co zapisano w pierwszym deskryptorze pliku, jest przekazywane do drugiego. Najczęstszym zastosowaniem jest konstruktem rurociągów
foo | bar
zbash
, których poziom wyjściowyfoo
jest zastąpiona przez część zapisu rury, a standardowe wejście jest zastąpione przez odczytu strony. Standardowe wejście i standardowe wyjście to tylko dwa pierwsze wpisy w tabeli plików (wpisy 0 i 1; 2 to błąd standardowy), dlatego zastąpienie go oznacza po prostu przepisanie tego wpisu tabeli danymi odpowiadającymi drugiemu deskryptorowi pliku (ponownie faktyczna implementacja może się różnić). Ponieważ proces nie może uzyskać bezpośredniego dostępu do tabeli, dostępna jest funkcja jądra.Zastąpienie procesu
Teraz mamy wszystko razem, aby zrozumieć, jak działa podstawienie procesu:
echo
proces. Proces potomny (który jest dokładną kopią oryginalnegobash
procesu) zamyka koniec odczytu potoku i zamienia własne standardowe wyjście na koniec zapisu potoku. Biorąc pod uwagę, żeecho
jest to wbudowana powłoka,bash
może oszczędzić sobieexec
wywołania, ale i tak nie ma to znaczenia (wbudowana powłoka może być również wyłączona, w takim przypadku wykonuje się/bin/echo
).<(echo 1)
pseudo linkiem pliku w/dev/fd
odniesieniu do końca odczytu nienazwanego potoku./dev/fd/
. Ponieważ odpowiedni deskryptor pliku jest nadal otwarty, nadal odpowiada końcowi odczytu potoku. Dlatego jeśli program PHP otworzy dany plik do odczytu, to w rzeczywistości tworzysecond
deskryptor pliku dla końca odczytu nienazwanego potoku. Ale to nie problem, można odczytać z obu.echo
polecenia, które przechodzi do końca zapisu tego samego potoku.źródło
php
scenariuszu, alephp
nie radzi sobie dobrze z rurami . Ponadto, biorąc pod uwagę poleceniecat <(echo test)
, dziwną rzeczą jest to, żebash
rozwidla się razcat
, ale dwa razyecho test
.Pożyczanie od
celtschk
odpowiedzi/dev/fd
to symboliczny link do/proc/self/fd
. I/proc
to pseudo system plików, który prezentuje informacje o procesach i inne informacje systemowe w hierarchicznej plikopodobny struktury. Pliki w plikach/dev/fd
odpowiadają plikom otwieranym przez proces i mają deskryptor plików jako swoje nazwy i same pliki jako cele. Otwarcie pliku/dev/fd/N
jest równoważne z powieleniem deskryptoraN
(przy założeniu, że deskryptorN
jest otwarty).A oto wyniki mojego badania tego, jak to działa (
strace
dane wyjściowe pozbywają się niepotrzebnych szczegółów i zmodyfikowane, aby lepiej wyrazić, co się dzieje):Zasadniczo
bash
tworzy potok i przekazuje swoje końce swoim potomkom jako deskryptory plików (odczytaj koniec do1.out
i zapisz koniec do2.out
). I przekazuje koniec odczytu jako parametr wiersza poleceń do1.out
(/dev/fd/63
). W ten sposób1.out
można się otworzyć/dev/fd/63
.źródło