Czy bufor zostanie automatycznie opróżniony na dysk po zakończeniu procesu?

21

Kiedy przekieruję wyjście polecenia do pliku (np. echo Hello > file), Czy plik ten będzie miał gwarancję, że będzie miał takie dane zaraz po wyjściu polecenia? Czy jest jeszcze bardzo małe okno między wyjściami polecenia a danymi zapisanymi w pliku? Chciałbym przeczytać plik zaraz po wyjściu polecenia, ale nie chcę czytać pustego pliku.

Eric
źródło
1
Prawdopodobnie wykonuje polecenie od razu, ale ilość czasu potrzebnego do faktycznie otworzyć pliku, zapisz i zamknij będzie zależeć od szybkości i rodzaju dysku twardego, wszystkie uruchomione programy, itp
freginold
W odniesieniu do podanego przykładu, czym jest „proces”? Są echoi >nie oddzielne (krótko) procesy? A gdzie wykonywany jest wynik echopozostania przed >?
o
1
@ oɔɯǝɹ >to przekierowanie powłoki. Jest tak, jakby program otworzył nazwany plik do zapisu i zastąpił go stdout, co jest dokładnie tym, co robi powłoka.
Dan D.
7
Myślę, że obowiązkiem systemu operacyjnego jest zapewnienie filezawartości Hellobez względu na to, czy jest ona opróżniona czy nie.
Salman A
1
Jeśli program działa na komputerze A i czytasz plik na komputerze B, z systemem plików komputera A podłączonym do sieci, możesz skończyć czytaniem pustego pliku, w zależności od typu sieciowego systemu plików i ustawień montowania. Więc możesz chcieć wyłączyć buforowanie dla tego montowania.
pkt

Odpowiedzi:

21

W grę wchodzi wiele warstw buforów / pamięci podręcznych.

  1. Pamięć podręczna procesora.

    Dane są łączone bajt po bajcie i przechowywane w pamięci podręcznej procesora. Jeśli pamięć podręczna procesora jest pełna i dane nie były dostępne przez pewien czas, blok zawierający nasze dane może zostać zapisany w pamięci głównej. Są one w większości ukryte przed programistami aplikacji.

  2. Bufory procesowe.

    W procesie, w którym gromadzone są dane, jest trochę pamięci, dlatego musimy składać jak najmniej żądań do systemu operacyjnego, ponieważ jest to stosunkowo kosztowne. Proces kopiuje dane do tych buforów, które ponownie mogą być wspierane przez pamięci podręczne procesora, więc nie ma gwarancji, że dane zostaną skopiowane do pamięci głównej. Aplikacja musi jawnie opróżnić te bufory, na przykład używając fclose (3) lub fsync (3). Funkcja exit (3) robi to również przed zakończeniem procesu, podczas gdy funkcja _exit (2) nie , dlatego na stronie podręcznika jest duże ostrzeżenie, aby ta funkcja mogła ją wywołać tylko wtedy, gdy wiesz, kim jesteś robić.

  3. Bufory jądra

    System operacyjny utrzymuje następnie własną pamięć podręczną, aby zminimalizować liczbę żądań, które musi wysłać na dyski. Ta pamięć podręczna nie należy w szczególności do żadnego procesu, więc dane w niej mogą należeć do procesów, które już się zakończyły, a ponieważ wszystkie przejścia przechodzą tutaj, następny program zobaczy dane, jeśli tutaj dotarł. Jądro zapisze te dane na dyskach, gdy będzie miał na to czas lub gdy zostanie wyraźnie o to poproszony.

  4. Pamięć podręczna dysku

    Same dyski przechowują również pamięć podręczną, aby przyspieszyć dostęp. Są one pisane dość szybko i istnieje polecenie, aby zapisać pozostałe dane w pamięci podręcznej i zgłosić, kiedy jest to zakończone, którego system operacyjny używa podczas zamykania, aby upewnić się, że żadne dane nie zostaną niepisane przed wyłączeniem.

