Czy pliki są otwierane przez procesy ładowane do pamięci RAM?

24

Komendy , na przykład sed, są programami, a programy są zakodowaną logiką w pliku i pliki te znajdują się gdzieś na dysku twardym. Jednak podczas wykonywania poleceń kopia ich plików z dysku twardego jest umieszczana w pamięci RAM , gdzie ożywają i mogą wykonywać różne czynności oraz są nazywane procesami .

Procesy mogą wykorzystywać inne pliki, odczytywać je lub zapisywać, a jeśli tak, to pliki te nazywane są plikami otwartymi. Jest to polecenie, aby wyświetlić listę wszystkich otwartych plików przez wszystkich uruchomionych procesów: lsof.

OK, więc zastanawiam się, czy podwójne życie polecenia, jednego na dysku twardym, drugiego w pamięci RAM, dotyczy również innych rodzajów plików, na przykład tych, które nie mają zaprogramowanej logiki, ale są po prostu pojemnikami na dane.

Zakładam, że pliki otwierane przez procesy są również ładowane do pamięci RAM. Nie wiem, czy to prawda, to tylko intuicja.

Czy ktoś mógłby to zrozumieć?

rekin
źródło

Odpowiedzi:

27

Jednak podczas wykonywania poleceń kopia ich plików z dysku twardego jest umieszczana w pamięci RAM,

To jest złe (ogólnie). Kiedy program jest wykonywany (przez execve (2) ...), proces (uruchamiający ten program) zmienia swoją wirtualną przestrzeń adresową i jądro ponownie konfiguruje MMU w tym celu. Przeczytaj także o pamięci wirtualnej . Zauważ, że aplikacje mogą zmieniać swoją wirtualną przestrzeń adresową za pomocą mmap (2) i munmap& mprotect (2) , również używanych przez dynamiczny linker (patrz ld-linux (8) ). Zobacz także madvise (2) i posix_fadvise (2) i mlock (2) .

Błędy przyszłej strony będą przetwarzane przez jądro w celu ładowania (leniwie) stron z pliku wykonywalnego. Przeczytaj także o thrashingu .

Jądro utrzymuje dużą pamięć podręczną stron . Przeczytaj także o kopiowaniu przy zapisie . Zobacz także readahead (2) .

OK, więc zastanawiam się, czy podwójne życie polecenia, jednego na dysku twardym, drugiego w pamięci RAM, dotyczy również innych rodzajów plików, na przykład tych, które nie mają zaprogramowanej logiki, ale są po prostu pojemnikami na dane.

W przypadku wywołań systemowych, takich jak read (2) i write (2), używana jest również pamięć podręczna stron. Jeśli dane, które mają zostać odczytane, znajdują się w nim, nie zostanie wykonane IO dysku. Jeśli potrzebne jest IO dysku, odczytane dane najprawdopodobniej zostaną umieszczone w pamięci podręcznej strony. Tak więc w praktyce, jeśli uruchomisz to samo polecenie dwa razy, może się zdarzyć, że po raz drugi na dysku nie zostaną wykonane fizyczne operacje we / wy (jeśli masz stary obrotowy dysk twardy - nie dysk SSD), możesz to usłyszeć; lub uważnie obserwuj diodę dysku twardego).

Polecam lekturę książki takiej jak Systemy operacyjne: trzy łatwe utwory (do pobrania za darmo, jeden plik PDF na rozdział), która wyjaśnia to wszystko.

Zobacz także Linux Ate My RAM i wykonywania poleceń jak xosview, top, htoplub cat /proc/self/mapslub cat /proc/$$/maps(patrz proc (5) ).

PS. Skupiam się na Linuksie, ale inne systemy operacyjne mają również pamięć wirtualną i pamięć podręczną stron.

Basile Starynkevitch
źródło
35

Nie, plik nie jest automatycznie wczytywany do pamięci przez jego otwarcie. To byłoby okropnie nieefektywne. sed, na przykład, czyta swoje wejście wiersz po wierszu, podobnie jak wiele innych narzędzi uniksowych. Rzadko musi utrzymywać w pamięci więcej niż bieżący wiersz.

