Ogólne zastępowanie plików
Po pierwsze, istnieje kilka strategii zastępowania pliku:
Otwórz istniejący plik do zapisu, skróć go do 0 długości i napisz nową zawartość. (Mniej powszechnym wariantem jest otwarcie istniejącego pliku, zastąpienie starej treści nową treścią, obcięcie pliku do nowej długości, jeśli jest krótszy.)
echo 'new content' >somefile
Usuń stary plik i utwórz nowy plik o tej samej nazwie. W skrócie:
rm somefile
echo 'new content' >somefile
Napisz do nowego pliku pod tymczasową nazwą, a następnie przenieś nowy plik do istniejącej nazwy. Przeniesienie usuwa stary plik. W skrócie:
echo 'new content' >somefile.new
mv somefile.new somefile
Nie wymienię wszystkich różnic między strategiami, wymienię tylko niektóre z nich, które są tutaj ważne. W przypadku strategii 1, jeśli jakikolwiek proces aktualnie korzysta z pliku, proces widzi nową zawartość w trakcie aktualizacji. Może to powodować pewne zamieszanie, jeśli proces oczekuje, że zawartość pliku pozostanie taka sama. Należy pamiętać, że dotyczy to tylko procesów, w których plik jest otwarty (jak widać w lsof
lub w ; aplikacje interaktywne, które mają otwarty dokument (np. Otwieranie pliku w edytorze) zwykle nie utrzymują pliku otwartego, ładują zawartość pliku podczas Operacja „otwórz dokument” i zastępują plik (przy użyciu jednej z powyższych strategii) podczas operacji „zapisuj dokument”./proc/PID/fd/
W przypadku strategii 2 i 3, jeśli jakiś proces ma somefile
otwarty plik , stary plik pozostaje otwarty podczas aktualizacji zawartości. W przypadku strategii 2 krok usuwania pliku w rzeczywistości usuwa tylko pozycję pliku w katalogu. Sam plik jest usuwany tylko wtedy, gdy nie prowadzi do niego pozycja katalogu (w typowych systemach plików Unix może istnieć więcej niż jedna pozycja katalogu dla tego samego pliku ) i żaden proces nie ma otwartego. Oto sposób, aby to zaobserwować - plik jest usuwany tylko po sleep
zabiciu procesu ( rm
usuwa tylko pozycję katalogu).
echo 'old content' >somefile
sleep 9999999 <somefile &
df .
rm somefile
df .
cat /proc/$!/fd/0
kill $!
df .
W strategii 3 krok przeniesienia nowego pliku do istniejącej nazwy usuwa pozycję katalogu prowadzącą do starej treści i tworzy pozycję katalogu prowadzącą do nowej treści. Odbywa się to w jednej operacji atomowej, więc ta strategia ma główną zaletę: jeśli proces otworzy plik w dowolnym momencie, zobaczy albo starą lub nową zawartość - nie ma ryzyka, że zawartość zostanie zmieszana lub plik nie będzie istniejący.
Zastępowanie plików wykonywalnych
Jeśli wypróbujesz strategię 1 z działającym plikiem wykonywalnym w systemie Linux, pojawi się błąd.
cp /bin/sleep .
./sleep 999999 &
echo oops >|sleep
bash: sleep: Text file busy
„Plik tekstowy” oznacza plik zawierający kod wykonywalny z niejasnych powodów historycznych . Linux, podobnie jak wiele innych wariantów uniksowych, odmawia zastąpienia kodu działającego programu; na to pozwala kilka wariantów uniksowych, co prowadzi do awarii, chyba że nowy kod był bardzo przemyślaną modyfikacją starego kodu.
W systemie Linux można zastąpić kod biblioteki dynamicznie ładowanej. Prawdopodobnie doprowadzi to do awarii używającego go programu. (Możesz nie być w stanie tego zaobserwować, sleep
ponieważ ładuje cały kod biblioteki, którego potrzebuje, gdy się uruchamia. Wypróbuj bardziej złożony program, który robi coś pożytecznego po spaniu, jak perl -e 'sleep 9; print lc $ARGV[0]'
.)
Jeśli interpreter uruchamia skrypt, plik skryptu jest otwierany przez interpretera w zwykły sposób, więc nie ma ochrony przed zastąpieniem skryptu. Niektórzy tłumacze czytają i analizują cały skrypt przed rozpoczęciem wykonywania pierwszego wiersza, inni czytają skrypt w razie potrzeby. Zobacz Co się stanie, jeśli edytujesz skrypt podczas wykonywania? i jak Linux radzi sobie ze skryptami powłoki? po więcej szczegółów.
Strategie 2 i 3 są również bezpieczne dla plików wykonywalnych: chociaż uruchamianie plików wykonywalnych (i bibliotek ładowanych dynamicznie) nie jest plikami otwartymi w sensie posiadania deskryptora plików, zachowują się w bardzo podobny sposób. Tak długo, jak jakiś program uruchamia kod, plik pozostaje na dysku, nawet bez wpisu katalogu.
Aktualizowanie aplikacji
Większość menedżerów pakietów używa strategii 3 do zastępowania plików, ze względu na główną zaletę wspomnianą powyżej - w dowolnym momencie otwarcie pliku prowadzi do jego prawidłowej wersji.
Uaktualnienia aplikacji mogą się zepsuć, ponieważ podczas aktualizacji jednego pliku jest atomowy, aktualizacja aplikacji jako całości nie jest taka, że aplikacja składa się z wielu plików (program, biblioteki, dane,…). Rozważ następującą sekwencję zdarzeń:
- Wystąpiła instancja aplikacji.
- Aplikacja została zaktualizowana.
- Działająca aplikacja instancji otwiera jeden ze swoich plików danych.
W kroku 3 działająca instancja starej wersji aplikacji otwiera plik danych z nowej wersji. To, czy to działa, zależy od aplikacji, jakiego pliku to jest i jak bardzo plik został zmodyfikowany.
Po aktualizacji zauważysz, że stary program nadal działa. Jeśli chcesz uruchomić nową wersję, musisz wyjść ze starego programu i uruchomić nową wersję. Menedżerowie pakietów zwykle zabijają i ponownie uruchamiają demony podczas aktualizacji, ale pozostawiają aplikacje użytkowników w spokoju.
Kilka demonów ma specjalne procedury do obsługi aktualizacji bez konieczności zabijania demona i czekania na ponowne uruchomienie nowej instancji (co powoduje zakłócenie usługi). Jest to konieczne w przypadku init , którego nie można zabić; Systemy init zapewniają sposób żądania wywołania działającej instancji w execve
celu zastąpienia się nową wersją.