W przypadku aplikacji wystarczy, aby dane zostały zarejestrowane w buforach jądra (rzeczywiste dane mogą w tym momencie nadal znajdować się w pamięci podręcznej procesora i mogły nie zostać zapisane w pamięci głównej): proces „echo” kończy się, co oznacza, że ​​wszelkie bufory wewnątrzprocesowe musiały zostać opróżnione, a dane przekazane do systemu operacyjnego, a po rozpoczęciu nowego procesu gwarantuje się, że system operacyjny zwróci te same dane, gdy zostanie o to poproszony.

Simon Richter
źródło
7
Biorąc pod uwagę, że buforowanie procesora nie wydaje mi się istotne. To jest tutaj niepotrzebny poziom szczegółowości. Podobnie jak przechodzenie przez wszystkie szczegóły, aż jakaś fizyczna ilość reprezentująca bit na dysku twardym lub pamięci ssd zostanie zmieniona, aby ją odwrócić.
mvw
3
Rzeczywiście, pamięć podręczna procesora jest dość ortogonalna.
Simon Richter
2
Co ważniejsze, pamięć podręczna procesora jest spójna między rdzeniami, dlatego całkowicie zniknęła z obrazu. Na x86 jest nawet spójny z DMA (i x86 ma tryb zamawiania pamięci w całkowitej kolejności przechowywania), więc wszystko, co może odczytać pamięć, zobaczy ostatnio zapisane dane pod tym adresem w globalnej kolejności operacji pamięci. (Rdzeń procesora zobaczy własne sklepy nawet zanim staną się globalnie widoczne z powodu przekierowania sklepu z kolejki sklepu). Na platformach innych niż x86 bez pamięci DMA spójnej z pamięcią podręczną jądro Linuksa upewnia się, że pamięć podręczna jest opróżniana przed DMA na te adresy.
Peter Cordes
1
„Są one w większości ukryte przed programistami aplikacji”. Dlaczego „w przeważającej części”? Jestem wbudowanym programistą i z wyjątkiem programu ładującego (więc nie „aplikacji”) całkowicie ignoruję pamięć podręczną procesora. Nie sądzę, aby wpływ na pamięć podręczną procesora miał wpływ na dowolnego programistę aplikacji.
Sam
1
Pominięcia / trafienia pamięci podręcznej @Sam w połączeniu z wykonywaniem spekulacyjnym mogą być wykorzystywane w niektórych procesorach w celu ominięcia ograniczeń dostępu do odczytu. Być może o to właśnie chodziło w odpowiedzi?
John Dvorak
22

Jeśli aplikacja nie ma żadnych wewnętrznych pamięci podręcznych, zmiany zostaną natychmiast zapisane w pliku. To samo dla twojego przykładu. Plik jest logicznym bytem w pamięci, który zostanie natychmiast zaktualizowany. Wszelkie dalsze operacje na pliku spowodują zmiany dokonane przez program.

Nie oznacza to jednak , że zmiana została zapisana na dysku fizycznym. Zmiany mogą pozostać w pamięci podręcznej systemu plików OS lub pamięci podręcznej sprzętu. Aby opróżnić bufory systemu plików, użyj syncpolecenia.

Chciałbym przeczytać plik zaraz po wyjściu polecenia, ale nie chcę czytać pustego pliku.

Nie powinieneś mieć tutaj żadnych praktycznych problemów.

mtak
źródło
1
„Jeśli aplikacja nie ma żadnych wewnętrznych pamięci podręcznych” - to bardzo duże „jeśli”: ogromna większość implementacji bibliotek we / wy domyślnie używa standardowego bufora. To powiedziawszy, standard C na przykład nakazuje opróżnienie bufora standardowego przy wyjściu (ale potencjalnie nie, jeśli exitnie zostanie przynajmniej domyślnie wywołany). Inne biblioteki / języki (np. Java!) Dają mniej gwarancji.
Konrad Rudolph
Co jeśli po prostu ograniczysz to do prymitywu przekierowań (tj. Polecenia w moim pytaniu)? Nie ma wewnętrznych pamięci podręcznych, prawda?
Eric
@Eric Nie, wszystko powinno być w porządku.
mtak
10
Nie jestem pewien, czy otrzymam tę odpowiedź. Pytanie dotyczy „kiedy proces się kończy”. Każda aplikacja z wewnętrznymi pamięciami podręcznymi zapisuje je na dysk przy wyjściu z procesu, jeśli nie miało to miejsca wcześniej. IOW, te skrzynki nie mają tutaj znaczenia.
MSalters
2
Co więcej, wewnętrzny bufor albo zostanie opróżniony przy wyjściu, albo po prostu zniknie z istnienia, prawda? Więc nawet jeśli bufory wewnętrzne nie opróżniają się, zawartość nie będzie obserwowalna, bez względu na to, jak długo byś czekał.
WorldSEnder
21

