Co się stanie, gdy plik w 100% umieszczony w pamięci podręcznej strony zostanie zmodyfikowany przez inny proces

14

Wiem, że gdy strona pamięci podręcznej jest modyfikowana, jest oznaczona jako brudna i wymaga zapisu zwrotnego, ale co się dzieje, gdy:

Scenariusz: plik / aplikacje / EXE, który jest plikiem wykonywalnym, jest całkowicie umieszczany w pamięci podręcznej stron (wszystkie strony znajdują się w pamięci podręcznej / pamięci) i jest wykonywany przez proces P

Wydanie ciągłe zastępuje następnie / apps / EXE nowym plikiem wykonywalnym.

Założenie 1: Zakładam, że proces P (i każdy inny posiadający deskryptor pliku odnoszący się do starego pliku wykonywalnego) będzie nadal używał starego, w pamięci / aplikacjach / EXE bez problemu, i każdy nowy proces, który próbuje wykonać tę ścieżkę, otrzyma nowy plik wykonywalny.

Założenie 2: Zakładam, że jeśli nie wszystkie strony pliku zostaną zmapowane w pamięci, wszystko będzie w porządku, dopóki nie wystąpi błąd strony wymagający stron z pliku, który został zastąpiony, i prawdopodobnie wystąpi awaria?

Pytanie 1: Jeśli zablokujesz wszystkie strony pliku za pomocą czegoś takiego jak vmtouch, czy w ogóle zmieni to scenariusz?

Pytanie 2: Jeśli / apps / EXE znajduje się na zdalnym NFS, czy to coś zmieni? (Zakładam, że nie)

Popraw lub zweryfikuj moje 2 założenia i odpowiedz na 2 moje pytania.

Załóżmy, że jest to okno CentOS 7.6 z jakimś rodzajem jądra 3.10.0-957.el7

Aktualizacja: Zastanawiając się nad tym, zastanawiam się, czy ten scenariusz nie różni się od żadnego innego scenariusza z brudną stroną.

Podejrzewam, że proces, który zapisuje nowy plik binarny, wykona odczyt i pobierze wszystkie strony pamięci podręcznej, ponieważ są one stronicowane, a następnie wszystkie te strony zostaną oznaczone jako brudne. Jeśli zostaną zablokowane, będą po prostu bezużytecznymi stronami zajmującymi pamięć podstawową, gdy liczba odniesień spadnie do zera.

Podejrzewam, że po zakończeniu aktualnie wykonywanych programów wszystko inne wykorzysta nowy plik binarny. Zakładając, że wszystko jest w porządku, myślę, że jest to interesujące tylko wtedy, gdy tylko część pliku jest stronicowana.

Gregg Leventhal
źródło
Aby to wyjaśnić, zastąpienie pliku nie będzie dużą sprawą (w zależności od tego, czy aplikacja zostanie ponownie otwarta i jak aplikacja reaguje na zmodyfikowaną zawartość), ale modyfikowanie plików mmaped może spowodować awarię aplikacji (jest to częsty problem w świecie Java, gdy zmieniany jest plik zip z pozycją katalogu w kształcie mm). Zależy to jednak od platformy, nie ma gwarancji, że regiony w kształcie mm zobaczą zmianę, czy nie.
eckes

Odpowiedzi:

12

Wydanie ciągłe zastępuje następnie / apps / EXE nowym plikiem wykonywalnym.

To jest ważna część.

Sposób, w jaki nowy plik jest wydawany, polega na utworzeniu nowego pliku (np. /apps/EXE.tmp.20190907080000), Zapisaniu zawartości, ustawieniu uprawnień i własności, a na końcu zmianie nazwy (2) na ostateczną nazwę/apps/EXE , zastępując stary plik.

W rezultacie nowy plik ma nowy numer i-węzła (co w efekcie oznacza, że ​​jest to inny plik).

Stary plik miał swój własny numer i-węzła, który jest nadal obecny, mimo że nazwa pliku już go nie wskazuje (lub nie ma już nazw plików wskazujących na ten i-węzeł).

Kluczem jest tutaj to, że kiedy mówimy o „plikach” w Linuksie, najczęściej tak naprawdę mówimy o „i-węzłach”, ponieważ po otwarciu pliku i-węzeł jest odniesieniem do pliku.

Założenie 1 : Zakładam, że proces P (i każdy inny posiadający deskryptor pliku odnoszący się do starego pliku wykonywalnego) będzie nadal używał starego, w pamięci / aplikacjach / EXE bez problemu, i każdy nowy proces, który próbuje wykonać tę ścieżkę, otrzyma nowy plik wykonywalny.

