Pada 19 Agustus 2013, Randal L. Schwartz memposting skrip shell ini , yang dimaksudkan untuk memastikan, di Linux, "bahwa hanya satu instance skrip sedang berjalan, tanpa syarat balapan atau harus membersihkan file kunci":
#!/bin/sh
# randal_l_schwartz_001.sh
(
if ! flock -n -x 0
then
echo "$$ cannot get flock"
exit 0
fi
echo "$$ start"
sleep 10 # for testing. put the real task here
echo "$$ end"
) < $0
Tampaknya berfungsi seperti yang diiklankan:
$ ./randal_l_schwartz_001.sh & ./randal_l_schwartz_001.sh
[1] 11863
11863 start
11864 cannot get flock
$ 11863 end
[1]+ Done ./randal_l_schwartz_001.sh
$
Inilah yang saya mengerti:
- Script mengarahkan (
<
) salinan dari isinya sendiri (yaitu dari$0
) ke STDIN (yaitu deskriptor file0
) dari sebuah subkulit. - Dalam subkulit, skrip mencoba untuk mendapatkan kunci (
flock -n -x
) eksklusif non-pemblokiran pada deskriptor file0
.- Jika upaya itu gagal, subshell keluar (dan begitu juga skrip utama, karena tidak ada lagi yang bisa dilakukan).
- Jika upaya tersebut berhasil, subkulit menjalankan tugas yang diinginkan.
Ini pertanyaan saya:
- Mengapa skrip perlu diarahkan, ke deskriptor file yang diwarisi oleh subkulit, salinan kontennya sendiri, bukan, katakanlah, isi beberapa file lain? (Saya mencoba mengalihkan dari file yang berbeda dan menjalankan kembali seperti di atas, dan urutan eksekusi berubah: tugas non-latar belakang memperoleh kunci sebelum latar belakang. Jadi, mungkin menggunakan konten file sendiri menghindari kondisi ras; tetapi bagaimana?)
- Mengapa skrip perlu diarahkan, ke deskriptor file yang diwarisi oleh subkulit, salinan konten file?
- Mengapa memegang kunci eksklusif pada deskriptor file
0
dalam satu shell mencegah salinan script yang sama, berjalan di shell yang berbeda, dari mendapatkan kunci eksklusif pada deskriptor file0
? Jangan kerang memiliki sendiri, salinan yang terpisah mereka dari file deskriptor standar (0
,1
, dan2
, yaitu STDIN, STDOUT, dan stderr)?
linux
shell-script
io-redirection
subshell
lock
sampablokuper
sumber
sumber
Jawaban:
Anda dapat menggunakan file apa saja, selama semua salinan skrip menggunakan yang sama. Menggunakan
$0
hanya mengikat kunci ke skrip itu sendiri: Jika Anda menyalin skrip dan memodifikasinya untuk penggunaan lain, Anda tidak perlu membuat nama baru untuk file kunci. Ini nyaman.Jika skrip dipanggil melalui symlink, kuncinya ada pada file aktual, dan bukan tautannya.
(Tentu saja, jika beberapa proses menjalankan skrip dan memberinya nilai yang dibuat sebagai argumen nol bukannya jalan yang sebenarnya, maka ini rusak. Tapi itu jarang dilakukan.)
Apakah Anda yakin itu karena file yang digunakan, dan bukan hanya variasi acak? Seperti halnya pipeline, benar-benar tidak ada cara untuk memastikan dalam urutan apa perintah bisa dijalankan
cmd1 & cmd
. Sebagian besar terserah pada penjadwal OS. Saya mendapatkan variasi acak pada sistem saya.Sepertinya shell itu sendiri yang menyimpan salinan deskripsi file yang menahan kunci, bukan hanya
flock
utilitas yang menahannya. Kunci yang dibuat denganflock(2)
dilepaskan ketika deskriptor file yang memilikinya ditutup.flock
memiliki dua mode, baik untuk mengambil kunci berdasarkan nama file, dan menjalankan perintah eksternal (dalam hal iniflock
memegang deskriptor file terbuka yang diperlukan), atau untuk mengambil file deskriptor dari luar, sehingga proses luar bertanggung jawab untuk memegang saya t.Perhatikan bahwa konten file tidak relevan di sini, dan tidak ada salinan yang dibuat. Pengalihan ke subkulit tidak menyalin data apa pun di sekitarnya, itu hanya membuka pegangan ke file.
Ya, tetapi kuncinya ada di file , bukan deskriptor file. Hanya satu instance file yang dibuka yang dapat menahan kunci pada suatu waktu.
Saya pikir Anda harus dapat melakukan hal yang sama tanpa subkulit, dengan menggunakan
exec
untuk membuka pegangan ke file kunci:sumber
{ }
bukannya( )
juga akan bekerja dan menghindari subkulit.exec
.Kunci file dilampirkan ke file, melalui deskripsi file . Pada tingkat tinggi, urutan operasi dalam satu contoh skrip adalah:
Menahan kunci mencegah salinan lain dari skrip yang sama untuk dijalankan karena itulah yang dilakukan kunci. Selama kunci eksklusif pada file ada di suatu tempat di sistem, tidak mungkin membuat instance kedua dari kunci yang sama, bahkan melalui deskripsi file yang berbeda.
Membuka file menciptakan deskripsi file . Ini adalah objek kernel yang tidak memiliki banyak visibilitas langsung dalam antarmuka pemrograman. Anda mengakses deskripsi file secara tidak langsung melalui deskriptor file, tetapi biasanya Anda menganggapnya sebagai mengakses file (membaca atau menulis konten atau metadata). Kunci adalah salah satu atribut yang merupakan properti untuk deskripsi file daripada file atau ke deskriptor.
Pada awalnya, ketika file dibuka, deskripsi file memiliki deskriptor file tunggal, tetapi lebih banyak deskriptor dapat dibuat dengan membuat deskriptor lain (
dup
keluarga panggilan sistem) atau dengan membentuk subproses (setelah itu baik induk dan anak memiliki akses ke deskripsi file yang sama). Deskriptor file dapat ditutup secara eksplisit atau ketika prosesnya mati. Ketika deskriptor file terakhir yang dilampirkan ke file ditutup, deskripsi file ditutup.Begini cara urutan operasi di atas memengaruhi deskripsi file.
<$0
membuka file skrip di subkulit, membuat deskripsi file. Pada titik ini ada satu file deskriptor terlampir pada deskripsi: deskriptor nomor 0 di subkulit.flock
dan menunggu sampai keluar. Saat kawanan sedang berjalan, ada dua penjelas yang terlampir pada deskripsi: angka 0 di subkulit dan angka 0 dalam proses kawanan. Ketika kawanan mengambil kunci, itu mengatur properti dari deskripsi file. Jika deskripsi file lain sudah memiliki kunci pada file tersebut, kawanan tidak dapat mengambil kunci, karena itu adalah kunci eksklusif.Alasan skrip menggunakan pengalihan dari
$0
adalah bahwa pengalihan adalah satu-satunya cara untuk membuka file di shell, dan menjaga pengalihan aktif adalah satu-satunya cara untuk menjaga deskriptor file tetap terbuka. Subkulit tidak pernah membaca dari input standar, hanya perlu tetap terbuka. Dalam bahasa yang memberikan akses langsung ke buka dan tutup panggilan, Anda bisa menggunakanAnda benar-benar bisa mendapatkan urutan operasi yang sama di shell jika Anda melakukan pengalihan dengan
exec
builtin.Script dapat menggunakan deskriptor file yang berbeda jika ingin tetap mengakses input standar asli.
atau dengan subkulit:
Kunci tidak harus ada di file skrip. Itu bisa di file apa saja yang bisa dibuka untuk dibaca (jadi harus ada, itu harus tipe file yang bisa dibaca seperti file biasa atau pipa bernama tetapi bukan direktori, dan proses skrip harus memiliki izin untuk membacanya). File skrip memiliki keuntungan bahwa itu dijamin untuk hadir dan dapat dibaca (kecuali dalam kasus tepi di mana itu dihapus secara eksternal antara waktu skrip dipanggil dan waktu skrip sampai ke
<$0
pengalihan).Selama
flock
berhasil, dan skrip berada pada sistem file di mana kunci tidak buggy (beberapa sistem file jaringan seperti NFS mungkin buggy), saya tidak melihat bagaimana menggunakan file kunci yang berbeda dapat memungkinkan kondisi balapan. Saya menduga ada kesalahan manipulasi di pihak Anda.sumber
File yang digunakan untuk mengunci tidak penting, skrip menggunakan
$0
karena itu file yang diketahui ada.Urutan penguncian akan lebih atau kurang acak, tergantung pada seberapa cepat mesin Anda dapat memulai dua tugas.
Anda dapat menggunakan deskriptor file apa pun, tidak harus 0. Kunci dipegang pada file yang dibuka untuk deskriptor file, bukan deskriptor itu sendiri.
sumber