Czy bufor zostanie automatycznie opróżniony na dysk po zakończeniu procesu?

Ogólnie odpowiedź brzmi „ nie” .

To zależy od polecenia. Jak wspomniano w innych odpowiedziach, jeśli polecenie nie buforuje danych wewnętrznie, wszystkie dane będą dostępne po zakończeniu polecenia.

Ale większość, jeśli nie wszystkie, standardowe biblioteki we / wy domyślnie wykonują buforowanie standardowe (do pewnego stopnia) i dają różne gwarancje dotyczące automatycznego opróżniania buforów po zamknięciu aplikacji.

C gwarantuje, że normalne wyjście opróżni bufory . „Normalne wyjście” oznacza, że exitjest wywoływane - albo jawnie, albo przez powrót z main. Jednak nienormalne wyjście może obejść to wezwanie (a zatem pozostawić niewyłączone bufory).

Oto prosty przykład:

#include <signal.h>
#include <stdio.h>

int main() {
    printf("test");
    raise(SIGABRT);
}

Jeśli to skompilować i uruchomić go, testbędzie nie muszą być zapisywane na standardowe wyjście.

Inne języki programowania dają nawet mniej gwarancje: Java, na przykład, czy nie auto-flush Po wygaśnięciu programu . Jeśli bufor wyjściowy zawiera niezakończoną linię, może zostać utracony, chyba że zostanie System.out.flush()jawnie wywołany.

To powiedziawszy, twoje pytanie pyta o coś nieco innego: jeśli dane w ogóle dotrą do pliku , powinno to zrobić natychmiast po zakończeniu polecenia (z zastrzeżeniem zastrzeżeń opisanych w innych odpowiedziach).

Konrad Rudolph
źródło
7
Widziałem także nienormalne wyjście, gdy narzędzie wiersza poleceń zapisuje do pliku i do standardowego lub standardowego, jak dziennik debugowania, a użytkownik wykonał potok do nagłówka lub mniej, niż wpisał „q”, aby rzucić mniej. Plik dyskowy nie zawsze jest w pełni opróżniany, jeśli narzędzie wiersza poleceń nie obsługuje SIGPIPE.
Zan Lynx,
+1, ale „powinien to uczynić natychmiast po tych wygaśnięciem dowodzenia” jest nie tak: każdy write()lub pwrite()wywołanie systemowe nastąpi wcześniej wyjść procesowych, a to jest, gdy zmienia plików stają się widoczne. Tak więc ostatnia zmiana pliku jest zdecydowanie przed zakończeniem procesu, najpóźniej bezpośrednio przed. Myślę, że nawet w przypadku mmap(MAP_SHARED)pliku nie ma sposobu, aby zaobserwować zakończenie procesu przed wszystkimi zmianami plików, które mają nastąpić.
Peter Cordes
9

Myślę, że żadne pytanie nie rozwiązuje jeszcze wystarczająco tej kwestii:

Chciałbym przeczytać plik zaraz po wyjściu polecenia, ale nie chcę czytać pustego pliku.

Jak wyjaśniają inne odpowiedzi, dobrze zachowujący się program opróżnia wewnętrzne bufory plików, zanim proces zakończy się normalnie . Następnie dane mogą pozostać w buforze jądra lub sprzętu, zanim zostaną zapisane w pamięci trwałej. Jednak semantyka systemu plików w systemie Linux gwarantuje, że wszystkie procesy widzą zawartość plików w taki sam sposób, jak jądro, w tym bufory wewnętrzne 1 .