Poprawny.

Założenie 2 : Zakładam, że jeśli nie wszystkie strony pliku zostaną zamapowane w pamięci, wszystko będzie w porządku, dopóki nie wystąpi błąd strony wymagający stron z pliku, które zostały zastąpione i prawdopodobnie wystąpi awaria?

Błędny. Stary i-węzeł jest nadal obecny, więc błędy strony w procesie używającym starego pliku binarnego nadal będą w stanie znaleźć te strony na dysku.

Możesz zobaczyć niektóre z tego efektów, patrząc na /proc/${pid}/exedowiązanie symboliczne (lub równoważnie lsofdane wyjściowe) dla procesu uruchamiającego stary plik binarny, który pokaże, /app/EXE (deleted)aby wskazać, że nazwa już nie istnieje, ale i-węzeł jest nadal obecny.

Możesz także zobaczyć, że przestrzeń dyskowa używana przez plik binarny zostanie zwolniona dopiero po śmierci procesu (zakładając, że jest to jedyny proces z otwartą i-węzłem). Sprawdź wyjście dfprzed i po zabiciu procesu, zobaczysz, że zmniejsza się o rozmiar tego starego pliku binarnego, o którym myślałeś, że już go nie ma.

BTW, nie dotyczy to tylko plików binarnych, ale także dowolnych otwartych plików. Jeśli otworzysz plik w procesie i usuniesz plik, plik będzie przechowywany na dysku, dopóki ten proces nie zamknie pliku (lub nie zginie). Podobnie jak w przypadku twardych linków licznik liczby nazw wskazuje i-węzeł na dysku, Sterownik systemu plików (w jądrze Linuksa) przechowuje licznik liczby odwołań do tego i-węzła w pamięci i zwalnia i-węzeł z dysku dopiero po wydaniu wszystkich odwołań z działającego systemu.

Pytanie 1 : Jeśli zablokujesz wszystkie strony pliku za pomocą czegoś takiego jak vmtouch, czy to zmieni scenariusz

To pytanie opiera się na niepoprawnym założeniu 2, że niezablokowanie stron spowoduje segfault. Nie będzie.

Pytanie 2 : Jeśli / apps / EXE znajduje się na zdalnym NFS, czy to coś zmieni? (Zakładam, że nie)

Ma to działać w ten sam sposób i przez większość czasu, ale jest kilka „problemów” z NFS.

Czasami można zobaczyć artefakty usuwania pliku, który jest nadal otwarty w NFS (pojawia się jako ukryty plik w tym katalogu).

Istnieje również sposób przypisania numerów urządzeń do eksportu NFS, aby upewnić się, że nie zostaną one „przetasowane” po ponownym uruchomieniu serwera NFS.

Ale główna idea jest taka sama. Sterownik klienta NFS nadal używa i-węzłów i będzie próbował utrzymywać pliki w pobliżu (na serwerze), podczas gdy i-węzeł będzie nadal przywoływany.

filbranden
źródło
1
Czy zmiana nazwy (2) blokuje się, dopóki liczba odwołań do pliku starej nazwy nie spadnie do zera?
Gregg Leventhal,
2
Nie, zmiana nazwy (2) nie zostanie zablokowana. Stary i-węzeł jest przechowywany potencjalnie przez bardzo długi czas.
filbranden
1
Zobacz odpowiedź @ mosvy, dlaczego nie możesz zapisać do wykonywanego pliku (dostajesz ETXTBSY). Odłączanie i tworzenie nowego ma taki sam efekt jak zmiana nazwy: w końcu powstaje nowy i-węzeł. (Zmiana nazwy jest lepsza, ponieważ wtedy nie ma momentu, w którym nazwa pliku nie istnieje, jest to operacja atomowa zastępująca nazwę w celu wskazania nowego i-węzła.)
filbranden
4
@GreggLeventhal: „Jakie są twoje założenia dotyczące ciągłego procesu wydawania, z którego korzystam, co daje pewność, że używa plików tymczasowych?” - Ponieważ tak długo, jak istnieje Unix, jest to jedyny rozsądny sposób, aby to zrobić. renamejest dość dużo tylko plików i plików operacja, która jest gwarancją atomowy (zakładając, że nie przekraczają granice systemu plików lub urządzeń), więc „tworzenie pliku tymczasowego, a następnie rename” jest standardowy wzór do aktualizacji plików. Tego też używa na przykład każdy edytor tekstu w Uniksie.
Jörg W Mittag
1
@ grahamj42: renamejest częścią POSIX. To prawda, że ​​jest uwzględniony przez odniesienie do ISO C (sekcja 7.21.4.2 w bieżącym projekcie), ale tam jest.
Jörg W Mittag
7

