Sostituzione dei file in generale
Innanzitutto, esistono diverse strategie per sostituire un file:
Aprire il file esistente per la scrittura, troncarlo a lunghezza 0 e scrivere il nuovo contenuto. (Una variante meno comune è aprire il file esistente, sovrascrivere il vecchio contenuto con il nuovo contenuto, troncare il file alla nuova lunghezza se è più corto.) In termini di shell:
echo 'new content' >somefile
Rimuovere il vecchio file e creare un nuovo file con lo stesso nome. In termini di shell:
rm somefile
echo 'new content' >somefile
Scrivi in un nuovo file con un nome temporaneo, quindi sposta il nuovo file sul nome esistente. La mossa cancella il vecchio file. In termini di shell:
echo 'new content' >somefile.new
mv somefile.new somefile
Non elencherò tutte le differenze tra le strategie, ne citerò solo alcune importanti qui. Con la categoria 1, se un processo sta attualmente utilizzando il file, il processo vede il nuovo contenuto mentre viene aggiornato. Ciò può causare confusione se il processo prevede che il contenuto del file rimanga lo stesso. Si noti che si tratta solo di processi che hanno il file aperto (come visibile in lsof
o in ; le applicazioni interattive che hanno un documento aperto (ad es. Apertura di un file in un editor) di solito non mantengono il file aperto, caricano il contenuto del file durante Operazione "apri documento" e sostituiscono il file (usando una delle strategie sopra) durante l'operazione "salva documento"./proc/PID/fd/
Con le strategie 2 e 3, se un processo ha il file somefile
aperto, il vecchio file rimane aperto durante l'aggiornamento del contenuto. Con la strategia 2, la fase di rimozione del file rimuove infatti solo la voce del file nella directory. Il file stesso viene rimosso solo quando non ha una voce di directory che lo porta (su file system Unix tipici, può esserci più di una voce di directory per lo stesso file ) e nessun processo lo ha aperto. Ecco un modo per osservarlo: il file viene rimosso solo quando il sleep
processo viene interrotto ( rm
rimuove solo la sua voce di directory).
echo 'old content' >somefile
sleep 9999999 <somefile &
df .
rm somefile
df .
cat /proc/$!/fd/0
kill $!
df .
Con la strategia 3, la fase di spostamento del nuovo file sul nome esistente rimuove la voce della directory che porta al vecchio contenuto e crea una voce della directory che porta al nuovo contenuto. Questo viene fatto in un'unica operazione atomica, quindi questa strategia ha un grande vantaggio: se un processo apre il file in qualsiasi momento, vedrà il vecchio contenuto o il nuovo contenuto - non c'è rischio di ottenere contenuti misti o il file no esistente.
Sostituzione degli eseguibili
Se provi la strategia 1 con un eseguibile in esecuzione su Linux, otterrai un errore.
cp /bin/sleep .
./sleep 999999 &
echo oops >|sleep
bash: sleep: Text file busy
Un "file di testo" indica un file contenente codice eseguibile per oscuri motivi storici . Linux, come molte altre varianti unix, rifiuta di sovrascrivere il codice di un programma in esecuzione; alcune varianti unix lo consentono, causando arresti anomali a meno che il nuovo codice non sia stato una modifica molto ben concepita del vecchio codice.
Su Linux, puoi sovrascrivere il codice di una libreria caricata dinamicamente. È probabile che porti a un arresto anomalo del programma che lo sta utilizzando. (Potresti non essere in grado di osservare questo sleep
perché carica tutto il codice della libreria di cui ha bisogno all'avvio. Prova un programma più complesso che fa qualcosa di utile dopo aver dormito, come perl -e 'sleep 9; print lc $ARGV[0]'
.)
Se un interprete esegue uno script, il file di script viene aperto in modo ordinario dall'interprete, quindi non esiste protezione contro la sovrascrittura dello script. Alcuni interpreti leggono e analizzano l'intero script prima di iniziare l'esecuzione della prima riga, altri leggono lo script secondo necessità. Vedi Cosa succede se modifichi uno script durante l'esecuzione? e in che modo Linux gestisce gli script della shell? per ulteriori dettagli.
Le strategie 2 e 3 sono sicure anche per gli eseguibili: sebbene i file eseguibili (e le librerie caricate dinamicamente) non siano file aperti nel senso di avere un descrittore di file, si comportano in modo molto simile. Finché alcuni programmi eseguono il codice, il file rimane sul disco anche senza una voce di directory.
Aggiornamento di un'applicazione
La maggior parte dei gestori di pacchetti utilizza la strategia 3 per sostituire i file, a causa del grande vantaggio di cui sopra - in qualsiasi momento, l'apertura del file porta a una versione valida di esso.
Il punto in cui gli aggiornamenti dell'applicazione possono interrompersi è che durante l'aggiornamento di un file è atomico, l'aggiornamento dell'applicazione nel suo insieme non lo è se l'applicazione è composta da più file (programma, librerie, dati, ...). Considera la seguente sequenza di eventi:
- Viene avviata un'istanza dell'applicazione.
- L'applicazione è stata aggiornata.
- L'applicazione di istanza in esecuzione apre uno dei suoi file di dati.
Nel passaggio 3, l'istanza in esecuzione della versione precedente dell'applicazione sta aprendo un file di dati dalla nuova versione. Se funziona o meno dipende dall'applicazione, da quale file è e da quanto è stato modificato.
Dopo un aggiornamento, noterai che il vecchio programma è ancora in esecuzione. Se vuoi eseguire la nuova versione, dovrai uscire dal vecchio programma ed eseguire la nuova versione. I gestori di pacchetti di solito uccidono e riavviano i daemon durante un aggiornamento, ma lasciano solo le applicazioni dell'utente finale.
Alcuni demoni hanno procedure speciali per gestire gli aggiornamenti senza dover uccidere il demone e attendere il riavvio della nuova istanza (che causa un'interruzione del servizio). Ciò è necessario nel caso di init , che non può essere ucciso; I sistemi init forniscono un modo per richiedere che l'istanza in esecuzione chiami execve
per sostituirsi con la nuova versione.