Zwykle jest to realizowane przez posiadanie co najwyżej jednego bufora jądra na obiekt pliku i wymaganie pełnego dostępu do pliku, aby przejść przez ten bufor.

  • Jeśli proces odczyta plik, jądro przedstawi zawartość bufora procesowi, jeśli żądana część pliku znajduje się obecnie w buforze; jeśli nie jest, jądro pobierze dane z podstawowego nośnika pamięci i umieści je w buforze, a następnie wróci do poprzedniego kroku.

  • Jeśli proces zapisuje do pliku, dane są najpierw umieszczane w buforze jądra dla tego pliku. Ostatecznie zawartość bufora zostanie opróżniona do pamięci. Tymczasem dostęp do odczytu jest zapewniony z tego samego bufora (patrz wyżej).


1 Przynajmniej dla zwykłych plików, katalogów i dowiązań symbolicznych. FIFO i gniazda to inna sprawa, ponieważ ich zawartość i tak nigdy nie jest przechowywana w sposób trwały. Istnieją pewne szczególne przypadki zwykłych plików, których zawartość zależy od tego, kto pyta; przykładami są pliki w procfs i sysfs (pomyśl, /proc/selfktóry jest dowiązaniem symbolicznym do identyfikatora procesu odczytującego dowiązanie symboliczne).

David Foerster
źródło
2
Ściśle mówiąc, to nie semantyka systemu plików Linuksa to gwarantuje, to semantyka POSIX. W szczególności BSD zachowuje się dokładnie tak samo, jak macOS, a nawet Windows (choć jest to jeden z niewielu przypadków, w których Windows stosuje semantykę POSIX). Zakłada to również, że nikt nie robi dziwnych rzeczy z mmap()O_DIRECT, co może prowadzić do braku synchronizacji między dyskiem a pamięcią podręczną strony (ale to rozwiąże moment, w którym proces się kończy).
Austin Hemmelgarn
2
@AustinHemmelgarn: Ściśle mówiąc, oboje mamy rację, ponieważ Linux został zaprojektowany z myślą o obsłudze aplikacji uniksowych (System V), a później stworzony do obsługi POSIX, który również opiera wiele koncepcji na Systemie V.
David Foerster
5

Zakładając, że twoje polecenie jest wykonywane przez jakiś program korzystający z biblioteki wykonawczej C, w pewnym momencie powinien on wywołać, fcloseaby zamknąć otwarty plik.

Strona podręcznika dla fclosefunkcji C mówi:

UWAGI Zauważ, że fclose () opróżnia tylko bufory przestrzeni użytkownika dostarczone przez bibliotekę C. Aby upewnić się, że dane są fizycznie przechowywane na dysku, bufory jądra również muszą zostać opróżnione, na przykład za pomocą sync (2) lub fsync (2).

a strona podręcznika dla fflushma tę samą notatkę. Strona closepodręcznika dla mówi:

Pomyślne zamknięcie nie gwarantuje, że dane zostały pomyślnie zapisane na dysk, ponieważ jądro odkłada zapis. System plików nie często opróżnia bufory, gdy strumień jest zamknięty. Jeśli chcesz mieć pewność, że dane są fizycznie przechowywane, użyj fsync (2). (W tym momencie będzie to zależeć od sprzętu dysku.)

Pamiętaj, że dane są dostępne dla innych procesów, nawet jeśli nie są zsynchronizowane z dyskiem. Może to już ci wystarczy.

W razie wątpliwości napisz test.

