Terkadang Anda harus memastikan bahwa hanya satu instance skrip shell yang berjalan pada saat yang sama.
Misalnya pekerjaan cron yang dijalankan melalui crond yang tidak menyediakan penguncian sendiri (misalnya default Solaris crond).
Pola umum untuk menerapkan penguncian adalah kode seperti ini:
#!/bin/sh
LOCK=/var/tmp/mylock
if [ -f $LOCK ]; then # 'test' -> race begin
echo Job is already running\!
exit 6
fi
touch $LOCK # 'set' -> race end
# do some work
rm $LOCK
Tentu saja, kode tersebut memiliki kondisi balapan. Ada jendela waktu di mana eksekusi dua instance dapat maju setelah baris 3 sebelum seseorang dapat menyentuh $LOCK
file.
Untuk pekerjaan cron ini biasanya tidak masalah karena Anda memiliki interval menit antara dua doa.
Tetapi ada yang salah - misalnya ketika lockfile ada di server NFS - yang hang. Dalam hal ini beberapa pekerjaan cron dapat memblokir pada baris 3 dan mengantri. Jika server NFS aktif lagi maka Anda memiliki guruh yang menjalankan pekerjaan paralel.
Mencari di web saya menemukan alat lockrun yang sepertinya solusi yang baik untuk masalah itu. Dengannya Anda menjalankan skrip yang perlu dikunci seperti ini:
$ lockrun --lockfile=/var/tmp/mylock myscript.sh
Anda bisa memasukkan ini ke dalam pembungkus atau menggunakannya dari crontab Anda.
Menggunakan lockf()
(POSIX) jika tersedia dan kembali ke flock()
(BSD). Dan lockf()
dukungan terhadap NFS harus relatif luas.
Apakah ada alternatif lain lockrun
?
Bagaimana dengan daemon cron lainnya? Apakah ada crond umum yang mendukung penguncian dengan cara yang waras? Pandangan cepat ke halaman manual Vixie Crond (default pada sistem Debian / Ubuntu) tidak menunjukkan apa pun tentang penguncian.
Apakah ide yang bagus untuk memasukkan alat seperti lockrun
ke dalam coreutils ?
Menurut pendapat saya itu menerapkan tema yang sangat mirip dengan timeout
, nice
dan teman-teman.
kill
ed; dan tampaknya merupakan praktik yang baik untuk menyimpan pid Anda sendiri di lockfile, daripada hanya menyentuhnya.Jawaban:
Berikut cara lain untuk mengunci skrip shell yang dapat mencegah kondisi balapan yang Anda jelaskan di atas, di mana dua pekerjaan dapat melewati baris 3.
noclobber
Opsi akan bekerja dalam ksh dan bash. Jangan gunakanset noclobber
karena Anda tidak boleh menulis skrip di csh / tcsh. ;)YMMV dengan mengunci NFS (Anda tahu, ketika server NFS tidak dapat dijangkau), tetapi secara umum jauh lebih kuat daripada dulu. (10 tahun yang lalu)
Jika Anda memiliki pekerjaan cron yang melakukan hal yang sama pada waktu yang sama, dari beberapa server, tetapi Anda hanya perlu 1 instance untuk benar-benar dijalankan, sesuatu seperti ini mungkin bekerja untuk Anda.
Saya tidak punya pengalaman dengan lockrun, tetapi memiliki lingkungan kunci yang telah ditentukan sebelum script benar-benar berjalan mungkin membantu. Atau mungkin juga tidak. Anda hanya mengatur tes untuk lockfile di luar skrip Anda dalam pembungkus, dan secara teoritis, tidak bisakah Anda menekan kondisi balapan yang sama jika dua pekerjaan dipanggil oleh lockrun pada waktu yang bersamaan, seperti halnya dengan 'inside- solusi the-script '?
Penguncian file cukup banyak untuk menghormati perilaku sistem, dan setiap skrip yang tidak memeriksa keberadaan lockfile sebelum menjalankan akan melakukan apa pun yang akan mereka lakukan. Hanya dengan memasukkan tes lockfile, dan perilaku yang tepat, Anda akan menyelesaikan 99% masalah potensial, jika tidak 100%.
Jika Anda sering mengalami kondisi perlombaan lockfile, ini mungkin merupakan indikator masalah yang lebih besar, seperti tidak memiliki waktu yang tepat untuk pekerjaan Anda, atau mungkin jika intervalnya tidak sepenting penyelesaian pekerjaan, mungkin pekerjaan Anda lebih cocok untuk dihidupkan kembali. .
EDIT DI BAWAH - 2016-05-06 (jika Anda menggunakan KSH88)
Berdasarkan komentar @Clint Pachl di bawah ini, jika Anda menggunakan ksh88, gunakan
mkdir
sebagai gantinoclobber
. Ini sebagian besar mengurangi kondisi ras potensial, tetapi tidak sepenuhnya membatasi itu (meskipun risikonya sangat kecil). Untuk informasi lebih lanjut baca tautan yang diposting Clint di bawah ini .Dan, sebagai keuntungan tambahan, jika Anda perlu membuat tmpfile di skrip Anda, Anda dapat menggunakan
lockdir
direktori untuk mereka, mengetahui bahwa mereka akan dibersihkan ketika skrip keluar.Untuk bash yang lebih modern, metode noclobber di bagian atas harus sesuai.
sumber
lockf()
system call - ketika dicadangkan semua proses dilanjutkan tetapi hanya satu proses yang akan memenangkan kunci. Tidak ada kondisi balapan. Saya tidak mengalami masalah seperti itu dengan cronjobs banyak - yang terjadi sebaliknya - tetapi ini adalah masalah ketika hits Anda memiliki potensi untuk membuat banyak rasa sakit.set -o noclobber && echo "$$" > "$lockfile"
untuk mendapatkan fallback yang aman ketika shell tidak mendukung opsi noclobber.noclobber
pilihan mungkin rentan terhadap kondisi ras. Lihat mywiki.wooledge.org/BashFAQ/045 untuk beberapa makanan untuk dipikirkan.noclobber
(atau-C
) di ksh88 tidak berfungsi karena ksh88 tidak digunakanO_EXCL
untuknoclobber
. Jika Anda menjalankan dengan shell yang lebih baru, Anda mungkin baik-baik saja ...Saya lebih suka menggunakan tautan keras.
Hard link adalah atom lebih dari NFS dan untuk sebagian besar, mkdir juga . Menggunakan
mkdir(2)
ataulink(2)
hampir sama, pada tingkat praktis; Saya hanya lebih suka menggunakan tautan keras karena lebih banyak implementasi NFS yang memungkinkan tautan keras atom daripada atommkdir
. Dengan rilis NFS modern, Anda tidak perlu khawatir menggunakan keduanya.sumber
Saya mengerti itu
mkdir
atomik, jadi mungkin:sumber
mkdir()
lebih dari NFS (> = 3) distandarisasi untuk menjadi atom.mkdir
menjadi atomik (memang untukrename
). Dalam praktiknya, diketahui bahwa beberapa implementasi tidak. Terkait: utas menarik, termasuk kontribusi oleh penulis GNU arch .Cara mudah adalah dengan menggunakan paket yang
lockfile
biasanya datangprocmail
.sumber
sem
yang datang sebagai bagian dariparallel
alat GNU mungkin apa yang Anda cari:Seperti dalam:
keluaran:
Perhatikan bahwa pesanan tidak dijamin. Juga output tidak ditampilkan sampai selesai (menjengkelkan!). Namun demikian, ini adalah cara paling ringkas yang saya tahu untuk menjaga dari eksekusi bersamaan, tanpa khawatir tentang lockfile dan coba lagi dan pembersihan.
sumber
sem
pegangan ditembak jatuh di tengah eksekusi?Saya menggunakan
dtach
.sumber
Saya menggunakan alat baris perintah "kawanan" untuk mengelola kunci di skrip bash saya, seperti yang dijelaskan di sini dan di sini . Saya telah menggunakan metode sederhana ini dari halaman kawanan, untuk menjalankan beberapa perintah dalam sebuah subkulit ...
Dalam contoh itu, gagal dengan kode keluar 1 jika tidak dapat memperoleh file kunci. Tetapi flock juga dapat digunakan dengan cara yang tidak membutuhkan perintah untuk dijalankan di sub-shell :-)
sumber
flock()
system call tidak bekerja lebih NFS .flock()
dan bermasalah dengan NFS. Btw, sementara itu, flock () di Linux sekarang kembali kefcntl()
ketika file tersebut terletak di mount NFS, dengan demikian, dalam lingkungan NFS hanya Linuxflock()
sekarang bekerja lebih dari NFS.Jangan gunakan file.
Jika skrip Anda dieksekusi seperti ini misalnya:
Anda dapat mendeteksi apakah itu berjalan menggunakan:
sumber
my_script
? Dalam hal ini contoh lain sedang berjalan - tidakrunning_proc
mengandung dua baris yang cocok? Saya suka idenya, tetapi tentu saja - Anda akan mendapatkan hasil yang salah ketika pengguna lain menjalankan skrip dengan nama yang sama ...$!
bukan$$
dalam contoh Anda.grep -v $$
.). Pada dasarnya saya berusaha memberikan pendekatan yang berbeda untuk masalah tersebut.Untuk penggunaan aktual, Anda harus menggunakan jawaban terpilih teratas .
Namun, saya ingin membahas beberapa pendekatan yang rusak dan semi-bisa diterapkan menggunakan
ps
dan banyak peringatan yang mereka miliki, karena saya terus melihat orang menggunakannya.Jawaban ini benar-benar jawaban untuk "Mengapa tidak menggunakan
ps
dangrep
menangani penguncian di shell?"Pendekatan yang rusak # 1
Pertama, pendekatan yang diberikan dalam jawaban lain yang memiliki beberapa upvotes terlepas dari kenyataan bahwa itu tidak (dan tidak pernah bisa) bekerja dan jelas tidak pernah diuji:
Mari kita perbaiki kesalahan sintaks dan
ps
argumen yang rusak dan dapatkan:Script ini akan selalu keluar 6, setiap kali, tidak peduli bagaimana Anda menjalankannya.
Jika Anda menjalankannya
./myscript
, makaps
hasilnya hanya akan12345 -bash
, yang tidak cocok dengan string yang diperlukan12345 bash ./myscript
, sehingga akan gagal.Jika Anda menjalankannya
bash myscript
, segalanya menjadi lebih menarik. Proses bash bercabang untuk menjalankan pipa, dan shell anak menjalankanps
dangrep
. Shell asli dan shell anak akan muncul dips
output, kira-kira seperti ini:Itu bukan hasil yang diharapkan
$$ bash $0
, jadi skrip Anda akan keluar.Pendekatan yang rusak # 2
Sekarang dalam keadilan bagi pengguna yang menulis pendekatan # 1 yang rusak, saya melakukan sesuatu yang serupa saat pertama kali mencoba ini:
Ini hampir berhasil. Tetapi fakta forking untuk menjalankan pipa membuang ini. Jadi yang ini akan selalu keluar juga.
Pendekatan tidak dapat diandalkan # 3
Versi ini menghindari masalah forking pipa dalam pendekatan # 2 dengan terlebih dahulu mendapatkan semua PID yang memiliki skrip saat ini dalam argumen baris perintah mereka, dan kemudian memfilter pidlist itu, secara terpisah, untuk menghilangkan PID dari skrip saat ini.
Ini mungkin berhasil ... asalkan tidak ada proses lain yang memiliki baris perintah yang cocok dengan
$0
, dan menyediakan skrip selalu disebut dengan cara yang sama (misalnya jika itu disebut dengan jalur relatif dan kemudian jalur absolut, contoh terakhir tidak akan melihat yang sebelumnya ).Pendekatan tidak dapat diandalkan # 4
Jadi bagaimana jika kita lewati memeriksa baris perintah penuh, karena itu mungkin tidak menunjukkan skrip benar-benar berjalan, dan
lsof
bukannya memeriksa untuk menemukan semua proses yang memiliki skrip ini terbuka?Ya, pendekatan ini sebenarnya tidak terlalu buruk:
Tentu saja, jika salinan skrip sedang berjalan, maka instance baru akan mulai baik-baik saja dan Anda akan menjalankan dua salinan .
Atau jika skrip yang berjalan dimodifikasi (mis. Dengan Vim atau dengan a
git checkout
), maka skrip versi "baru" akan mulai tanpa masalah, karena keduanya Vim dangit checkout
menghasilkan file baru (inode baru) menggantikan yang lama.Namun, jika skrip tidak pernah dimodifikasi dan tidak pernah disalin, maka versi ini cukup bagus. Tidak ada kondisi balapan karena file skrip harus dibuka sebelum pemeriksaan dapat dicapai.
Masih bisa ada false positive jika proses lain membuka file skrip, tetapi perhatikan bahwa meskipun itu terbuka untuk diedit di Vim, vim tidak benar-benar menahan file skrip terbuka sehingga tidak akan menghasilkan false positive.
Tapi ingat, jangan gunakan pendekatan ini jika skrip mungkin diedit atau disalin karena Anda akan mendapatkan negatif palsu, yaitu beberapa kejadian berjalan sekaligus - jadi fakta bahwa mengedit dengan Vim tidak memberikan hasil positif palsu, seharusnya tidak menjadi masalah. kepadamu. Saya menyebutkannya, karena pendekatan # 3 memang memberikan positif palsu (yaitu menolak untuk memulai) jika Anda memiliki skrip terbuka dengan Vim.
Lalu apa yang harus dilakukan?
Jawaban teratas untuk pertanyaan ini memberikan pendekatan yang bagus dan solid.
Mungkin Anda bisa menulis yang lebih baik ... tetapi jika Anda tidak memahami semua masalah dan peringatan dengan semua pendekatan di atas, Anda tidak akan menulis metode penguncian yang menghindari semuanya.
sumber
Menggunakan alat FLOM (Free LOck Manager) , perintah berserialisasi menjadi semudah menjalankan
FLOM memungkinkan Anda untuk menerapkan kasus penggunaan yang lebih lunak (penguncian terdistribusi, pembaca / penulis, sumber daya numerik, dll ...) seperti yang dijelaskan di sini: http://sourceforge.net/p/flom/wiki/FLOM%20by%20examples/
sumber
Inilah sesuatu yang kadang-kadang saya tambahkan di server untuk dengan mudah menangani kondisi lomba untuk pekerjaan apa pun di mesin. Ini serupa dengan pos Tim Kennedy, tetapi dengan cara ini Anda mendapatkan penanganan balapan dengan hanya menambahkan satu baris ke setiap skrip bash yang membutuhkannya.
Letakkan konten di bawah ini di mis / opt / racechecker / racechecker:
Inilah cara menggunakannya. Perhatikan baris setelah shebang:
Cara kerjanya adalah ia mengetahui nama file bashscript utama dan membuat pidfile di bawah "/ tmp". Ini juga menambahkan pendengar pada sinyal akhir. Pendengar akan menghapus pidfile ketika skrip utama selesai dengan benar.
Sebaliknya jika pidfile ada ketika sebuah instance diluncurkan, maka pernyataan if yang berisi kode di dalam if-statement kedua akan dieksekusi. Dalam hal ini saya telah memutuskan untuk meluncurkan surat alarm ketika ini terjadi.
Bagaimana jika skrip mogok
Latihan selanjutnya adalah menangani tabrakan. Idealnya pidfile harus dihapus bahkan jika skrip utama macet karena alasan apa pun, ini tidak dilakukan dalam versi saya di atas. Itu berarti jika skrip macet, pidfile harus dihapus secara manual untuk mengembalikan fungsionalitas.
Dalam kasus sistem crash
Ini adalah ide yang baik untuk menyimpan pidfile / lockfile di bawah sebagai contoh / tmp. Dengan cara ini skrip Anda akan terus dieksekusi setelah sistem crash karena pidfile akan selalu dihapus saat bootup.
sumber
Periksa skrip saya ...
Anda mungkin MENYUKAINYA ....
sumber
Saya menawarkan solusi berikut ini, dalam sebuah skrip bernama 'flocktest'
sumber