Założenie 2: Zakładam, że jeśli nie wszystkie strony pliku zostaną zmapowane w pamięci, wszystko będzie w porządku, dopóki nie wystąpi błąd strony wymagający stron z pliku, który został zastąpiony, i prawdopodobnie wystąpi awaria?

Nie, tak się nie stanie, ponieważ jądro nie pozwoli ci otworzyć do napisania zamiany czegokolwiek w aktualnie wykonywanym pliku. Takie działanie zakończy się niepowodzeniem w przypadku ETXTBSY[1]:

cp /bin/sleep sleep; ./sleep 3600 & echo none > ./sleep
[9] 5332
bash: ./sleep: Text file busy

Kiedy dpkg itp. Aktualizuje plik binarny, nie zastępuje go, ale używa, rename(2)co po prostu wskazuje pozycję katalogu na zupełnie inny plik, a wszelkie procesy, które nadal mają mapowania lub otwarte uchwyty do starego pliku, będą go nadal używać bez problemów .

[1] taka ochrona nie jest rozszerzona na inne pliki, które można również uznać za „tekst” (kod na żywo / plik wykonywalny): biblioteki współużytkowane, klasy java itp .; modyfikacji takiego pliku podczas odwzorowywane przez inny proces będzie spowodować jego awarię. W Linuksie dynamiczny linker słusznie przekazuje MAP_DENYWRITEflagę mmap(2), ale nie popełnij błędu - nie ma to żadnego wpływu.

mosvy
źródło
1
W scenariuszu dpkg, w którym momencie następuje zmiana nazwy, tak że dentry dla / apps / EXE odwoła się do i-węzła nowego pliku binarnego? Kiedy nie ma już żadnych odniesień do starego? Jak to działa?
Gregg Leventhal,
2
rename(2)jest atomowy; jak tylko się zakończy, pozycja dir odnosi się do nowego pliku. Procesy, które w tym momencie nadal używały starego pliku, byłyby w stanie uzyskać do niego dostęp tylko za pomocą istniejących mapowań lub otwartych uchwytów (które mogą odnosić się do sierocej dentystyki, niedostępnej inaczej niż przez /proc/PID/fd).
mosvy
1
Najbardziej podoba mi się twoja odpowiedź, ponieważ twoja wzmianka o ETXTBSY doprowadziła mnie do tego utcc.utoronto.ca/~cks/space/blog/unix/WhyTextFileBusyError, który odpowiada na wszystkie moje pytania.
Gregg Leventhal,
4

odpowiedź filbranden jest poprawna, zakładając, że proces ciągłego wydawania dokonuje prawidłowej atomowej wymiany plików za pośrednictwem rename. Jeśli nie, ale modyfikuje plik w miejscu, sprawy wyglądają inaczej. Jednak twój model mentalny jest nadal w błędzie.

Nie ma możliwości modyfikowania rzeczy na dysku i niespójności z pamięcią podręczną strony, ponieważ pamięć podręczna strony jest wersją kanoniczną i zmodyfikowaną. Wszelkie zapisy do pliku odbywają się przez pamięć podręczną strony. Jeśli już tam jest, istniejące strony są modyfikowane. Jeśli nie jest jeszcze obecny, próby modyfikacji częściowej strony spowodują buforowanie całej strony, a następnie modyfikację tak, jakby była już buforowana. Pisze, że obejmuje całą stronę lub więcej, może (i prawie na pewno tak) zoptymalizować krok odczytu stronicując je. W każdym razie istnieje tylko jedna kanoniczna modyfikowalna wersja pliku (*), jedna w pamięci podręcznej strony .

(*) Lekko skłamałem. W przypadku NFS i innych zdalnych systemów plików może być ich więcej niż jeden i zazwyczaj (w zależności od tego, który z nich jest używany i jakie opcje montowania i po stronie serwera są używane) niepoprawnie implementują atomowość i porządkowanie semantyki zapisu. Dlatego wielu z nas uważa, że ​​są one zasadniczo zepsute i nie chcą ich używać w sytuacjach, w których będą pisane równolegle z użyciem.

R .. GitHub ZATRZYMAJ LÓD
źródło