Co się stanie, gdy zamknę () deskryptor pliku?

16

Próbuję uzyskać cały obraz z deskryptorami plików. Powiedzmy, że mam proces1, który początkowo ma następujące deskryptory plików:

 _process1_
|          |
| 0 stdin  |
| 1 stdout |
| 2 stderr |
|__________|

Następnie zamykam deskryptor pliku 1:

close(1);

Deskryptor pliku 1 tłumaczy (wskazuje) na standardową strukturę PLIK w tabeli otwartych plików jądra .

Z powyższym kodem deskryptor pliku 1 zostaje usunięty z tabeli procesu, który staje się:

 _process1_
|          |
| 0 stdin  |
| 2 stderr |
|__________|

Ale co dzieje się w jądrze? Czy stdoutstruktura PLIKU zostaje zwolniona? Jak to możliwe, jeśli stdout jest specjalnym plikiem (monitorem) i prawdopodobnie jest wykorzystywany przez inne procesy? Co ze strukturami PLIKÓW, które są zwykłymi plikami (na przykład .txt)? Co jeśli taki plik jest używany przez inny proces?

Pithikos
źródło

Odpowiedzi:

13

Deskryptor pliku 1 tłumaczy na standardową strukturę PLIKU w Tabeli Otwartych Plików Jądra.

To nieporozumienie. Tabela plików jądra nie ma nic wspólnego ze strukturami plików w przestrzeni użytkownika.

W każdym razie jądro ma dwa poziomy pośrednie. Istnieje wewnętrzna struktura reprezentująca sam plik, który jest liczony jako odniesienie. Istnieje „opis otwartego pliku”, który jest liczony jako odniesienie. A potem jest uchwyt pliku, który nie jest liczony jako odniesienie. Struktura plików wskazuje drogę do samego i-węzła. Opis otwartego pliku zawiera takie rzeczy, jak tryb otwarty i wskaźnik pliku.

Kiedy wywołujesz close, zawsze zamykasz uchwyt pliku. Gdy uchwyt pliku jest zamknięty, liczba odwołań w opisie otwartego pliku jest zmniejszana. Jeśli osiągnie zero, otwarty opis pliku jest również zwalniany, a liczba odwołań do samego pliku jest zmniejszana. Tylko wtedy, gdy osiągnie zero, struktura jądra zostanie zwolniona.

Jeden proces nie ma szans na uwolnienie zasobu, z którego korzysta inny proces, ponieważ zasoby współużytkowane są liczone jako referencje.

David Schwartz
źródło
Mam niewielką trudność ze zrozumieniem terminologii w twojej odpowiedzi. Zgaduję, że wskaźnik pliku oznacza „przesunięcie pliku”. Czy o to ci chodziło? Co również rozumiesz przez uchwyt pliku ?
Geek
Zgadza się, przez „przesunięcie pliku” rozumiem przesunięcie, przy którym nastąpiłby kolejny odczyt lub zapis. „Uchwyt pliku” to łącze między procesem a otwartym opisem pliku - to, co otrzymujesz, gdy się openpowiedzie.
David Schwartz
6

W takim przypadku niewiele się wydarzy. stdin, stdout i stderr są zwykle klonami tego samego deskryptora pliku. Licznik referencyjny deskryptora pliku zostanie zmniejszony o jeden. Ten sam deskryptor pliku zwykle znajduje się w powłoce, z której uruchomiono program, więc deskryptor pliku musi być przechowywany.

Jądro przechowuje liczniki referencji dla wszystkich otwartych plików (i-węzłów). Dopóki liczba referencji jest większa od zera, plik będzie przechowywany. Spodziewałbym się, że dla otwartych uchwytów plików jest przechowywany osobny licznik. Gdy osiągnie zero, jądro może zwolnić pamięć używaną przez uchwyt pliku.

Po usunięciu wszystkich odniesień do pliku (pozycji katalogu i uchwytów plików) kod systemu plików oznaczy i-węzeł do ponownego użycia. Wszelkie bloki pliku są udostępniane do alokacji. Wiele systemów plików wyczyści wskaźniki blokowe w i-węzle, gdy zostanie zwolnione. Utrudnia to odzyskanie usuniętego pliku. Aktualizacje dysku mogą być buforowane i ukończone w późniejszym czasie.

BillThor
źródło
1
Dwa pytania: (1) czy deskryptory plików są naprawdę liczone ponownie? Kiedy kontrolujesz d-a cat > some.file, kot dostaje EOF na standardowe wejście, ale skorupa nie. (2) Dlaczego liczenie referencji? Dlaczego nie jakaś forma śmieci? Czy GC nie jest dużo lepsze w przestrzeni użytkownika?
Bruce Ediger
Rozszerzając odpowiedź BillThora: w normalnych przypadkach stdin, stdout i stderr są po prostu otwartymi uchwytami plików dla urządzenia TTY. Więc jeśli zamkniesz uchwyt pliku, to urządzenie TTY nadal tam jest, a nawet może być ponownie otwarte później.
Patrick,
1
@BruceEdiger: (1) kiedy powłoka uruchamia cat > some.fileto, co faktycznie robi, to rozwidla się, otwierając plik „some.file” i przypisując go do deskryptora pliku 1, a następnie robi to exec("cat"). Kiedy proces jest wykonywany (d), dziedziczy deskryptory otwartych plików.
Patrick
@BruceEdiger (2) Zliczanie referencji to doskonale doskonała forma odśmiecania, gdy jest używana w strukturach danych, które nie zawierają wskaźników do (lub łańcuchów wskaźników kończących się) innych struktur danych tego samego typu. Dzieje się tak również w przestrzeni jądra (nie ma to większego znaczenia).
Gilles 'SO - przestań być zły'