Misalkan, misalnya, Anda memiliki skrip shell yang mirip dengan:
longrunningthing &
p=$!
echo Killing longrunningthing on PID $p in 24 hours
sleep 86400
echo Time up!
kill $p
Harus melakukan trik, bukan? Kecuali bahwa proses tersebut mungkin telah dihentikan lebih awal dan PID-nya mungkin telah didaur ulang, artinya beberapa pekerjaan yang tidak bersalah mendapatkan bom dalam antrian sinyalnya. Dalam praktiknya ini mungkin memang penting, tetapi tetap saja itu mengkhawatirkan saya. Meretas longrunning untuk mati dengan sendirinya, atau mempertahankan / menghapus PID-nya di FS akan dilakukan, tetapi saya sedang memikirkan situasi umum di sini.
killall
yang cocok dengan nama, jadi setidaknya Anda hanya membunuh proses dengan nama yang samalongrunningthing
. Dengan asumsi Anda hanya akan menjalankan salah satu dari ini pada satu waktu.Jawaban:
Yang terbaik adalah menggunakan
timeout
perintah jika Anda memilikinya yang dimaksudkan untuk itu:Implementasi GNU saat ini (8,23) setidaknya bekerja dengan menggunakan
alarm()
atau setara saat menunggu proses anak. Tampaknya tidak menjaga terhadapSIGALRM
yang disampaikan di antarawaitpid()
kembali dantimeout
keluar (secara efektif membatalkan alarm itu ). Selama jendela kecil itu,timeout
bahkan mungkin menulis pesan di stderr (misalnya jika anak membuang inti) yang akan lebih memperbesar jendela ras itu (tanpa batas jika stderr adalah pipa penuh misalnya).Saya pribadi dapat hidup dengan batasan itu (yang mungkin akan diperbaiki dalam versi yang akan datang).
timeout
juga akan sangat berhati-hati untuk melaporkan status keluar yang benar, menangani kasus sudut lainnya (seperti SIGALRM diblokir / diabaikan pada saat startup, menangani sinyal lain ...) lebih baik daripada yang mungkin Anda bisa lakukan dengan tangan.Sebagai perkiraan, Anda bisa menulis
perl
seperti:Ada
timelimit
perintah di http://devel.ringlet.net/sysutils/timelimit/ (mendahului GNUtimeout
beberapa bulan).Yang itu menggunakan
alarm()
mekanisme mirip-tetapi menginstal pawang padaSIGCHLD
(mengabaikan anak-anak yang berhenti) untuk mendeteksi anak sekarat. Itu juga membatalkan alarm sebelum menjalankanwaitpid()
(itu tidak membatalkan pengirimanSIGALRM
jika itu tertunda, tetapi cara itu ditulis, saya tidak bisa melihatnya menjadi masalah) dan membunuh sebelum memanggilwaitpid()
(jadi tidak bisa membunuh pid digunakan kembali ).netpipes juga memiliki
timelimit
perintah. Yang satu mendahului semua yang lain dalam beberapa dekade, membutuhkan pendekatan lain, tetapi tidak berfungsi dengan benar untuk perintah yang berhenti dan mengembalikan a1
status keluar setelah batas waktu.Sebagai jawaban yang lebih langsung untuk pertanyaan Anda, Anda dapat melakukan sesuatu seperti:
Yaitu, periksa apakah prosesnya masih anak-anak kita. Sekali lagi, ada jendela ras kecil (di antara
ps
mengambil status proses itu dankill
membunuhnya) selama proses itu bisa mati dan pid nya digunakan kembali oleh proses lain.Dengan beberapa kerang (
zsh
,bash
,mksh
), Anda dapat melewati spesifikasi pekerjaan bukan PID.Itu hanya bekerja jika Anda menelurkan hanya satu pekerjaan latar belakang (jika tidak mendapatkan jobspec yang tepat tidak selalu mungkin andal).
Jika itu masalah, mulai saja instance shell baru:
Itu bekerja karena shell menghapus pekerjaan dari tabel pekerjaan pada saat anak sekarat. Di sini, seharusnya tidak ada jendela balapan karena pada saat shell memanggil
kill()
, baik sinyal SIGCHLD belum ditangani dan pid tidak dapat digunakan kembali (karena belum menunggu), atau sudah ditangani dan pekerjaan telah dihapus dari tabel proses (dankill
akan melaporkan kesalahan).bash
Inikill
setidaknya blok SIGCHLD sebelum mengakses meja tugasnya untuk memperluas%
dan unblocks itu setelahkill()
.Pilihan lain untuk menghindari
sleep
proses yang berkeliaran bahkan setelahcmd
telah mati, denganbash
atauksh93
menggunakan pipa denganread -t
bukannyasleep
:Yang itu masih memiliki kondisi balapan, dan Anda kehilangan status keluar perintah. Ia juga menganggap
cmd
tidak menutup fd 4.Anda dapat mencoba menerapkan solusi bebas ras di
perl
seperti:(Meskipun itu perlu ditingkatkan untuk menangani jenis kasus sudut lainnya).
Metode bebas ras lain bisa menggunakan grup proses:
Namun perhatikan bahwa menggunakan grup proses dapat memiliki efek samping jika ada I / O ke perangkat terminal yang terlibat. Ini memiliki manfaat tambahan meskipun untuk membunuh semua proses ekstra lainnya yang dihasilkan oleh
cmd
.sumber
timeout
tidak portabel, jawabannya menyebutkan solusi portabel terlebih dahulu.jobs
dan kemudian mengetahui bahwa (karena itu adalah shell Anda sendiri, di mana Anda memiliki kendali atas apa yang terjadi selanjutnya) yang berikutnya dilatar belakangi pekerjaan akan menjadi N +1? [maka Anda dapat menyimpan N, dan kemudian membunuh% N +1])Secara umum, Anda tidak bisa. Semua jawaban yang diberikan sejauh ini adalah heuristik bermasalah. Hanya ada satu kasus di mana Anda dapat dengan aman menggunakan pid untuk mengirim sinyal: ketika proses target adalah anak langsung dari proses yang akan mengirimkan sinyal, dan orang tua belum menunggu di atasnya. Dalam hal ini, bahkan jika sudah keluar, pid dicadangkan (inilah "proses zombie") sampai orang tua menunggu di atasnya. Saya tidak mengetahui cara untuk melakukan itu dengan bersih dengan shell.
Cara aman alternatif untuk mematikan proses adalah memulainya dengan tty pengendali yang disetel ke terminal semu yang Anda miliki dari sisi master. Anda kemudian dapat mengirim sinyal melalui terminal, misalnya menulis karakter untuk
SIGTERM
atauSIGQUIT
lebih dari pty.Namun cara lain yang lebih nyaman dengan scripting adalah dengan menggunakan
screen
sesi bernama dan mengirim perintah ke sesi layar untuk mengakhirinya. Proses ini berlangsung di atas pipa atau soket unix bernama sesuai dengan sesi layar, yang tidak akan secara otomatis digunakan kembali jika Anda memilih nama unik yang aman.sumber
Saat meluncurkan proses, hemat waktu mulainya:
Sebelum mencoba untuk menghentikan proses hentikan itu (ini tidak benar-benar penting, tetapi ini adalah cara untuk menghindari kondisi balapan: jika Anda menghentikan proses, itu pid tidak dapat digunakan kembali)
Periksa bahwa proses dengan PID itu memiliki waktu mulai yang sama dan jika ya, bunuh, jika tidak biarkan proses berlanjut:
Ini berfungsi karena hanya ada satu proses dengan PID yang sama dan waktu mulai pada OS yang diberikan.
Menghentikan proses selama pemeriksaan membuat kondisi ras tidak menjadi masalah. Jelas ini memiliki masalah itu, beberapa proses acak dapat dihentikan selama beberapa milidetik. Bergantung pada jenis proses ini mungkin atau mungkin tidak menjadi masalah.
Secara pribadi saya cukup menggunakan python dan
psutil
yang menangani penggunaan kembali PID secara otomatis:sumber
ps -o start=
format berubah dari 18:12 menjadi Jan26 setelah beberapa saat. Waspadalah terhadap perubahan DST juga. Jika di Linux, Anda mungkin lebih sukaTZ=UTC0 ps -o lstart=
.lstart
, saya akan mengeditnya.Pada sistem linux, Anda dapat memastikan bahwa pid tidak akan digunakan kembali dengan menjaga namespace pid-nya tetap hidup. Ini dapat dilakukan melalui
/proc/$pid/ns/pid
file.man namespaces
-init
.man pid_namespaces
-util-linux
paket menyediakan banyak alat yang berguna untuk memanipulasi ruang nama. Misalnya, adaunshare
, jika Anda belum mengatur haknya di ruang nama pengguna, itu akan membutuhkan hak pengguna super:Jika Anda belum mengatur namespace pengguna, maka Anda masih dapat dengan aman menjalankan perintah arbitrer dengan segera menjatuhkan hak istimewa. The
runuser
perintah lain (non setuid) biner yang disediakan olehutil-linux
paket dan menggabungkan mungkin terlihat seperti:...dan seterusnya.
Dalam contoh di atas dua switch dilewatkan ke
unshare(1)
dalam--fork
bendera yang membuat dipanggilsh -c
proses anak pertama dibuat dan memastikan nyainit
status, dan--pid
bendera yang menginstruksikanunshare(1)
untuk membuat namespace pid.The
sh -c
Proses menumbuhkan lima kerang anak dilatarbelakangi - masing sebuah inifinitewhile
loop yang akan terus menambahkan output daridate
ke ujunglog
selamasleep 1
pengembalian benar. Setelah menelurkan proses inish
panggilansleep
selama 5 detik tambahan kemudian berakhir.Mungkin perlu dicatat bahwa jika
-f
bendera tidak digunakan, tidak ada yang berlatar belakangwhile
loop akan berakhir, tetapi dengan itu ...KELUARAN:
sumber
Pertimbangkan untuk membuat Anda
longrunningthing
berperilaku sedikit lebih baik, sedikit lebih seperti daemon. Misalnya, Anda dapat membuatnya membuat pidfile yang akan memungkinkan setidaknya beberapa kontrol terbatas dari proses. Ada beberapa cara untuk melakukan ini tanpa memodifikasi biner asli, semuanya melibatkan pembungkus. Sebagai contoh:skrip pembungkus sederhana yang akan memulai pekerjaan yang diperlukan di latar belakang (dengan redirection output opsional), tulis PID dari proses ini ke dalam file, kemudian tunggu proses untuk menyelesaikan (menggunakan
wait
) dan menghapus file. Jika selama menunggu proses akan terbunuh misalnya oleh sesuatu sepertibungkusnya hanya akan memastikan pidfile dihapus.
bungkus monitor, yang akan menempatkan PID -nya sendiri di suatu tempat dan menangkap (dan merespons) sinyal yang dikirim kepadanya. Contoh sederhana:
Sekarang, seperti yang ditunjukkan oleh @R .. dan @ StéphaneChazelas, pendekatan ini sering memiliki kondisi balapan di suatu tempat atau memaksakan batasan pada jumlah proses yang dapat Anda hasilkan. Selain itu, tidak menangani kasus, di mana
longrunningthing
garpu dan anak-anak bisa terlepas (yang mungkin bukan apa masalah dalam pertanyaan awal).Dengan kernel Linux baru-baru ini (baca beberapa tahun), ini dapat diperlakukan dengan baik dengan menggunakan cgroup , yaitu freezer - yang, saya kira, adalah apa yang digunakan oleh beberapa sistem init Linux modern.
sumber
longrunningthing
adalah bahwa Anda tidak memiliki kendali atas apa itu. Saya juga memberikan contoh skrip shell karena menjelaskan masalahnya. Saya suka milik Anda, dan semua solusi kreatif lainnya di sini, tetapi jika Anda menggunakan Linux / bash, ada batas waktu "built-in" untuk itu. Saya kira saya harus mendapatkan sumber itu dan melihat bagaimana melakukannya!timeout
adalah tidak builtin shell. Ada berbagai implementasitimeout
perintah untuk Linux, satu baru-baru ini (2008) ditambahkan ke GNU coreutils (jadi tidak spesifik untuk Linux), dan itulah yang sebagian besar distribusi Linux gunakan saat ini.Jika Anda menjalankan Linux (dan beberapa * nixes lainnya), Anda dapat memeriksa apakah proses yang ingin Anda bunuh masih digunakan dan bahwa baris perintah cocok dengan proses panjang Anda. Sesuatu seperti :
Alternatifnya adalah memeriksa berapa lama proses yang ingin Anda bunuh berjalan, dengan sesuatu seperti
ps -p $p -o etime=
. Anda dapat melakukannya sendiri dengan mengekstraksi informasi ini dari/proc/$p/stat
, tetapi ini akan sulit (waktu diukur dalam jiffies, dan Anda harus menggunakan waktu kerja sistem dalam/proc/stat
juga).Bagaimanapun, Anda biasanya tidak dapat memastikan bahwa proses tersebut tidak diganti setelah cek dan sebelum Anda membunuhnya.
sumber
cat pidfile
hasilnya secara blak-blakan . Saya tidak ingat cara bersih untuk melakukannya di shell saja. Namun, jawaban namespace yang diusulkan tampaknya menarik ...Ini sebenarnya pertanyaan yang sangat bagus.
Cara untuk menentukan keunikan proses adalah dengan melihat (a) di mana ia berada dalam memori; dan (b) apa isi memori itu. Untuk lebih spesifik, kami ingin tahu di mana dalam memori adalah teks program untuk doa awal, karena kami tahu bahwa area teks dari setiap utas akan menempati lokasi yang berbeda dalam memori. Jika proses mati dan yang lain diluncurkan dengan pid yang sama, teks program untuk proses baru tidak akan menempati tempat yang sama di memori dan tidak akan berisi informasi yang sama.
Jadi, segera setelah meluncurkan proses Anda, lakukan
md5sum /proc/[pid]/maps
dan simpan hasilnya. Kemudian ketika Anda ingin mematikan proses, lakukan md5sum lain dan bandingkan. Jika cocok, maka bunuh pid itu. Jika tidak, jangan.untuk melihat ini sendiri, luncurkan dua shell bash yang identik. Periksa
/proc/[pid]/maps
untuk mereka dan Anda akan menemukan bahwa mereka berbeda. Mengapa? Karena meskipun itu adalah program yang sama, mereka menempati lokasi yang berbeda dalam memori dan alamat tumpukan mereka berbeda. Jadi, jika proses Anda mati dan PID digunakan kembali, bahkan dengan perintah yang sama diluncurkan kembali dengan argumen yang sama , file "peta" akan berbeda dan Anda akan tahu bahwa Anda tidak berurusan dengan proses asli.Lihat: halaman manual proc untuk detailnya.
Perhatikan bahwa file tersebut
/proc/[pid]/stat
sudah berisi semua informasi yang disebutkan oleh poster lain dalam jawaban mereka: usia proses, orang tua pid, dll. File ini berisi informasi statis dan informasi dinamis, jadi jika Anda lebih suka menggunakan file ini sebagai basis perbandingan, lalu saat meluncurkanlongrunningthing
, Anda perlu mengekstrak bidang statis berikut daristat
file dan menyimpannya untuk perbandingan nanti:pid, nama file, pid orang tua, id grup proses, terminal pengendali, waktu proses dimulai setelah boot sistem, ukuran set penduduk, alamat mulai stack,
secara bersama-sama, proses di atas secara unik mengidentifikasi proses, dan karenanya ini merupakan cara lain untuk melangkah. Sebenarnya Anda bisa lolos dengan tidak lebih dari "pid" dan "proses waktu dimulai setelah boot sistem" dengan tingkat kepercayaan yang tinggi. Cukup ekstrak bidang ini dari
stat
file dan simpan di suatu tempat setelah meluncurkan proses Anda. Kemudian sebelum membunuhnya, ekstrak lagi dan bandingkan. Jika mereka cocok, maka Anda yakin Anda sedang melihat proses aslinya.sumber
/proc/[pid]/maps
perubahan dari waktu ke waktu sebagai alokasi memori tambahan atau tumpukan tumbuh atau file baru mmapped ... Dan apa artinya segera setelah peluncuran berarti? Setelah semua perpustakaan di-mmapped? Bagaimana cara anda menentukan itu?md5sum
pada file peta mereka. Saya akan membiarkannya berjalan selama satu atau dua hari dan melaporkan kembali ke sini dengan hasilnya.Cara lain adalah memeriksa usia proses sebelum membunuhnya. Dengan begitu, Anda dapat memastikan Anda tidak membunuh proses yang tidak muncul dalam waktu kurang dari 24 jam. Anda dapat menambahkan
if
kondisi berdasarkan itu sebelum mematikan proses.if
Kondisi ini akan memeriksa apakah ID proses$p
kurang dari 24 jam (86400 detik).PS: - Perintah
ps -p $p -o etime=
akan memiliki format<no.of days>-HH:MM:SS
sumber
mtime
dari/proc/$p
tidak ada hubungannya dengan waktu mulai dari proses.if
kondisi. Jangan ragu untuk berkomentar jika itu bermasalah.Apa yang saya lakukan adalah, setelah membunuh prosesnya, lakukan lagi. Setiap kali saya melakukannya, jawabannya kembali, "tidak ada proses seperti itu"
Tidak bisa lebih sederhana dan saya sudah melakukan ini selama bertahun-tahun tanpa masalah.
sumber