mvw
źródło
2
C lub nie, wszystko użyje / powinno użyć close()syscall do zamknięcia deskryptora pliku.
Attie
@Attie: Nie potrzeba do closeplików przed zamknięciem (w Hacky programów, które nie sprawdzają błędów); jądro oczyści je, skutecznie wzywając closepo zakończeniu procesu. Potrzebujesz jednak fclosebuforowanych strumieni stdio lub pozwól libc zrobić to za Ciebie exit(3), w przeciwieństwie do bezpośredniego wyjścia systemowego.
Peter Cordes
W razie wątpliwości napisz test. To zła rada do wykrywania warunków wyścigu. Testowanie na jednym jądrze działającym na jednym sprzęcie może ci powiedzieć, że wyścig nie może się odbyć w warunkach oprogramowania wygenerowanych przez twój test w tym systemie, a jeśli tak, to zbyt rzadko można go wykryć. Ale nie może powiedzieć, czy takie zachowanie powinno być bezpieczne we wszystkich systemach plików, jądrach i całym sprzęcie (np. PowerPC). tzn. nie wiesz, czy gwarancja, na której polegasz, jest szczegółem wdrożenia, czy też celową gwarancją na przyszłość! (W tym przypadku tak jest.)
Peter Cordes
To zależy od sytuacji. Ta rada może pomóc niektórym ludziom próbującym uruchomić skrypt powłoki. Nie było to ogólne rozwiązanie dla bardziej zaawansowanych, ale mniej prawdopodobnych środowisk, np. Inżynier oprogramowania pracujący na jądrze systemu operacyjnego, niektórzy ludzie pracujący nad aktualizacją mikrokodu Intela lub niektórzy pracujący nad niektórymi systemami ISS.
mvw
3

Kiedy przekieruję wyjście polecenia do pliku (np. echo Hello > file), Czy plik ten będzie miał gwarancję, że będzie miał takie dane zaraz po wyjściu polecenia?

Tak. Powłoka otwiera plik echowyjściowy i wysyła bezpośrednio do niego. Po wyjściu polecenia jest gotowe.

Czy jest jeszcze bardzo małe okno między wyjściami polecenia a danymi zapisanymi w pliku?

To, czy dane są już na nośniku, to inna sprawa, która ma znaczenie tylko wtedy, gdy wystąpi awaria sprzętowa, lub gdy sprawdzasz partycję na żywo za pomocą jakiegoś oprogramowania kryminalistycznego, omijając zamontowany system plików.

Chciałbym przeczytać plik zaraz po wyjściu polecenia, ale nie chcę czytać pustego pliku.

Nie martw się, jądro zachowuje tylko jeden widok pliku, niezależnie od tego, jak często jest otwierany.

Deduplikator
źródło
„jądro zachowuje tylko jeden widok pliku”: nie do końca prawda mmap(MAP_SHARED): przechowywanie w regionie mmaped nie jest spójne z odczytami pliku (przez ten wątek lub inne procesy). Właśnie dlatego msync(2)istnieje. Przynajmniej o tym ostrzegają strony podręcznika; w zależności od implementacji, Linux może faktycznie mapować fizyczne strony z pamięci podręcznej, w takim przypadku domyślam się, że jest zasadniczo spójny (modułowe porządkowanie pamięci). Tak czy inaczej, wszystko dzieje się wcześniej _exit(2).
Peter Cordes
2

Zasadniczo wszelkie dane posiadane przez jądro są utrzymywane i usuwane przez jądro, kropka. Takie dane obejmują dane przesyłane do pamięci jądra za pomocą wywołania systemowego, takiego jak write(2).

Jednakże, jeśli aplikacja (np C Library) wykonuje buforowanie na szczycie tego, wtedy jądro oczywiście nie ma pojęcia, a tym samym nie gwarantują jego oczyszczenia.

Co więcej, nie sądzę, istnieje jakikolwiek rozrządu gwarancją czystego-up-to jest na ogół wykonywana na zasadzie „best-effort” (czytaj: „kiedy mam sec”) podstawy.

