Saya ingin menyalin file dari A ke B, yang mungkin ada di sistem file yang berbeda.
Ada beberapa persyaratan tambahan:
- Salinan adalah semua atau tidak sama sekali, tidak ada file B sebagian atau rusak yang tersisa pada saat crash;
- Jangan menimpa file B yang ada;
- Jangan bersaing dengan eksekusi perintah yang sama secara bersamaan, paling banyak orang bisa berhasil.
Saya pikir ini semakin dekat:
cp A B.part && \
ln B B.part && \
rm B.part
Tetapi 3. dilanggar oleh cp tidak gagal jika B.part ada (bahkan dengan flag -n). Selanjutnya 1. bisa gagal jika proses lain 'memenangkan' cp dan file yang ditautkan ke tempatnya tidak lengkap. B.part juga bisa berupa file yang tidak terkait, tapi saya senang gagal tanpa mencoba nama tersembunyi lainnya dalam kasus itu.
Saya pikir bash noclobber membantu, apakah ini berfungsi sepenuhnya? Apakah ada cara untuk mendapatkannya tanpa persyaratan versi bash?
#!/usr/bin/env bash
set -o noclobber
cat A > B.part && \
ln B.part B && \
rm B.part
Tindak lanjut, saya tahu beberapa sistem file akan gagal saat ini (NFS). Apakah ada cara untuk mendeteksi sistem file seperti itu?
Beberapa pertanyaan lain yang terkait tetapi tidak persis sama:
Mendekati perpindahan atom melintasi sistem file?
Apakah atom saya ada pada fs saya?
https://rcrowley.org/2010/01/06/things-unix-can-do-atomically.html
mv
akan menimpa file B. yang adamv -n
tidak akan memberi tahu bahwa ia gagal.ln(1)
(rename(2)
) akan gagal jika B sudah ada.Jawaban:
rsync
melakukan pekerjaan ini. File sementaraO_EXCL
dibuat secara default (hanya dinonaktifkan jika Anda menggunakan--inplace
) dan kemudian direnamed
atas file target. Gunakan--ignore-existing
untuk tidak menimpa B jika ada.Dalam prakteknya, saya tidak pernah mengalami masalah dengan ini pada ext4, zfs atau bahkan NFS mounts.
sumber
Jangan khawatir,
noclobber
ini fitur standar .sumber
Anda bertanya tentang NFS. Jenis kode ini kemungkinan akan pecah di bawah NFS, karena pemeriksaan untuk
noclobber
melibatkan dua operasi NFS yang terpisah (memeriksa apakah ada file, membuat file baru) dan dua proses dari dua klien NFS yang terpisah dapat masuk ke kondisi balapan di mana keduanya berhasil ( keduanya memverifikasi yangB.part
belum ada, lalu keduanya melanjutkan untuk berhasil membuatnya, sebagai akibatnya mereka saling menimpa.)Tidak ada benar-benar melakukan pemeriksaan generik untuk apakah sistem file yang Anda tulis akan mendukung sesuatu seperti
noclobber
atom atau tidak. Anda dapat memeriksa tipe sistem file, apakah itu NFS, tetapi itu akan menjadi heuristik dan belum tentu menjadi jaminan. Sistem file seperti SMB / CIFS (Samba) kemungkinan akan mengalami masalah yang sama. Filesystem mengekspos melalui FUSE mungkin atau mungkin tidak berperilaku dengan benar, tetapi itu sebagian besar tergantung pada implementasinya.Pendekatan yang mungkin lebih baik adalah menghindari tabrakan pada
B.part
langkah tersebut, dengan menggunakan nama file unik (melalui kerja sama dengan agen lain) sehingga Anda tidak perlu bergantungnoclobber
. Misalnya, Anda dapat memasukkan, sebagai bagian dari nama file, nama host Anda, PID dan stempel waktu (+ mungkin nomor acak). Karena harus ada satu proses yang berjalan di bawah PID tertentu pada host pada waktu tertentu, ini harus menjamin keunikan.Jadi salah satu dari:
Atau:
Jadi jika Anda memiliki kondisi balapan antara dua agen, mereka berdua akan melanjutkan dengan operasi, tetapi operasi terakhir akan menjadi atom, sehingga B ada dengan salinan penuh A, atau B tidak ada.
Anda dapat mengurangi ukuran balapan dengan memeriksa lagi setelah salinan dan sebelum
mv
atauln
operasi, tetapi masih ada kondisi balapan kecil di sana. Tetapi, terlepas dari kondisi balapan, isi B harus konsisten, dengan asumsi kedua proses mencoba membuatnya dari A (atau salinan dari file yang valid sebagai asal.)Perhatikan bahwa dalam situasi pertama dengan
mv
, ketika ada perlombaan, proses terakhir adalah yang menang, karena mengubah nama (2) secara atom akan menggantikan file yang ada:Jadi, sangat mungkin proses memakan B pada saat itu mungkin melihat versi yang berbeda (inode berbeda) selama proses ini. Jika penulis hanya mencoba menyalin konten yang sama, dan pembaca hanya mengkonsumsi konten file, itu mungkin baik-baik saja, jika mereka mendapatkan inode berbeda untuk file dengan konten yang sama, mereka akan senang sama saja.
Pendekatan kedua menggunakan hardlink terlihat lebih baik, tapi saya ingat membuat percobaan dengan hardlink dalam loop ketat pada NFS dari banyak klien bersamaan dan menghitung keberhasilan dan tampaknya masih ada beberapa kondisi lomba di sana, di mana tampaknya jika dua klien mengeluarkan hardlink Operasi pada saat yang sama, dengan tujuan yang sama, keduanya tampaknya berhasil. (Ada kemungkinan bahwa perilaku ini terkait dengan implementasi server NFS tertentu, YMMV.) Bagaimanapun, itu mungkin kondisi ras yang sama, di mana Anda mungkin mendapatkan dua inode terpisah untuk file yang sama dalam kasus di mana ada berat konkurensi antara penulis untuk memicu kondisi lomba ini. Jika penulis Anda konsisten (keduanya menyalin A ke B), dan pembaca Anda hanya mengkonsumsi konten, itu mungkin cukup.
Akhirnya, Anda menyebutkan penguncian. Sayangnya penguncian sangat kurang, setidaknya di NFSv3 (tidak yakin tentang NFSv4, tapi saya yakin itu juga tidak baik.) Jika Anda mempertimbangkan penguncian, Anda harus melihat ke protokol yang berbeda untuk penguncian yang didistribusikan, mungkin keluar dari band dengan salinan file yang sebenarnya, tapi itu mengganggu, kompleks dan rentan terhadap masalah seperti kebuntuan, jadi saya akan mengatakan lebih baik untuk dihindari.
Untuk latar belakang lebih lanjut tentang masalah atomisitas pada NFS, Anda mungkin ingin membaca pada format kotak surat Maildir , yang dibuat untuk menghindari kunci dan bekerja dengan andal bahkan pada NFS. Itu dilakukan dengan menjaga nama file unik di mana-mana (sehingga Anda bahkan tidak mendapatkan B akhir di akhir.)
Mungkin agak lebih menarik untuk kasus khusus Anda, format Maildir ++ memperluas Maildir untuk menambahkan dukungan untuk kuota kotak surat dan melakukannya dengan memperbarui secara atomis file dengan nama tetap di dalam kotak surat (sehingga mungkin lebih dekat ke B. Anda) Saya pikir Maildir ++ mencoba untuk menambahkan, yang tidak benar-benar aman di NFS, tetapi ada pendekatan perhitungan kembali yang menggunakan prosedur yang mirip dengan ini dan itu valid sebagai pengganti atom.
Semoga semua petunjuk ini bermanfaat!
sumber
Anda dapat menulis program untuk ini.
Gunakan
open(O_CREAT|O_RDWD)
untuk membuka file target, baca semua byte dan metadata untuk memeriksa apakah file target sudah lengkap, jika tidak, ada dua kemungkinan,Menulis tidak lengkap
Proses lain menjalankan program yang sama.
Cobalah untuk mendapatkan kunci deskripsi file terbuka pada file target.
Kegagalan berarti ada proses bersamaan, proses saat ini harus ada.
Sukses berarti penulisan terakhir macet, Anda harus memulai kembali atau mencoba memperbaikinya dengan menulis ke file.
Juga perhatikan bahwa Anda akan lebih baik
fsync()
setelah menulis ke file target sebelum Anda menutup file dan melepaskan kunci, atau proses lain mungkin membaca data yang belum-pada-disk.https://www.gnu.org/software/libc/manual/html_node/Open-File-Description-Locks.html
Ini penting untuk membantu Anda membedakan antara program yang berjalan bersamaan dan operasi yang macet terakhir.
sumber
Anda akan mendapatkan hasil yang benar dengan melakukan
cp
bersamamv
. Ini akan menggantikan "B" dengan salinan baru "A", atau meninggalkan "B" seperti sebelumnya.pembaruan untuk mengakomodasi yang ada
B
:Ini bukan atom 100%, tetapi mendekati. Ada kondisi balapan di mana dua hal ini berjalan, keduanya masuk
if
tes pada saat yang sama, keduanya melihat bahwaB
tidak ada, kemudian keduanya menjalankanmv
.sumber
mv B.tmp B
tidak akan berjalan kecualicp A B.tmp
berjalan pertama dan mengembalikan kode hasil yang sukses. bagaimana itu gagal? juga, saya setuju bahwacp A B.tmp
akan menimpa yang sudah adaB.tmp
yang ingin Anda lakukan. The&&
jaminan bahwa 2 perintah akan berjalan jika dan hanya jika yang pertama selesai normal.