Często spotykam się z sytuacją podczas programowania, gdzie uruchamiam plik binarny, powiedzmy a.out
w tle, ponieważ wykonuje on trochę długiej pracy. W tym czasie wprowadzam zmiany do kodu C, który utworzył a.out
i a.out
ponownie skompilował . Do tej pory nie miałem z tym żadnych problemów. Proces, który jest uruchomiony, jest a.out
kontynuowany normalnie, nigdy nie ulega awarii i zawsze uruchamia stary kod, od którego został uruchomiony.
Powiedzmy jednak, że a.out
był to ogromny plik, może porównywalny z rozmiarem pamięci RAM. Co by się stało w tym przypadku? I powiedzmy, że jest połączony z plikiem współdzielonego obiektu libblas.so
, a co jeśli zmodyfikowałem libblas.so
podczas działania? Co by się stało?
Moje główne pytanie brzmi - czy system operacyjny gwarantuje, że kiedy uruchomię a.out
, oryginalny kod zawsze będzie działał normalnie, tak jak oryginalny plik binarny, niezależnie od rozmiaru .so
pliku binarnego lub plików, do których prowadzi łącze, nawet jeśli te .o
i .so
pliki są modyfikowane podczas środowisko uruchomieniowe?
Wiem, że są te pytania, które dotyczą podobnych problemów: /programming/8506865/when-a-binary-file-runs-does-it-copy-its-entire-binary-data-into-memory -at-once Co się stanie, jeśli edytujesz skrypt podczas wykonywania? Jak można wykonać aktualizację na żywo, gdy program jest uruchomiony?
Co pomogło mi zrozumieć trochę więcej na ten temat, ale nie sądzę, że pytają dokładnie o to, czego chcę, co jest ogólną zasadą dotyczącą konsekwencji modyfikacji pliku binarnego podczas wykonywania
if they are read-only copies of something already on disc (like an executable, or a shared object file), they just get de-allocated and are reloaded from their source
, więc mam wrażenie, że jeśli twój plik binarny jest ogromny, to jeśli część Twojego pliku binarnego skończy się z pamięci RAM, ale jest potrzebna ponownie, jest „ponownie ładowana ze źródła” - więc wszelkie zmiany.(s)o
plik zostanie uwzględniony w trakcie realizacji. Ale oczywiście mogłem źle zrozumieć - dlatego zadaję to bardziej szczegółowe pytanieNo, it only loads the necessary pages into memory. This is demand paging.
więc miałem wrażenie, że to, o co prosiłem, nie może być zagwarantowane.Odpowiedzi:
Chociaż pytanie Przepełnienie stosu wydawało się na początku wystarczające, rozumiem z twoich komentarzy, dlaczego wciąż możesz mieć co do tego wątpliwości. Dla mnie jest to dokładnie taka sytuacja krytyczna, gdy komunikują się dwa podsystemy UNIX (procesy i pliki).
Jak zapewne wiesz, systemy UNIX są zwykle podzielone na dwa podsystemy: podsystem plików i podsystem procesów. Teraz, o ile nie zostanie wydane inne polecenie przez wywołanie systemowe, jądro nie powinno mieć interakcji między tymi dwoma podsystemami. Jest jednak jeden wyjątek: ładowanie pliku wykonywalnego do regionów tekstowych procesu . Oczywiście można argumentować, że ta operacja jest również wywoływana przez wywołanie systemowe (
execve
), ale zwykle wiadomo, że jest to jedyny przypadek, w którym podsystem procesu wysyła niejawne żądanie do podsystemu plików.Ponieważ podsystem procesu naturalnie nie ma możliwości obsługi plików (w przeciwnym razie nie byłoby sensu dzielenia całej rzeczy na dwie części), musi korzystać z wszystkiego, co zapewnia podsystem plików, aby uzyskać dostęp do plików. Oznacza to również, że podsystem procesu jest poddawany wszelkim pomiarom, jakie podsystem plików podejmuje w odniesieniu do edycji / usuwania pliku. W tym miejscu poleciłbym przeczytanie odpowiedzi Gillesa na to pytanie dotyczące U&L . Reszta mojej odpowiedzi oparta jest na bardziej ogólnej odpowiedzi Gillesa.
Pierwszą rzeczą, na którą należy zwrócić uwagę jest to, że wewnętrznie pliki są dostępne tylko za pośrednictwem i- węzłów . Jeśli jądro otrzymuje ścieżkę, jego pierwszym krokiem będzie przełożenie go na i-węzeł, który będzie używany do wszystkich innych operacji. Kiedy proces ładuje plik wykonywalny do pamięci, robi to przez swój i-węzeł, który został dostarczony przez podsystem plików po przetłumaczeniu ścieżki. I-węzły mogą być powiązane z kilkoma ścieżkami (linkami), a programy mogą usuwać tylko linki. Aby usunąć plik i jego i-węzeł, użytkownik musi usunąć wszystkie istniejące łącza do tego i-węzła i upewnić się, że jest całkowicie nieużywany. Gdy te warunki zostaną spełnione, jądro automatycznie usunie plik z dysku.
Jeśli spojrzysz na część Gilles dotyczącą zastępowania plików wykonywalnych , zobaczysz, że w zależności od tego, jak edytujesz / usuwasz plik, jądro będzie reagować / dostosowywać się inaczej, zawsze poprzez mechanizm zaimplementowany w podsystemie plików.
ETXTBSY
). Bez konsekwencji.mv
operacja jest atomowa. Prawdopodobnie będzie to wymagało użyciarename
wywołania systemowego, a ponieważ procesów nie można przerwać w trybie jądra, nic nie może zakłócać tej operacji, dopóki się nie zakończy (pomyślnie lub nie). Ponownie, nie ma zmian w i-węźle starego pliku: tworzony jest nowy i już działające procesy nie będą o nim wiedziały, nawet jeśli są powiązane z jednym z łączy starego i-węzła.Ponowna
gcc
kompilacja pliku : podczas używania (a zachowanie jest prawdopodobnie podobne w przypadku wielu innych kompilatorów), używasz strategii 2. Możesz to zobaczyć, uruchamiając jedenstrace
z procesów kompilatora:stat
ilstat
.a.out
, jego i-węzeł i zawartość pozostają na dysku, dopóki są używane przez już uruchomione procesy.a.out
. Jest to zupełnie nowy i-węzeł i zupełnie nowe treści, na których już nie działają uruchomione procesy.Teraz, jeśli chodzi o biblioteki współdzielone, zastosowanie będzie miało to samo zachowanie. Dopóki obiekt biblioteki jest używany przez proces, nie zostanie on usunięty z dysku, bez względu na to, jak zmienisz jego łącza. Ilekroć coś musi zostać załadowane do pamięci, jądro zrobi to przez i-węzeł pliku, a zatem zignoruje zmiany, które wprowadziłeś w linkach (takie jak powiązanie ich z nowymi plikami).
źródło
df
do obliczenia liczby wolnych bajtów na dysku jest błędne, ponieważ nie przyjmuje i-węzłów, które czy wszystkie łącza do systemu plików zostały usunięte? Więc powinienem użyćdf -i
? (To tylko techniczna ciekawość, tak naprawdę nie muszę znać dokładnego użycia dysku!)rm
lubmv
on jako i-węzeł oryginalnego pliku nie zostanie usunięty, dopóki wszystkie procesy nie usuną łącza do tego i-węzła.df
zestawie) nie może uzyskać informacji o i-węzle. Wszelkie znalezione nowe informacje dotyczą nowego pliku i nowego i-węzła. Najważniejsze jest to, że podsystem procesu nie jest zainteresowany tym problemem, więc pojęcia zarządzania pamięcią (stronicowanie popytu, zamiana procesów, błędy stron, ...) są całkowicie nieistotne. Jest to problem z podsystemem plików, którym zajmuje się podsystem plików. Podsystem procesu nie przejmuje się tym, nie po to tu jest.df -i
: to narzędzie prawdopodobnie pobiera informacje z superbloku fs lub jego pamięci podręcznej, co oznacza, że może zawierać i-węzeł starego pliku binarnego (dla którego wszystkie łącza zostały usunięte). Nie oznacza to jednak, że nowe procesy mogą swobodnie korzystać ze starych danych.Rozumiem, że z powodu odwzorowania pamięci uruchomionego procesu jądro nie pozwoli na aktualizację zarezerwowanej części zmapowanego pliku. Wydaje mi się, że w przypadku, gdy proces jest uruchomiony, cały jego plik jest zarezerwowany, dlatego jego aktualizacja, ponieważ skompilowana nowa wersja źródła faktycznie powoduje utworzenie nowego zestawu i-węzłów. Krótko mówiąc, starsze wersje plików wykonywalnych pozostają dostępne na dysku poprzez zdarzenia błędu strony. Więc nawet jeśli zaktualizujesz ogromny plik, powinien on pozostać dostępny, a jądro powinno widzieć nietkniętą wersję tak długo, jak proces jest uruchomiony. Pierwotne i-węzły plików nie powinny być ponownie używane, dopóki proces jest uruchomiony.
To oczywiście musi zostać potwierdzone.
źródło
Nie zawsze tak jest w przypadku zastępowania pliku .jar. Zasoby jar i niektóre moduły ładujące klasy środowiska wykonawczego nie są odczytywane z dysku, dopóki program nie zażąda wyraźnie informacji.
Jest to tylko problem, ponieważ jar jest po prostu archiwum, a nie pojedynczym plikiem wykonywalnym, który jest mapowany do pamięci. Jest to nieco off-stopowe, ale wciąż jest odgałęzieniem twojego pytania i czymś, w co postrzeliłem się w stopę.
W przypadku plików wykonywalnych: tak. W przypadku plików jar: może (w zależności od implementacji).
źródło