Mehrdad
źródło
Istnieje gwarancja, że ​​każde czyszczenie / opróżnianie bufora nastąpi przed waitpid()powrotem procesu nadrzędnego , jeśli w ogóle nastąpi. tzn. inne procesy nie mogą bezpośrednio zaobserwować zakończenia procesu przed jakimikolwiek modyfikacjami pliku dokonanymi przez ten proces. (Powiedziałem „bezpośrednio”, aby wykluczyć pośrednią obserwację za pomocą znaczników czasowych plików NFS, ponieważ buforowanie NFS nie jest idealnie spójne między hostami.)
Peter Cordes
@PeterCordes: Przypuszczam, że to zależy od tego, co rozumiesz przez „czyszczenie”, a nie „utrzymanie”. Dla mnie „utrzymanie” oznacza „zapewnienie spójnego obrazu” (który ma wspomnianą gwarancję), a „czyszczenie” to „opróżnianie dysku”, co nie wydaje się mieć gwarancji czasu.
Mehrdad
Och, rozumiem, odpowiadasz na część pytania „opróżnionego na dysk”, która nie ma znaczenia dla tego, co zobaczą późniejsze procesy podczas czytania pliku. „wyczyść” w znaczeniu „wyczyść pamięć podręczną we / wy / pamięć bufora”. Tak, nie ma gwarancji czasu, chyba że użyjesz fsync/ fdatasync, chociaż buforowanie w Linuksie rozpocznie się po /proc/sys/vm/dirty_writeback_centisecssetnych sekundach (jeśli nie zostanie opóźnione przez inny ruch I / O), a różne inne elementy dostrajające w tym katalogu procfs również wpływają na różne rzeczy (np. Jak duże, aby bufory rosły przed wykonaniem jakiegokolwiek zapisu).
Peter Cordes
2

Czy jest jeszcze bardzo małe okno między wyjściami polecenia a danymi zapisanymi w pliku?

Nie, nie ma.

Chciałbym przeczytać plik zaraz po wyjściu polecenia, ale nie chcę czytać pustego pliku.

Możesz przeczytać ostateczną zawartość pliku zaraz po wyjściu z polecenia, zamiast tego nigdy nie będziesz czytać pustego pliku. (W C i C ++ użyj wywołań systemowych wait , waitpid , wait3 lub wait4 , aby poczekać na zakończenie programu, a dopiero potem przeczytaj plik. Jeśli używasz powłoki, innego języka programowania lub biblioteki (np. Biblioteki C. system wywołania lub klasa Java Process ), prawdopodobnie używa już jednego z tych wywołań systemowych.)

Jak wskazały inne odpowiedzi i komentarze, możesz skończyć z czytaniem pustego pliku po wyjściu z programu, jeśli program zakończył pracę bez opróżniania wewnętrznych buforów wyjściowych (np. Z powodu wyjścia , przerwania lub odebrania krytycznego sygnału lub ponieważ program Java wychodzący normalnie). Jednak w tej chwili nic nie możesz na to poradzić : nierozpoznane dane zostaną utracone na zawsze, dodatkowe czekanie ich nie odzyska.

pkt
źródło
0

tak

Przepraszamy za dodanie kolejnej zbędnej odpowiedzi, ale większość wydaje się skupiać na czerwonym śledziu tytułu pytania. Ale o ile wiem, pytanie wcale nie dotyczy buforowania, ale to:

Kiedy przekieruję wyjście polecenia do pliku (np. Echo Hello> plik), czy plik ten będzie miał gwarancję, że będzie miał takie dane zaraz po wyjściu polecenia?

Tak, bezwarunkowo. Użycie opisywanego słowa „>” wraz z „|” a „<” to oparty na potokach model przetwarzania, na którym opiera się świat Unix i Linux. W każdej instalacji Linuksa znajdziesz setki, jeśli nie tysiące skryptów całkowicie zależnych od tego zachowania.

Działa tak, jak chcesz według projektu, a jeśli byłaby nawet najmniejsza szansa na wyścig, zostałby naprawiony prawdopodobnie kilkadziesiąt lat temu.

AnoE
źródło
Niestety jest to zbyteczne. Tylko kilka odpowiedzi skupia się głównie na rudym śledzeniu przekazywania danych do pamięci trwałej. Zobacz @ pts i kilka innych, aby uzyskać jasny opis: modyfikacja pliku następuje przed wyjściem lub wcale.
Peter Cordes