Průběžné vydávání pak nahradí /apps/EXE zbrusu novým spustitelným souborem.
Toto je důležitá část.
Nový soubor je uvolněn vytvořením nového souboru (např. /apps/EXE.tmp.20190907080000
), psaní obsahu, nastavení oprávnění a vlastnictví a konečně přejmenujte (2) na konečný název /apps/EXE
, který nahradí starý soubor.
Výsledkem je, že nový soubor má nové číslo inodu (což ve skutečnosti znamená, že jde o jiný soubor.)
A starý soubor měl své vlastní číslo inodu, které ve skutečnosti stále existuje i když název souboru již na něj neukazuje (nebo na tento inode již žádné názvy souborů neukazují.)
Klíčem je tedy to, že když mluvíme o „souborech“ v Linuxu, nejčastěji skutečně mluvíme o „inodech“, protože jakmile je soubor otevřen, inode je odkaz, který k souboru uchováváme.
Předpoklad 1 :Předpokládám, že proces P (a kdokoli jiný s deskriptorem souboru odkazujícím na starý spustitelný soubor) bude i nadále bez problémů používat starý v paměti /apps/EXE a každý nový proces, který se pokusí provést tuto cestu, získá nový spustitelný.
Správně.
Předpoklad 2 :Předpokládám, že pokud nejsou všechny stránky souboru namapovány do paměti, bude to v pořádku, dokud nedojde k chybě stránky vyžadující stránky ze souboru, které byly nahrazeny, a pravděpodobně dojde k segfaultu?
Nesprávný. Starý inode je stále k dispozici, takže chyby stránek z procesu používajícího starou binární hodnotu budou stále moci najít tyto stránky na disku.
Některé efekty toho můžete vidět, když se podíváte na /proc/${pid}/exe
symbolický odkaz (nebo ekvivalentně lsof
output) pro proces běžící starý binární soubor, který bude ukazovat /app/EXE (deleted)
pro označení, že jméno už tam není, ale inode je stále kolem.
Můžete také vidět, že diskový prostor používaný binárním souborem bude uvolněn až poté, co proces zemře (za předpokladu, že je to jediný proces s tímto otevřeným inodem.) Zkontrolujte výstup df
před a po zabití procesu uvidíte, jak poklesne o velikost té staré binárky, o které jste si mysleli, že už neexistuje.
BTW, není to jen u binárních souborů, ale u všech otevřených souborů. Pokud otevřete soubor v procesu a odstraníte soubor, bude soubor uchován na disku, dokud tento proces soubor nezavře (nebo neukončí). ovladač souborového systému (v linuxovém jádře) udržuje počítadlo, kolik odkazů na tento inode existuje v paměti a uvolní inode z disku až poté, co budou uvolněny i všechny reference z běžícího systému.
Otázka 1 :Pokud zamknete všechny stránky souboru něčím jako vmtouch, změní to scénář
Tato otázka je založena na nesprávném předpokladu 2, že nezamykání stránek způsobí segfaulty. Nebude.
Otázka 2 :Pokud je /apps/EXE na vzdáleném NFS, bude to nějaký rozdíl? (Předpokládám, že ne)
Je to myšleno fungovat stejně a většinou to tak funguje, ale s NFS existují určité problémy.
Někdy můžete vidět artefakty smazání souboru, který je stále otevřený v NFS (zobrazuje se jako skrytý soubor v tomto adresáři.)
Máte také nějaký způsob, jak přiřadit čísla zařízení k exportům NFS, abyste se ujistili, že nedojde k „přeházení“ při restartu serveru NFS.
Ale hlavní myšlenka je stejná. Ovladač klienta NFS stále používá inody a bude se snažit uchovávat soubory (na serveru), dokud je inode stále odkazováno.
Předpoklad 2:Předpokládám, že pokud nejsou všechny stránky souboru namapovány do paměti, bude vše v pořádku, dokud nedojde k chybě stránky vyžadující stránky ze souboru, které byly nahrazeny, a pravděpodobně dojde k segfaultu?
Ne, to se nestane, protože jádro vám nedovolí otevřít pro zápis nebo nahradit cokoli uvnitř souboru, který je právě spuštěn. Taková akce se nezdaří s ETXTBSY
[1] :
cp /bin/sleep sleep; ./sleep 3600 & echo none > ./sleep
[9] 5332
bash: ./sleep: Text file busy
Když dpkg atd. aktualizuje binární soubor, nepřepíše ho, ale použije rename(2)
který jednoduše nasměruje položku adresáře na úplně jiný soubor a všechny procesy, které stále mají mapování nebo otevřené popisovače na starý soubor, jej budou bez problémů nadále používat.
[1] ETXBUSY
ochrana není rozšířena na další soubory, které lze rovněž považovat za „textové“ (=živý kód / spustitelný soubor):sdílené knihovny, třídy Java atd.; úprava takového souboru při mapování jiným procesem bude způsobit zhroucení procesu. Na linuxu dynamický linker poslušně předává MAP_DENYWRITE
příznak na mmap(2)
, ale nenechte se mýlit - nemá to žádný účinek. Příklad:
$ cc -xc - <<<'void lib(){}' -shared -o lib.so
$ cc -Wl,-rpath=. lib.so -include unistd.h -xc - <<<'
extern void lib();
int main(){ truncate("lib.so", 0); lib(); }
'
./a.out
Bus error
filbrandenova odpověď je správná za předpokladu, že proces nepřetržitého vydávání provádí správnou atomovou náhradu souborů pomocí rename
. Pokud ne, ale upraví soubor na místě, věci jsou jiné. Váš mentální model se však stále mýlí.
Neexistuje žádná možnost, že by se věci na disku upravovaly a byly nekonzistentní s mezipamětí stránek, protože mezipaměť stránky je kanonická verze a ten, který je upravený. Jakékoli zápisy do souboru probíhají prostřednictvím mezipaměti stránky. Pokud tam již je, stávající stránky se upraví. Pokud ještě není přítomna, pokusy o úpravu části stránky způsobí uložení celé stránky do mezipaměti a následnou úpravu, jako by již byla uložena do mezipaměti. Zápisy, které zabírají celou stránku nebo více, mohou (a téměř jistě to dělají) optimalizovat krok čtení a jejich stránkování. V každém případě existuje pouze jedna kanonická modifikovatelná verze souboru (*), ta v mezipaměti stránky. .
(*) Trochu jsem lhal. Pro NFS a další vzdálené souborové systémy může být více než jeden a obvykle (v závislosti na tom, který z nich a jaké možnosti připojení a na straně serveru jsou použity) neimplementují správně atomicitu a sémantiku řazení pro zápisy. To je důvod, proč je mnoho z nás považuje za zásadně nefunkční a odmítá je používat v situacích, kdy budou souběžně s používáním docházet k zápisům.