Cara menyebarkan isyarat kepada proses kanak -kanak dari skrip bash

Cara menyebarkan isyarat kepada proses kanak -kanak dari skrip bash

Katakan kita menulis skrip yang memancarkan satu atau lebih proses berjalan lama; Sekiranya skrip tersebut menerima isyarat seperti Sigint atau Sigterm, Kami mungkin mahu anak -anaknya ditamatkan juga (biasanya apabila ibu bapa mati, anak -anak bertahan). Kami juga mungkin mahu melaksanakan beberapa tugas pembersihan sebelum skrip itu sendiri keluar. Untuk dapat mencapai matlamat kita, kita mesti terlebih dahulu belajar tentang kumpulan proses dan bagaimana melaksanakan proses di latar belakang.

Dalam tutorial ini anda akan belajar:

  • Apa itu kumpulan proses
  • Perbezaan antara proses latar depan dan latar belakang
  • Cara melaksanakan program di latar belakang
  • Cara menggunakan cangkang tunggu dibina untuk menunggu proses yang dilaksanakan di latar belakang
  • Cara menamatkan proses kanak -kanak apabila ibu bapa menerima isyarat
Cara menyebarkan isyarat kepada proses kanak -kanak dari skrip bash

Keperluan perisian dan konvensyen yang digunakan

Keperluan Perisian dan Konvensyen Talian Perintah Linux
Kategori Keperluan, konvensyen atau versi perisian yang digunakan
Sistem Pengedaran bebas
Perisian Tiada perisian khusus diperlukan
Yang lain Tiada
Konvensyen # - Memerlukan arahan Linux yang diberikan untuk dilaksanakan dengan keistimewaan akar sama ada secara langsung sebagai pengguna root atau dengan menggunakan sudo perintah
$ - Memerlukan arahan Linux yang diberikan sebagai pengguna yang tidak layak

Contoh mudah

Mari buat skrip yang sangat mudah dan simulasi pelancaran proses jangka panjang:

#!/Bin/Bash Trap "isyarat echo diterima!"Sigint echo" skrip pid adalah $ "tidur 30 
Salinan

Perkara pertama yang kami lakukan dalam skrip adalah untuk membuat perangkap untuk menangkap Sigint dan mencetak mesej apabila isyarat diterima. Kami daripada membuat skrip kami mencetaknya pid: kita boleh mendapatkan dengan mengembangkan $$ pembolehubah. Seterusnya, kami melaksanakannya tidur perintah untuk mensimulasikan proses jangka panjang (30 saat).

Kami menyimpan kod di dalam fail (katakan ia dipanggil ujian.sh), menjadikannya boleh dilaksanakan, dan melancarkannya dari emulator terminal. Kami memperoleh hasil berikut:

PID skrip ialah 101248 

Sekiranya kita memberi tumpuan kepada emulator terminal dan tekan Ctrl+C semasa skrip berjalan, a Sigint isyarat dihantar dan dikendalikan oleh kami perangkap:

Skrip pid adalah 101248 ^csignal diterima! 

Walaupun perangkap mengendalikan isyarat seperti yang diharapkan, skrip itu terganggu pula. Kenapa ini berlaku? Tambahan pula, jika kita menghantar a Sigint isyarat kepada skrip menggunakan bunuh perintah, hasil yang kami peroleh agak berbeza: perangkap tidak segera dilaksanakan, dan skrip terus berjalan sehingga proses anak tidak keluar (selepas 30 detik "Tidur"). Kenapa perbezaan ini? Mari lihat ..

Kumpulan proses, pekerjaan latar depan dan latar belakang

Sebelum kita menjawab soalan -soalan di atas, kita harus memahami konsep lebih baik kumpulan proses.

Kumpulan proses adalah sekumpulan proses yang berkongsi perkara yang sama pgid (Proses ID Kumpulan). Apabila ahli kumpulan proses mencipta proses kanak -kanak, proses itu menjadi ahli kumpulan proses yang sama. Setiap kumpulan proses mempunyai pemimpin; kita dapat dengan mudah mengenalinya kerana itu pid dan juga pgid sama.

Kita boleh memvisualisasikan pid dan pgid proses berjalan menggunakan ps perintah. Output arahan boleh disesuaikan supaya hanya bidang yang kami minati dipaparkan: dalam kes ini Cmd, Pid dan Pgid. Kami melakukan ini dengan menggunakan -o pilihan, menyediakan senarai medan yang dipisahkan koma sebagai hujah:

$ ps -a -o pid, pgid, cmd 

Jika kita menjalankan arahan semasa skrip kita menjalankan bahagian yang relevan dari output yang kita perolehi adalah yang berikut:

 PID PGID CMD 298349 298349 /bin /bash ./ujian.SH 298350 298349 Tidur 30 

Kita dapat melihat dua proses dengan jelas: pid yang pertama adalah 298349, Sama seperti pgid: ini adalah pemimpin kumpulan proses. Ia dicipta ketika kami melancarkan skrip seperti yang anda dapat lihat di Cmd kolum.

Proses utama ini melancarkan proses kanak -kanak dengan arahan Tidur 30: Seperti yang dijangkakan kedua -dua proses berada dalam kumpulan proses yang sama.

Ketika kami menekan Ctrl-C sambil memberi tumpuan kepada terminal dari mana skrip itu dilancarkan, isyarat tidak hanya dihantar ke proses induk, tetapi ke seluruh kumpulan proses. Kumpulan proses mana? The Kumpulan Proses Foreground terminal. Semua proses ahli kumpulan ini dipanggil proses latar depan, semua yang lain dipanggil proses latar belakang. Inilah yang dikatakan oleh manual bash mengenai perkara itu:

ADAKAH KAMU TAHU?Untuk memudahkan pelaksanaan antara muka pengguna ke kawalan kerja, sistem operasi mengekalkan tanggapan mengenai ID Kumpulan Proses Terminal semasa. Ahli kumpulan proses ini (proses yang id kumpulan prosesnya sama dengan id kumpulan proses terminal semasa) menerima isyarat yang dihasilkan oleh papan kekunci seperti SigInt. Proses ini dikatakan berada di latar depan. Proses latar belakang adalah mereka yang proses ID kumpulan berbeza dari terminal; Proses sedemikian kebal terhadap isyarat yang dihasilkan oleh papan kekunci.

Semasa kami menghantar Sigint isyarat dengan bunuh Perintah, sebaliknya, kami hanya menyasarkan PID proses induk; Bash mempamerkan tingkah laku tertentu apabila isyarat diterima semasa menunggu program selesai: "kod perangkap" untuk isyarat itu tidak dilaksanakan sehingga proses itu selesai. Inilah sebabnya mesej "isyarat yang diterima" dipaparkan hanya selepas tidur Perintah keluar.

Untuk meniru apa yang berlaku apabila kita menekan Ctrl-C di terminal menggunakan bunuh Perintah untuk menghantar isyarat, kita mesti menargetkan kumpulan proses. Kami boleh menghantar isyarat kepada kumpulan proses dengan menggunakan penolakan pid pemimpin proses, Jadi, anggaplah pid pemimpin proses adalah 298349 (Seperti dalam contoh sebelumnya), kami akan menjalankan:

$ KILL -2 -298349 

Urus penyebaran isyarat dari dalam skrip

Sekarang, katakan kami melancarkan skrip jangka panjang dari shell yang tidak interaktif, dan kami mahu skrip berkata untuk menguruskan penyebaran isyarat secara automatik, supaya apabila ia menerima isyarat seperti Sigint atau Sigterm ia menamatkan anaknya yang berpotensi berlari, akhirnya melaksanakan beberapa tugas pembersihan sebelum keluar. Bagaimana kita dapat melakukan ini?

Seperti yang kita lakukan sebelum ini, kita boleh mengendalikan keadaan di mana isyarat diterima dalam perangkap; Walau bagaimanapun, seperti yang kita lihat, jika isyarat diterima semasa shell yang menunggu program selesai, "kod perangkap" hanya dilaksanakan selepas proses anak keluar keluar.

Ini bukan apa yang kita mahukan: kita mahu kod perangkap diproses sebaik sahaja proses induk menerima isyarat. Untuk mencapai matlamat kami, kami mesti melaksanakan proses kanak -kanak di latar belakang: kita boleh melakukan ini dengan meletakkan & simbol selepas arahan. Dalam kes kita kita akan menulis:

#!/Bin/Bash Trap 'isyarat echo diterima!'Sigint echo "skrip pid adalah $" tidur 30 & 
Salinan