Z awktym samym. Odczytuje rekord naraz, który domyślnie jest linią. Jeśli przechowujesz części danych wejściowych w zmiennych, będzie to dodatkowe, oczywiście 1 .

Niektórzy ludzie mają zwyczaj robienia takich rzeczy

for line in $(cat file); do ...; done

Ponieważ powłoka będzie musiała $(cat file)całkowicie rozszerzyć podstawianie poleceń przed uruchomieniem nawet pierwszej iteracji forpętli, spowoduje to odczytanie całej filepamięci (do pamięci używanej przez powłokę wykonującą forpętlę). To jest trochę głupie, a także nieeleganckie. Zamiast tego należy zrobić

while IFS= read -r line; do ...; done <file

Spowoduje to przetworzenie filelinia po linii (ale należy przeczytać Zrozumienie „IFS = czytaj -r linia” ).

Przetwarzanie plików wiersz po wierszu w powłoce jest jednak rzadko potrzebne, ponieważ większość narzędzi i tak jest zorientowana liniowo (zobacz Dlaczego używanie pętli powłoki do przetwarzania tekstu jest uważane za złą praktykę? ).

Pracuję w bioinformatyce, a kiedy przetwarzam ogromne ilości danych genomowych, nie byłbym w stanie wiele zrobić, chyba że zachowałem tylko te fragmenty danych, które były absolutnie niezbędne w pamięci. Na przykład, gdy muszę usunąć fragmenty danych, które mogłyby zostać wykorzystane do identyfikacji osób z 1-terabajtowego zestawu danych zawierającego warianty DNA w pliku VCF (ponieważ tego typu danych nie można upublicznić), wykonuję wiersz po wierszu przetwarzanie za pomocą prostego awkprogramu (jest to możliwe, ponieważ format VCF jest zorientowany liniowo). I nie odczytać pliku do pamięci, przetworzyć go tam, i ponownie je zapisze się ponownie! Gdyby plik został skompresowany, przekazałbym go zcatlub gzip -d -c, ponieważ ponieważ gzipprzetwarza przetwarzanie danych w strumieniu, również nie odczytałby całego pliku do pamięci.

Nawet w przypadku formatów plików, które nie są zorientowane liniowo, takich jak JSON lub XML, istnieją parsery strumieniowe, które umożliwiają przetwarzanie dużych plików bez przechowywania ich w pamięci RAM.

W przypadku plików wykonywalnych jest to nieco bardziej skomplikowane, ponieważ biblioteki współdzielone mogą być ładowane na żądanie i / lub dzielone między procesami ( na przykład Ładowanie bibliotek współdzielonych i użycie pamięci RAM ).

Buforowanie to coś, o czym tu nie wspomniałem. Jest to działanie polegające na użyciu pamięci RAM do przechowywania często używanych fragmentów danych. Mniejsze pliki (na przykład pliki wykonywalne) mogą być buforowane przez system operacyjny w nadziei, że użytkownik dokona wielu odwołań do nich. Oprócz pierwszego odczytu pliku, kolejne dostępy będą miały miejsce w pamięci RAM, a nie na dysku. Buforowanie, podobnie jak buforowanie danych wejściowych i wyjściowych, jest zwykle w dużej mierze przezroczyste dla użytkownika, a ilość pamięci używanej do buforowania rzeczy może się dynamicznie zmieniać w zależności od ilości pamięci RAM przydzielonej przez aplikacje itp.


1 Technicznie rzecz biorąc, większość programów prawdopodobnie odczytuje fragment danych wejściowych naraz, albo za pomocą buforowania jawnego, albo pośrednio przez buforowanie, które robią standardowe biblioteki we / wy, a następnie przedstawia ten fragment linia po linii do kodu użytkownika. O wiele bardziej efektywne jest odczytywanie wielokrotności rozmiaru bloku dysku niż np. Znak na raz. Jednak ten rozmiar fragmentu rzadko jest większy niż garść kilobajtów.

