Czytałem o tym, jak rury są implementowane w jądrze Linuksa i chciałem zweryfikować moje zrozumienie. Jeśli się mylę, zostanie wybrana odpowiedź z poprawnym wyjaśnieniem.
- Linux ma VFS o nazwie pipefs, który jest zamontowany w jądrze (nie w przestrzeni użytkownika)
- pipefs ma pojedynczy super blok i jest montowany we własnym rootie (
pipe:
) obok/
- pipefs nie może być oglądany bezpośrednio, w przeciwieństwie do większości systemów plików
- Wejście do pipefs odbywa się poprzez
pipe(2)
syscall pipe(2)
Syscall wykorzystywane przez powłok do rur z|
operatorem (ręcznie lub z dowolnego innego procesu) tworzy nowy plik pipefs która zachowuje się prawie jak normalny plik- Plik po lewej stronie operatora potoku został
stdout
przekierowany do pliku tymczasowego utworzonego w potokach - Plik po prawej stronie operatora potoku ma
stdin
ustawiony plik na pipefs - pipefs jest przechowywany w pamięci i za pomocą jakiejś magii jądra nie powinien być stronicowany
Czy to wyjaśnienie, w jaki sposób rury (np. ls -la | less
) Działają, jest właściwie poprawne?
Jednej rzeczy, której nie rozumiem, jest to, jak coś takiego jak bash ustawiłoby proces stdin
lub stdout
zwracany przez deskryptor pliku pipe(2)
. Nie znalazłem jeszcze nic na ten temat.
pipe()
Wywołanie jądro wraz z maszyną, która je obsługuje (pipefs
itp) jest znacznie niższy poziom niż|
operator zaoferował w swojej skorupie. Ten drugi jest zwykle implementowany przy użyciu tego pierwszego, ale nie musi tak być.|
operator po prostu wywołujepipe(2)
jak proces bash.Odpowiedzi:
Twoja dotychczasowa analiza jest ogólnie poprawna. Sposób, w jaki powłoka może ustawić standardowe wejście procesu na deskryptor potoku, może być (pseudokod):
źródło
dup2
potrzebne jest wywołanie, a nie można bezpośrednio przypisać deskryptora potoku do standardowego wejścia?pipe()
.dup2()
Połączenie pozwala wywołujący skopiować deskryptor określonej wartości liczbowej (potrzebne, ponieważ 0, 1, 2 są standardowe wejście, wyjście, stderr). To jest odpowiednik jądra „przypisywania bezpośrednio do standardowego wejścia”. Zauważ, że globalna zmienna biblioteki wykonawczej Cstdin
jest aFILE *
, która nie jest związana z jądrem (chociaż jest inicjowana w celu połączenia z deskryptorem 0).dup2
Połączenie się nie zmieniap[1]
. Zamiast tego tworzy dwa uchwytyp[1]
i0
wskazuje ten sam obiekt jądra (potok). Ponieważ proces potomny nie potrzebuje dwóch uchwytów stdin (i nie wiedziałby, co to jest numerowany uchwytp[1]
),p[1]
został wcześniej zamkniętyexec
.