Sekiranya kita akan meninggalkan skrip dengan cara ini, proses induk akan keluar tepat selepas pelaksanaan Tidur 30 perintah, meninggalkan kami tanpa peluang untuk melaksanakan tugas pembersihan setelah berakhir atau terganggu. Kita dapat menyelesaikan masalah ini dengan menggunakan cangkang tunggu dibina dalam. Halaman bantuan tunggu mentakrifkannya dengan cara ini:



Menunggu setiap proses yang dikenal pasti oleh ID, yang mungkin merupakan ID proses atau spesifikasi pekerjaan, dan melaporkan status penamatannya. Sekiranya ID tidak diberikan, menunggu semua proses kanak -kanak yang sedang aktif, dan status pulangan adalah sifar.

Setelah kami menetapkan proses yang akan dilaksanakan di latar belakang, kami dapat mengambilnya pid di dalam $! pembolehubah. Kita boleh lulus sebagai hujah untuk tunggu Untuk membuat proses induk menunggu anaknya:

#!/Bin/Bash Trap 'isyarat echo diterima!'Sigint echo "skrip pid adalah $" tidur 30 & tunggu $! 
Salinan

Adakah kita selesai? Tidak, masih ada masalah: penerimaan isyarat yang dikendalikan dalam perangkap di dalam skrip, menyebabkan tunggu dibina untuk kembali dengan segera, tanpa benar -benar menunggu penamatan perintah di latar belakang. Tingkah laku ini didokumenkan dalam manual bash:

Apabila Bash sedang menunggu arahan Asynchronous melalui tunggu yang dibina, penerimaan isyarat yang mana perangkap telah ditetapkan akan menyebabkan tunggu dibina untuk kembali dengan segera dengan status keluar lebih besar daripada 128, dengan segera selepas itu perangkap dilaksanakan. Ini bagus, kerana isyarat ditangani dengan segera dan perangkap dilaksanakan tanpa perlu menunggu anak itu menamatkan, tetapi menimbulkan masalah, kerana dalam perangkap kami, kami ingin melaksanakan tugas pembersihan kami hanya setelah kami yakin anak itu proses keluar.

Untuk menyelesaikan masalah ini yang harus kita gunakan tunggu Sekali lagi, mungkin sebagai sebahagian daripada perangkap itu sendiri. Inilah skrip kami yang boleh kelihatan seperti pada akhirnya:

#!/bin/bash cleanup () echo "membersihkan ..." # kod pembersihan kami pergi ke sini perangkap 'echo isyarat diterima!; membunuh "$ child_pid"; tunggu "$ child_pid"; CLEANUP 'SIGINT SIGTERM ECHO "Skrip PID adalah $" Tidur 30 & Child_pid = "$!"tunggu" $ child_pid " 
Salinan

Dalam skrip yang kami buat a bersihkan fungsi di mana kita boleh memasukkan kod pembersihan kita, dan menjadikannya perangkap menangkap juga Sigterm isyarat. Inilah yang berlaku apabila kita menjalankan skrip ini dan menghantar salah satu daripada dua isyarat kepadanya:

  1. Skrip dilancarkan dan Tidur 30 Perintah dilaksanakan di latar belakang;
  2. The pid proses kanak -kanak "disimpan" di Child_pid pemboleh ubah;
  3. Skrip menunggu penamatan proses kanak -kanak;
  4. Skrip menerima a Sigint atau Sigterm isyarat
  5. The tunggu Perintah kembali dengan serta -merta, tanpa menunggu penamatan kanak -kanak;

Pada ketika ini perangkap dilaksanakan. Di dalamnya:

  1. A Sigterm isyarat (yang bunuh lalai) dihantar ke Child_pid;
  2. Kita tunggu untuk memastikan anak itu ditamatkan setelah menerima isyarat ini.
  3. Selepas tunggu pulangan, kami melaksanakan bersihkan fungsi.

Menyebarkan isyarat kepada beberapa kanak -kanak

Dalam contoh di atas kami bekerja dengan skrip yang hanya mempunyai satu proses anak. Bagaimana jika skrip mempunyai banyak anak, dan bagaimana jika sebahagian daripada mereka mempunyai anak sendiri?

Dalam kes pertama, satu cara cepat untuk mendapatkan PIDS dari semua kanak -kanak adalah menggunakan Pekerjaan -P Perintah: Perintah ini memaparkan PIDs semua pekerjaan aktif dalam cangkang semasa. Kita boleh daripada menggunakan bunuh untuk menamatkan mereka. Berikut adalah contoh:

#!/bin/bash cleanup () echo "membersihkan ..." # kod pembersihan kami pergi ke sini perangkap 'echo isyarat diterima!; membunuh $ (pekerjaan -p); tunggu; pembersihan 'sigint sigterm echo "skrip pid adalah $" tidur 30 & tidur 40 & tunggu 
Salinan

Skrip melancarkan dua proses di latar belakang: dengan menggunakan tunggu Dibina tanpa hujah, kami menunggu semuanya, dan menjaga proses induk hidup. Apabila Sigint atau Sigterm isyarat diterima oleh skrip, kami menghantar a Sigterm kepada kedua -dua mereka, setelah PID mereka dikembalikan oleh Pekerjaan -P perintah (kerja Adakah sendiri shell terbina dalam, jadi apabila kita menggunakannya, proses baru tidak dibuat).

Sekiranya anak -anak mempunyai proses anak -anak mereka sendiri, dan kami ingin menamatkan mereka semua ketika nenek moyang menerima isyarat, kami dapat menghantar isyarat kepada seluruh kumpulan proses, seperti yang kita lihat sebelumnya.

Walau bagaimanapun, ini memberikan masalah, kerana dengan menghantar isyarat penamatan kepada kumpulan proses, kami akan memasukkan gelung "isyarat-sent/isyarat". Fikirkanlah: di perangkap untuk Sigterm Kami menghantar a Sigterm isyarat kepada semua ahli kumpulan proses; Ini termasuk skrip induk itu sendiri!

Untuk menyelesaikan masalah ini dan masih dapat melaksanakan fungsi pembersihan setelah proses anak ditamatkan, kita mesti mengubahnya perangkap untuk Sigterm Sebelum kita menghantar isyarat kepada kumpulan proses, sebagai contoh:

#!/bin/bash cleanup () echo "membersihkan ..." # kod pembersihan kami pergi ke sini perangkap 'perangkap "" sigterm; Bunuh 0; tunggu; pembersihan 'sigint sigterm echo "skrip pid adalah $" tidur 30 & tidur 40 & tunggu 
Salinan

Dalam perangkap, sebelum menghantar Sigterm ke kumpulan proses, kami menukar Sigterm perangkap, supaya proses induk mengabaikan isyarat dan hanya keturunannya yang dipengaruhi olehnya. Perhatikan juga bahawa dalam perangkap, untuk memberi isyarat kepada kumpulan proses, kami menggunakan bunuh dengan 0 sebagai PID. Ini adalah jalan pintas: ketika pid lulus ke bunuh adalah 0, semua proses di semasa Kumpulan proses memberi isyarat.

Kesimpulan

Dalam tutorial ini kita belajar mengenai kumpulan proses dan apakah perbezaan antara proses latar depan dan latar belakang. Kami mengetahui bahawa Ctrl-C menghantar a Sigint Isyarat kepada keseluruhan kumpulan proses latar depan terminal kawalan, dan kami belajar bagaimana menghantar isyarat kepada kumpulan proses menggunakan bunuh. Kami juga belajar bagaimana melaksanakan program di latar belakang, dan cara menggunakannya tunggu Shell dibina untuk menunggu ia keluar tanpa kehilangan cangkang induk. Akhirnya, kita melihat bagaimana untuk menyediakan skrip supaya apabila ia menerima isyarat ia menamatkan anak -anaknya sebelum keluar. Adakah saya terlepas sesuatu? Adakah anda mempunyai resipi peribadi anda untuk menyelesaikan tugas? Jangan teragak -agak untuk memberitahu saya!

Tutorial Linux Berkaitan:

  • Pengurusan proses latar belakang bash
  • Pengurusan skrip & proses bash berbilang threaded di…
  • Pengenalan kepada Automasi, Alat dan Teknik Linux
  • Cara menjalankan arahan di latar belakang di linux
  • Menguasai Gelung Skrip Bash
  • Cara memasang isyarat pada linux
  • Cara Mengesan Sistem Panggilan yang Dibuat oleh Proses Dengan Strace On ..
  • Mint 20: Lebih baik daripada Ubuntu dan Microsoft Windows?
  • Membandingkan Linux Apache Prefork vs Pekerja MPMS
  • Cara Bekerja dengan Kumpulan Pakej DNF