termA tidak membeku. Itu hanya menampilkan callback di kotak input. Cukup tekan Enter
kunci untuk melanjutkan masukan.
Sebelum menjelaskan masalah Anda, sedikit konteks tentang bagaimana read
perintah bekerja. Bunyinya dalam input data dari stdin
hingga EOF
dihadapi. Aman untuk mengatakan panggilan ke read
perintah tidak memblokir ketika harus membaca dari file di disk. Tetapi ketika stdin
terhubung ke terminal, perintah akan memblokir sampai pengguna mengetik sesuatu.
Bagaimana cara kerja penangan sinyal?
Penjelasan sederhana tentang cara kerja penanganan sinyal. Lihat cuplikan di bawah ini di C
yang hanya bekerja pada SIGINT
(alias CTRL+C
)
#include <stdio.h>
#include <signal.h>
/* signal handler definition */
void signal_handler(int signum){
printf("Hello World!\n");
}
int main(){
//Handle SIGINT with a signal handler
signal(SIGINT, signal_handler);
//loop forever!
while(1);
}
Ini akan mendaftarkan penangan sinyal dan kemudian akan memasuki loop tak terbatas. Saat kita menekan Ctrl-C
, kita semua setuju bahwa penangan sinyal signal_handler()
harus mengeksekusi dan "Hello World!"
mencetak ke layar, tetapi program berada dalam putaran tak terbatas. Untuk mencetak "Hello World!"
itu pasti kasus yang merusak loop untuk mengeksekusi penangan sinyal, bukan? Jadi itu harus keluar dari loop serta programnya. Mari kita lihat:
gcc -Wall -o sighdl.o signal.c
./sighdl.o
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
Seperti yang ditunjukkan oleh keluaran, setiap kali kami mengeluarkan Ctrl-C
, "Hello World!"
mencetak, tetapi program kembali ke loop tak terbatas. Hanya setelah mengeluarkan SIGQUIT
sinyal dengan Ctrl-\
apakah program benar-benar keluar. Bergantung pada ulimit
Anda pengaturan, itu akan membuang inti atau mencetak nomor sinyal yang diterima.
Sementara interpretasi bahwa loop akan keluar masuk akal, itu tidak mempertimbangkan alasan utama untuk penanganan sinyal, yaitu penanganan peristiwa asinkron. Itu berarti penangan sinyal bertindak di luar aliran standar kontrol program; pada kenyataannya, seluruh program disimpan dalam konteks, dan konteks baru dibuat hanya untuk penangan sinyal untuk dieksekusi. Setelah penangan sinyal menyelesaikan tindakannya, konteks dialihkan kembali dan aliran eksekusi normal dimulai (yaitu while(1)
).
Untuk menjawab pertanyaan Anda,
Kesimpulan memverifikasi bahwa Ketika bash menjalankan perintah eksternal di latar depan, bash tidak menangani sinyal apa pun yang diterima hingga proses latar depan berakhir
Hal utama yang perlu diperhatikan di sini adalah eksternal bagian perintah. Dalam kasus pertama, di mana sleep
adalah proses eksternal tetapi dalam kasus kedua, read
adalah built-in dari shell itu sendiri. Jadi propagasi sinyal ke keduanya berbeda dalam kedua kasus ini
type read
read is a shell builtin
type sleep
sleep is /usr/bin/sleep
1.Buka terminal, beri nama termA , dan jalankan file yang dibuat callback.sh dengan /bin/bash callback.sh untuk pertama kalinya, info akan muncul secara instan.
Ya, perilaku ini diharapkan. Karena saat ini hanya fungsi yang didefinisikan dan trap handler didaftarkan ke fungsi myCallback
dan sinyal belum diterima di skrip. Saat urutan eksekusi berjalan, pesan dari read
prompt dilemparkan untuk pertama kalinya.
2. Buka terminal baru, beri nama termB dan jalankan
pkill -USR1 -f callback.sh
pertama kali, info langsung muncul di termA
Ya, sedangkan read
perintah sedang menunggu string diikuti oleh Enter key pre yang menandakan EOF
, menerima sinyal SIGUSR1
dari terminal lain, konteks eksekusi saat ini disimpan dan kontrol dialihkan penangan sinyal yang mencetak string dengan tanggal saat ini.
Segera setelah handler selesai mengeksekusi, konteks dilanjutkan ke while
loop di mana read
perintah masih menunggu input string. Sampai read
perintah berhasil, semua perangkap sinyal berikutnya hanya akan mencetak string di dalam penangan sinyal.
Lanjutkan di termB, jalankan
pkill -USR1 -f callback.sh
kedua kalinya.
Sama seperti yang sudah dijelaskan sebelumnya, yaitu read
perintah tidak selesai sekali dalam while loop Anda, hanya jika berhasil membaca string, iterasi berikutnya dari loop akan dimulai dan pesan prompt baru akan dilontarkan.
Sumber gambar:Antarmuka Pemrograman Linux oleh Michael KerrisK
Kesimpulan memverifikasi bahwa Ketika bash menjalankan perintah eksternal di latar depan, bash tidak menangani sinyal apa pun yang diterima hingga proses latar depan berakhir.
bash
benar menangani sinyal di C
/ level implementasi, tetapi tidak akan menjalankan set penangan dengan trap
sampai proses latar depan telah dihentikan. Itu diwajibkan oleh standar:
When a signal for which a trap has been set is received while the shell is waiting for the completion of a utility executing a foreground command, the trap associated with that signal shall not be executed until after the foreground command has completed.
Perhatikan bahwa standar tidak membuat perbedaan antara perintah bawaan dan perintah eksternal; dalam kasus "utilitas" seperti read
, yang tidak dieksekusi dalam proses terpisah, tidak jelas apakah sinyal muncul dalam "konteks" atau di shell utama, dan apakah handler disetel dengan trap
harus dijalankan sebelum atau sesudah read
kembali.
masalah 1:callback.sh berisi while loop tak terbatas, bagaimana menjelaskannya tidak menangani sinyal apa pun yang diterima hingga proses latar depan berakhir., dalam hal ini proses latar depan tidak pernah berhenti.
Itu salah. bash
tidak menjalankan while
loop dalam proses terpisah. Karena tidak ada proses latar depan bash
sedang menunggu, ia dapat menjalankan kumpulan penangan apa saja dengan trap
langsung. Jika read
adalah perintah eksternal, itu dan bukan while
akan menjadi proses latar depan.
issue2:Tidak ada
please input something for foo: shown
dalam termA;buka termB, jalankan
pkill -USR1 -f callback.sh
ketiga kalinya.Info berikut ditampilkan di termA lagi:
callback function called at Mon Nov 19 09:07:24 HKT 2018
Masih belum ada
please input something for foo:
ditampilkan di termA.Mengapa infoplease input something for foo:
membeku?
Itu tidak membeku. Cukup tekan Enter dan itu akan muncul lagi.
Hanya saja
a) bash
akan memulai ulang read
bawaan di tempat ketika diinterupsi oleh sinyal -- ia tidak akan kembali dan melewati while
lagi lingkaran
b) itu tidak akan menampilkan ulang set prompt dengan -p
dalam hal itu.
Perhatikan bahwa bash
bahkan tidak akan menampilkan prompt lagi jika ada SIGINT
dari keyboard ditangani, bahkan jika itu membuang string yang dibaca sejauh ini dari pengguna:
$ cat goo
trap 'echo INT' INT
echo $$
read -p 'enter something: ' var
echo "you entered '$var'"
$ bash goo
24035
enter something: foo<Ctrl-C>^CINT
<Ctrl-C>^CINT
<Ctrl-C>^CINT
bar<Enter>
you entered 'bar'
Itu akan mencetak you entered 'foobar'
jika sinyal dikirim dari jendela lain dengan kill -INT <pid>
alih-alih dengan Ctrl-C dari terminal.
Semua hal di bagian terakhir ini (bagaimana read
terputus, dll) sangat bash
spesifik. Di shell lain seperti ksh
atau dash
, dan bahkan di bash
saat dijalankan dalam mode POSIX (bash --posix
), setiap sinyal yang ditangani sebenarnya akan mengganggu read
builtin. Pada contoh di atas, shell akan mencetak you entered ''
dan keluar setelah ^C pertama, dan jika read
dipanggil dari sebuah loop, loop akan dimulai ulang.