Kusalananda
źródło
powiedzieliście, że możliwe jest ładowanie bibliotek współdzielonych do pamięci RAM, czy możliwe jest również ładowanie zwykłego pliku, który zawiera tylko dane do pamięci RAM, nawet jeśli nie miałoby to sensu?
sharkant
1
@sharkant Oczywiście. To tylko kwestia dodania danych do zmiennej (lub tablicy, skrótu lub innej struktury danych dostarczanej przez dany język), dopóki cały plik nie zostanie zapisany. Dzięki awk, { a[i++] = $0 }by dodać wszystkie linie pliku wejściowego do tablicy a. Możesz także sprawdzić funkcję C mmap(), ale jej użycie może być nieco nie na temat.
Kusalananda
6
sed, awki inne programy zorientowane liniowo nie odczytują linii do pamięci na raz, ponieważ zwykłe pliki tekstowe nie zawierają indeksu linii, a interfejsy API systemu plików i sprzęt pamięci masowej niskiego poziomu odczytują jeden lub więcej „sektorów” (zwykle 512 lub 1024 bajty) na raz. Byłbym zaskoczony, gdyby system operacyjny wczytał mniej niż 8 KB do pamięci przed przetworzeniem pierwszego wiersza.
Russell Borogove
5
Chociaż narzędzie takie jak sedodczytuje do pamięci tylko jedną linię na raz, warto wspomnieć, że system operacyjny użyje wolnego pamięci RAM do buforowania plików, aby umożliwić szybki dostęp do nich. Jeśli używasz sedmniejszego pliku, możliwe jest, że system operacyjny zbuforuje cały plik w pamięci, a operacja zostanie wykonana całkowicie w pamięci RAM. Zobacz: en.wikipedia.org/wiki/Page_cache
Sean Dawson
5
@sharkant Przydatne jest posiadanie pliku całkowicie dostępnego w pamięci (patrz inna odpowiedź, mmap to słowo kluczowe wywołanie systemowe tutaj). Na przykład system bazy danych zwykle chciałby mieć, dla łatwości i szybkości dostępu, całą bazę danych lub przynajmniej niektóre wskaźniki odwzorowane w pamięci. Nie musi to wcale oznaczać, że cała rzecz jest rzeczywiście w pamięci. System operacyjny może „udawać”, że plik jest w pamięci. Mówi aplikacji „tutaj, w tym zakresie pamięci jest twój plik” i tylko po zakończeniu odczytu (tak jak w przypadku zamiany procesu) dane są w rzeczywistości odczytywane.
Jonas Schäfer
5

Nie. Podczas gdy występy pamięci RAM są fantastyczne, był czas, kiedy pamięć RAM była bardzo ograniczonym zasobem (nauczyłem się programowania na VAX 11/750 z 2 MB pamięci RAM), a jedyną rzeczą w pamięci RAM była aktywna strona wykonywalna i strony danych aktywnych procesów i danych plików znajdujących się w pamięci podręcznej bufora.
Pamięć podręczna bufora została opróżniona, a strony danych zostały zamienione. I często czasami. Strony wykonywalne tylko do odczytu zostały nadpisane, a tablice stron zaznaczone, więc jeśli program dotknie tych stron ponownie, zostaną umieszczone w systemie plików. Dane zostały przywołane z wymiany. Jak wspomniano powyżej, biblioteka STDIO pobierała dane w blokach i były uzyskiwane przez program w razie potrzeby: fgetc, fgets, fread itp. Przy pomocy mmap plik można mapować na przestrzeń adresową procesu, tak jak to robi się z współdzielone obiekty biblioteki lub nawet zwykłe pliki. Tak, możesz mieć pewien stopień kontroli, jeśli jest w pamięci RAM lub nie (mlock), ale idzie tylko tak daleko (patrz sekcja kodu błędu w mlock).

Roger L.
źródło
1
Stwierdzenie „twoja pamięć RAM będzie zbyt mała dla twoich plików” jest teraz prawdziwe, tak jak było w dawnych czasach VAX.
Federico Poloni
1
@Federico_Poloni Nie do końca tak dzisiaj. U mojego ostatniego pracodawcy mieliśmy komputer klasy stacji roboczej z 1 TB pamięci RAM i zaledwie 0,5 TB dysku twardego. (Klasa problemu: małe dane wejściowe, średnie dane wyjściowe, duże losowo dostępne tablice podczas obliczeń).
nigel222