Cara menyebarkan sinyal ke proses anak dari skrip bash
- 3475
- 104
- Simon Cormier
Misalkan kita menulis skrip yang melahirkan satu atau lebih proses berjalan lama; Jika skrip tersebut menerima sinyal seperti Sigint
atau Sigterm
, Kami mungkin ingin anak -anaknya diberhentikan juga (biasanya ketika orang tua meninggal, anak -anak bertahan). Kami mungkin juga ingin melakukan beberapa tugas pembersihan sebelum skrip itu sendiri keluar. Untuk dapat mencapai tujuan kami, kami harus terlebih dahulu belajar tentang kelompok proses dan cara menjalankan proses di latar belakang.
Dalam tutorial ini Anda akan belajar:
- Apa itu kelompok proses
- Perbedaan antara proses latar depan dan latar belakang
- Cara menjalankan program di latar belakang
- Cara menggunakan shell
Tunggu
Dibangun untuk menunggu proses yang dieksekusi di latar belakang - Cara menghentikan proses anak ketika orang tua menerima sinyal
Persyaratan dan konvensi perangkat lunak yang digunakan
Kategori | Persyaratan, konvensi atau versi perangkat lunak yang digunakan |
---|---|
Sistem | Distribusi Independen |
Perangkat lunak | Tidak ada perangkat lunak khusus yang dibutuhkan |
Lainnya | Tidak ada |
Konvensi | # - mensyaratkan perintah linux yang diberikan untuk dieksekusi dengan hak istimewa root baik secara langsung sebagai pengguna root atau dengan menggunakan sudo memerintah$ - mensyaratkan perintah Linux yang diberikan untuk dieksekusi sebagai pengguna biasa |
Contoh sederhana
Mari kita buat skrip yang sangat sederhana dan simulasikan peluncuran proses berjalan lama:
#!/Bin/Bash Trap "Sinyal gema diterima!"Sigint Echo" skrip pid adalah $ "sleep 30
Menyalin Hal pertama yang kami lakukan dalam naskah adalah membuat jebakan untuk ditangkap Sigint
dan cetak pesan saat sinyal diterima. Kami daripada membuat skrip kami mencetaknya pid: Kita bisa mendapatkan dengan memperluas $$
variabel. Selanjutnya, kami mengeksekusi tidur
perintah untuk mensimulasikan proses berjalan yang lama (30
detik).
Kami menyimpan kode di dalam file (katakan itu dipanggil tes.SH
), membuatnya dapat dieksekusi, dan meluncurkannya dari emulator terminal. Kami mendapatkan hasil berikut:
Skrip PID adalah 101248
Jika kita fokus pada emulator terminal dan tekan Ctrl+C saat skrip sedang berjalan, a Sigint
sinyal dikirim dan ditangani oleh kami perangkap:
Skrip PID adalah 101248 ^CSignal yang diterima!
Meskipun jebakan menangani sinyal seperti yang diharapkan, naskah itu terputus. Mengapa ini terjadi? Selanjutnya, jika kami mengirim a Sigint
sinyal ke skrip menggunakan membunuh
perintah, hasil yang kami peroleh sangat berbeda: jebakan tidak segera dieksekusi, dan skrip berlangsung sampai proses anak tidak keluar (setelah 30
detik "tidur"). Mengapa perbedaan ini? Mari kita lihat…
Kelompok proses, latar depan dan pekerjaan latar belakang
Sebelum kita menjawab pertanyaan di atas, kita harus lebih memahami konsep kelompok proses.
Grup proses adalah sekelompok proses yang memiliki hal yang sama PGID (Proses ID Grup). Ketika anggota kelompok proses menciptakan proses anak, proses itu menjadi anggota dari kelompok proses yang sama. Setiap kelompok proses memiliki pemimpin; kita dapat dengan mudah mengenalinya karena itu pid dan PGID adalah sama.
Kami dapat memvisualisasikan pid Dan PGID menjalankan proses menggunakan ps
memerintah. Output dari perintah dapat disesuaikan sehingga hanya bidang yang kami minati ditampilkan: dalam hal ini Cmd, Pid Dan PGID. Kami melakukan ini dengan menggunakan -Hai
opsi, memberikan daftar bidang yang dipisahkan secara koma sebagai argumen:
$ ps -a -o pid, pgid, cmd
Jika kami menjalankan perintah saat skrip kami menjalankan bagian yang relevan dari output yang kami peroleh adalah sebagai berikut:
PID PGID CMD 298349 298349 /BIN /BASH ./tes.SH 298350 298349 Tidur 30
Kita dapat dengan jelas melihat dua proses: pid yang pertama adalah 298349
, Sama seperti itu PGID: ini adalah pemimpin kelompok proses. Itu dibuat ketika kami meluncurkan skrip seperti yang Anda lihat di Cmd kolom.
Proses utama ini meluncurkan proses anak dengan perintah tidur 30
: Seperti yang diharapkan kedua proses berada dalam kelompok proses yang sama.
Ketika kami menekan Ctrl-C sambil berfokus pada terminal dari mana skrip diluncurkan, sinyal tidak hanya dikirim ke proses induk, tetapi ke seluruh kelompok proses. Grup proses mana? Itu Grup proses latar depan terminal. Semua Proses Anggota Grup ini dipanggil proses latar depan, Semua yang lain dipanggil proses latar belakang. Inilah yang dikatakan Bash Manual tentang masalah ini:
TAHUKAH KAMU?Untuk memfasilitasi implementasi antarmuka pengguna ke kontrol pekerjaan, sistem operasi mempertahankan gagasan ID grup proses terminal saat ini. Anggota Grup Proses ini (Prosesnya ID Grup Prosesnya sama dengan ID Grup Proses Terminal Saat Ini) Menerima sinyal yang dihasilkan keyboard seperti SIGINT. Proses -proses ini dikatakan berada di latar depan. Proses latar belakang adalah mereka yang ID grup prosesnya berbeda dari terminal; Proses seperti itu kebal terhadap sinyal yang dihasilkan keyboard.Saat kami mengirim Sigint
sinyal dengan membunuh
Perintah, sebaliknya, kami hanya menargetkan PID dari proses induk; Bash menunjukkan perilaku tertentu ketika sinyal diterima saat menunggu program selesai: "kode perangkap" untuk sinyal itu tidak dieksekusi sampai proses itu selesai. Inilah sebabnya mengapa pesan "sinyal diterima" ditampilkan hanya setelah tidur
perintah keluar.
Untuk mereplikasi apa yang terjadi ketika kita menekan ctrl-c di terminal menggunakan membunuh
Perintah untuk mengirim sinyal, kita harus menargetkan grup proses. Kami dapat mengirim sinyal ke grup proses dengan menggunakan negasi pid pemimpin proses, Jadi, seandainya pid pemimpin prosesnya 298349
(Seperti pada contoh sebelumnya), kami akan menjalankan:
$ kill -2 -298349
Kelola propagasi sinyal dari dalam skrip
Sekarang, misalkan kita meluncurkan skrip yang sudah berjalan lama dari shell non -interaktif, dan kami ingin skrip tersebut untuk mengelola perambatan sinyal secara otomatis, sehingga ketika menerima sinyal seperti Sigint
atau Sigterm
itu mengakhiri anaknya yang berpotensi lama, akhirnya melakukan beberapa tugas pembersihan sebelum keluar. Bagaimana kita bisa melakukan ini?
Seperti yang kami lakukan sebelumnya, kami dapat menangani situasi di mana sinyal diterima dalam perangkap; Namun, seperti yang kita lihat, jika sinyal diterima saat shell menunggu program selesai, "kode perangkap" dieksekusi hanya setelah proses anak keluar.
Ini bukan yang kami inginkan: kami ingin kode perangkap diproses segera setelah proses induk menerima sinyal. Untuk mencapai tujuan kami, kami harus melaksanakan proses anak di latar belakang: kita bisa melakukan ini dengan menempatkan &
simbol setelah perintah. Dalam kasus kami, kami akan menulis:
#!/Bin/Bash Trap 'sinyal gema diterima!'Sigint echo "skrip pid adalah $" sleep 30 &
Menyalin Jika kita akan meninggalkan skrip dengan cara ini, proses induk akan keluar tepat setelah eksekusi tidur 30
perintah, meninggalkan kami tanpa kesempatan untuk melakukan tugas pembersihan setelah berakhir atau terganggu. Kita dapat menyelesaikan masalah ini dengan menggunakan shell Tunggu
dibangun. Halaman Bantuan Tunggu
mendefinisikannya dengan cara ini:
Menunggu setiap proses yang diidentifikasi oleh ID, yang mungkin merupakan ID proses atau spesifikasi pekerjaan, dan melaporkan status penghentiannya. Jika ID tidak diberikan, tunggu semua proses anak yang aktif saat ini, dan status pengembalian nol.
Setelah kami menetapkan proses untuk dieksekusi di latar belakang, kami dapat mengambilnya pid dalam $!
variabel. Kita bisa meneruskannya sebagai argumen Tunggu
Untuk membuat proses induk menunggu anaknya:
#!/Bin/Bash Trap 'sinyal gema diterima!'Sigint echo "skrip pid adalah $" sleep 30 & tunggu $!
Menyalin Sudahkah kita selesai? Tidak, masih ada masalah: penerimaan sinyal yang ditangani dalam perangkap di dalam naskah, menyebabkan Tunggu
Builtin untuk segera kembali, tanpa benar -benar menunggu penghentian perintah di latar belakang. Perilaku ini didokumentasikan dalam manual bash:
Untuk menyelesaikan masalah ini yang harus kita gunakan Tunggu
Sekali lagi, mungkin sebagai bagian dari jebakan itu sendiri. Inilah yang terlihat seperti skrip kami pada akhirnya:
#!/bin/bash cleanup () echo "cleaning up…" # kode pembersihan kami pergi ke sini trap 'sinyal gema diterima!; kill "$ child_pid"; tunggu "$ child_pid"; cleanup 'sigint sigterm echo "skrip pid adalah $" sleep 30 & child_pid = "$!"tunggu" $ child_pid "
Menyalin Dalam skrip kami membuat a membersihkan
fungsi di mana kami dapat memasukkan kode pembersihan kami, dan membuat kami perangkap
Tangkap juga Sigterm
sinyal. Inilah yang terjadi ketika kita menjalankan skrip ini dan mengirim salah satu dari dua sinyal untuk itu:
- Skrip diluncurkan dan
tidur 30
Perintah dieksekusi di latar belakang; - Itu pid dari proses anak "disimpan" di
Child_pid
variabel; - Script menunggu penghentian proses anak;
- Skrip menerima
Sigint
atauSigterm
sinyal - Itu
Tunggu
Komando segera kembali, tanpa menunggu pemutusan anak;
Pada titik ini jebakan dieksekusi. Di dalamnya:
- A
Sigterm
sinyal (membunuh
default) dikirim keChild_pid
; - Kami
Tunggu
untuk memastikan anak diberhentikan setelah menerima sinyal ini. - Setelah
Tunggu
kembali, kami menjalankanmembersihkan
fungsi.
Menyebarkan sinyal ke banyak anak
Dalam contoh di atas kami bekerja dengan skrip yang hanya memiliki satu proses anak. Bagaimana jika sebuah naskah memiliki banyak anak, dan bagaimana jika beberapa dari mereka memiliki anak sendiri?
Dalam kasus pertama, satu cara cepat untuk mendapatkan PIDS dari semua anak adalah menggunakan pekerjaan -p
Perintah: Perintah ini menampilkan PID dari semua pekerjaan aktif di shell saat ini. Kami bisa daripada menggunakan membunuh
untuk mengakhiri mereka. Inilah contohnya:
#!/bin/bash cleanup () echo "cleaning up…" # kode pembersihan kami pergi ke sini trap 'sinyal gema diterima!; Bunuh $ (pekerjaan -p); Tunggu; Cleanup 'sigint sigterm echo "skrip pid adalah $" sleep 30 & sleep 40 & tunggu
Menyalin Script meluncurkan dua proses di latar belakang: dengan menggunakan Tunggu
dibangun tanpa argumen, kami menunggu semuanya, dan menjaga proses induk tetap hidup. Ketika Sigint
atau Sigterm
Sinyal diterima oleh skrip, kami mengirim a Sigterm
Bagi mereka berdua, pids mereka dikembalikan oleh pekerjaan -p
memerintah (pekerjaan
itu sendiri merupakan shell built-in, jadi ketika kita menggunakannya, proses baru tidak dibuat).
Jika anak -anak memiliki anak -anak proses mereka sendiri, dan kami ingin menghentikan mereka semua ketika leluhur menerima sinyal, kami dapat mengirim sinyal ke seluruh kelompok proses, seperti yang kita lihat sebelumnya.
Ini, bagaimanapun, menghadirkan masalah, karena dengan mengirimkan sinyal terminasi ke kelompok proses, kami akan memasukkan loop "Sinyal-Sent/Signal Trapped". Pikirkan tentang hal ini: di perangkap
untuk Sigterm
kami mengirim a Sigterm
sinyal untuk semua anggota kelompok proses; Ini termasuk skrip induk itu sendiri!
Untuk mengatasi masalah ini dan masih dapat menjalankan fungsi pembersihan setelah proses anak diakhiri, kita harus mengubah perangkap
untuk Sigterm
Tepat sebelum kami mengirim sinyal ke grup proses, misalnya:
#!/bin/bash cleanup () echo "cleaning up ..." # Kode pembersihan kami pergi ke sini jebakan 'jebakan "" sigterm; Bunuh 0; Tunggu; Cleanup 'sigint sigterm echo "skrip pid adalah $" sleep 30 & sleep 40 & tunggu
Menyalin Di dalam perangkap, sebelum mengirim Sigterm
Ke grup proses, kami mengubah Sigterm
jebakan, sehingga proses induk mengabaikan sinyal dan hanya keturunannya yang terpengaruh olehnya. Perhatikan juga bahwa dalam perangkap, untuk memberi sinyal kelompok proses, kami gunakan membunuh
dengan 0
sebagai pid. Ini adalah semacam jalan pintas: saat pid diteruskan ke membunuh
adalah 0
, semua proses di saat ini Grup proses ditandai.
Kesimpulan
Dalam tutorial ini kami belajar tentang kelompok proses dan apa perbedaan antara proses latar depan dan latar belakang. Kami belajar bahwa Ctrl-C mengirimkan a Sigint
Sinyal ke seluruh kelompok proses latar depan terminal pengendali, dan kami belajar cara mengirim sinyal ke kelompok proses menggunakan membunuh
. Kami juga belajar cara menjalankan program di latar belakang, dan cara menggunakan Tunggu
shell built in untuk menunggu agar keluar tanpa kehilangan cangkang induk. Akhirnya, kami melihat cara mengatur skrip sehingga ketika menerima sinyal, ia mengakhiri anak -anaknya sebelum keluar. Apakah saya merindukan sesuatu? Apakah Anda memiliki resep pribadi untuk menyelesaikan tugas? Jangan ragu untuk memberi tahu saya!
Tutorial Linux Terkait:
- Manajemen proses latar belakang bash
- Multi-threaded Bash Scripting & Manajemen Proses di…
- Pengantar Otomatisasi Linux, Alat dan Teknik
- Cara menjalankan perintah di latar belakang di linux
- Menguasai loop skrip bash
- Cara Menginstal Sinyal di Linux
- Cara melacak panggilan sistem yang dilakukan oleh proses dengan strace di…
- Mint 20: Lebih baik dari Ubuntu dan Microsoft Windows?
- Membandingkan Linux Apache Prefork vs Pekerja MPM
- Cara bekerja dengan grup paket DNF