Pozwól mi to zepsuć.
Po uruchomieniu pliku wykonywalnego wykonywana jest sekwencja wywołań systemowych, w szczególności fork()
i execve()
:
fork()
tworzy proces potomny procesu wywołującego, który jest (przeważnie) dokładną kopią elementu nadrzędnego, przy czym oba nadal uruchamiają ten sam plik wykonywalny (używając stron pamięci kopiowania przy zapisie, więc jest to wydajne). Zwraca dwa razy: W obiekcie nadrzędnym zwraca podrzędny PID. W potomku zwraca 0. Zwykle wywołania procesów potomnych wykonują natychmiast:
execve()
przyjmuje pełną ścieżkę do pliku wykonywalnego jako argument i zastępuje proces wywołujący plikiem wykonywalnym. W tym momencie nowo utworzony proces otrzymuje własną wirtualną przestrzeń adresową, tj. Pamięć wirtualną, a wykonywanie rozpoczyna się w punkcie wejścia (w stanie określonym przez zasady ABI platformy dla nowych procesów).
W tym momencie moduł ładujący ELF jądra zmapował segmenty tekstu i danych pliku wykonywalnego do pamięci, tak jakby używał mmap()
wywołania systemowego (odpowiednio ze współdzielonymi mapami tylko do odczytu i prywatnymi mapowaniami do odczytu i zapisu). BSS jest również odwzorowany tak, jakby zawierał MAP_ANONYMOUS. (BTW, ja ignorując dynamiczne łączenie tutaj dla uproszczenia: Dynamiczne łącznik open()
S i mmap()
wszystko bibliotek dynamicznych przed skokiem do punktu wejścia głównego wykonywalnego).
Tylko kilka stron jest ładowanych do pamięci z dysku, zanim nowo-exec () ed zacznie uruchamiać swój własny kod. Dalsze strony są wczytywane w razie potrzeby, jeśli / kiedy proces dotknie tych części wirtualnej przestrzeni adresowej. (Wstępne ładowanie dowolnych stron kodu lub danych przed rozpoczęciem wykonywania kodu przestrzeni użytkownika to tylko optymalizacja wydajności).
Plik wykonywalny jest identyfikowany przez i-węzeł na niższym poziomie. Po uruchomieniu pliku jądro utrzymuje zawartość pliku nienaruszoną przez odwołanie do i-węzła, a nie przez nazwę pliku, jak w przypadku otwartych deskryptorów plików lub mapowań pamięci opartych na pliku. Dzięki temu możesz łatwo przenieść plik wykonywalny w inne miejsce systemu plików lub nawet w innym systemie plików. Na marginesie, aby sprawdzić różne statystyki procesu, możesz zajrzeć do katalogu /proc/PID
(PID to identyfikator procesu danego procesu). Możesz nawet otworzyć plik wykonywalny, ponieważ /proc/PID/exe
nawet on został odłączony od dysku.
Teraz wykopmy ruch:
Gdy przenosisz plik w tym samym systemie plików, wykonywane jest wywołanie systemowe rename()
, które po prostu zmienia nazwę pliku na inną nazwę, i-węzeł pliku pozostaje taki sam.
Natomiast między dwoma różnymi systemami plików zdarzają się dwie rzeczy:
Zawartość pliku jest najpierw kopiowana do nowej lokalizacji przez read()
iwrite()
Następnie plik jest odłączany od katalogu źródłowego przy użyciu unlink()
i oczywiście plik otrzyma nowy i-węzeł w nowym systemie plików.
rm
to po prostu unlink()
-podanie danego pliku z drzewa katalogów, więc posiadanie uprawnienia do zapisu w katalogu zapewni ci wystarczające prawo do usunięcia dowolnego pliku z tego katalogu.
Teraz dla zabawy, wyobraź sobie, co się dzieje, gdy przenosisz pliki między dwoma systemami plików i nie masz uprawnień do unlink()
pliku ze źródła?
Plik zostanie najpierw skopiowany do miejsca docelowego ( read()
, write()
), a następnie unlink()
zakończy się niepowodzeniem z powodu niewystarczających uprawnień. Tak więc plik pozostanie w obu systemach plików !!