Mengganti file secara umum
Pertama, ada beberapa strategi untuk mengganti file:
Buka file yang ada untuk ditulis, potong hingga 0 panjang, dan tulis konten baru. (Varian yang kurang umum adalah membuka file yang ada, menimpa konten lama dengan konten baru, memotong file ke panjang baru jika lebih pendek.) Dalam istilah shell:
echo 'new content' >somefile
Hapus file lama, dan buat file baru dengan nama yang sama. Dalam istilah shell:
rm somefile
echo 'new content' >somefile
Tulis ke file baru dengan nama sementara, lalu pindahkan file baru ke nama yang ada. Langkah ini menghapus file lama. Dalam istilah shell:
echo 'new content' >somefile.new
mv somefile.new somefile
Saya tidak akan mencantumkan semua perbedaan antara strategi, saya hanya akan menyebutkan beberapa yang penting di sini. Dengan stategy 1, jika ada proses saat ini menggunakan file, proses melihat konten baru saat sedang diperbarui. Ini dapat menyebabkan beberapa kebingungan jika proses mengharapkan konten file tetap sama. Perhatikan bahwa ini hanya tentang proses yang membuka file (seperti terlihat di dalam lsof
atau di ; aplikasi interaktif yang memiliki dokumen terbuka (misalnya membuka file di editor) biasanya tidak membuat file tetap terbuka, mereka memuat konten file selama Operasi "buka dokumen" dan mereka mengganti file (menggunakan salah satu strategi di atas) selama operasi "simpan dokumen"./proc/PID/fd/
Dengan strategi 2 dan 3, jika beberapa proses somefile
membuka file , file lama tetap terbuka selama peningkatan konten. Dengan strategi 2, langkah menghapus file sebenarnya hanya menghapus entri file di direktori. File itu sendiri hanya dihapus ketika tidak memiliki entri direktori yang mengarah ke sana (pada sistem file Unix khas, mungkin ada lebih dari satu entri direktori untuk file yang sama ) dan tidak ada proses yang terbuka. Inilah cara untuk mengamati ini - file hanya dihapus ketika sleep
prosesnya dimatikan ( rm
hanya menghapus entri direktori-nya).
echo 'old content' >somefile
sleep 9999999 <somefile &
df .
rm somefile
df .
cat /proc/$!/fd/0
kill $!
df .
Dengan strategi 3, langkah memindahkan file baru ke nama yang ada menghapus entri direktori yang mengarah ke konten lama dan membuat entri direktori yang mengarah ke konten baru. Ini dilakukan dalam satu operasi atom, jadi strategi ini memiliki keuntungan besar: jika suatu proses membuka file kapan saja, ia akan melihat konten lama atau konten baru - tidak ada risiko mendapatkan konten campuran atau file tidak ada.
Mengganti executable
Jika Anda mencoba strategi 1 dengan executable yang berjalan di Linux, Anda akan mendapatkan kesalahan.
cp /bin/sleep .
./sleep 999999 &
echo oops >|sleep
bash: sleep: Text file busy
“File teks” berarti file yang berisi kode yang dapat dieksekusi untuk alasan historis yang tidak jelas . Linux, seperti banyak varian unix lainnya, menolak untuk menimpa kode program yang sedang berjalan; beberapa varian unix memungkinkan ini, menyebabkan crash kecuali kode baru itu modifikasi yang sangat baik dari kode lama.
Di Linux, Anda dapat menimpa kode perpustakaan yang dimuat secara dinamis. Kemungkinan akan menyebabkan crash pada program yang menggunakannya. (Anda mungkin tidak dapat mengamati ini dengan sleep
karena memuat semua kode perpustakaan yang dibutuhkan ketika dimulai. Cobalah program yang lebih kompleks yang melakukan sesuatu yang berguna setelah tidur, misalnya perl -e 'sleep 9; print lc $ARGV[0]'
.)
Jika penerjemah menjalankan skrip, file skrip dibuka dengan cara biasa oleh penerjemah, sehingga tidak ada perlindungan terhadap penulisan ulang skrip. Beberapa penerjemah membaca dan mem-parsing seluruh skrip sebelum mereka mulai mengeksekusi baris pertama, yang lain membaca skrip sesuai kebutuhan. Lihat Apa yang terjadi jika Anda mengedit skrip selama eksekusi? dan Bagaimana Linux menangani skrip shell? untuk lebih jelasnya.
Strategi 2 dan 3 juga aman untuk executable: walaupun menjalankan executable (dan perpustakaan yang dimuat secara dinamis) tidak membuka file dalam arti memiliki deskriptor file, mereka berperilaku dengan cara yang sangat mirip. Selama beberapa program menjalankan kode, file tetap di disk bahkan tanpa entri direktori.
Memutakhirkan aplikasi
Sebagian besar manajer paket menggunakan strategi 3 untuk mengganti file, karena keunggulan utama yang disebutkan di atas - kapan saja, membuka file mengarah ke versi yang valid.
Di mana pemutakhiran aplikasi dapat rusak adalah bahwa sementara memutakhirkan satu file bersifat atomik, memutakhirkan aplikasi secara keseluruhan tidak terjadi jika aplikasi terdiri dari banyak file (program, perpustakaan, data, ...). Pertimbangkan urutan kejadian berikut:
- Sebuah instance dari aplikasi dimulai.
- Aplikasi ditingkatkan.
- Aplikasi instance yang berjalan membuka salah satu file datanya.
Pada langkah 3, instance berjalan dari versi lama aplikasi membuka file data dari versi baru. Apakah ini berfungsi atau tidak tergantung pada aplikasi, dari file mana itu dan seberapa banyak file tersebut telah dimodifikasi.
Setelah peningkatan, Anda akan perhatikan bahwa program lama masih berjalan. Jika Anda ingin menjalankan versi baru, Anda harus keluar dari program lama dan menjalankan versi baru. Manajer paket biasanya membunuh dan memulai ulang daemon pada pembaruan, tetapi membiarkan aplikasi pengguna akhir saja.
Beberapa daemon memiliki prosedur khusus untuk menangani peningkatan tanpa harus mematikan daemon dan menunggu instance baru untuk restart (yang menyebabkan gangguan layanan). Ini diperlukan dalam kasus init , yang tidak dapat dibunuh; sistem init menyediakan cara untuk meminta panggilan instance yang berjalan execve
untuk menggantikan dirinya dengan versi baru.