Tidak ada hal seperti menangkap sinyal di C .... atau setidaknya jadi saya pikir sampai saya membaca standar C99. Ternyata ada penanganan sinyal yang didefinisikan dalam C tetapi Ctrl-C tidak diberi mandat untuk menghasilkan sinyal tertentu atau sinyal sama sekali. Tergantung pada platform Anda, ini mungkin tidak mungkin.
JeremyP
1
Penanganan sinyal sebagian besar tergantung pada implementasi. Pada platform * nix, gunakan <signal.h>, dan jika Anda menggunakan OSX, Anda dapat memanfaatkan GCD untuk membuat segalanya lebih mudah ~.
Dylan Lukes
Jawaban:
205
Dengan pengendali sinyal.
Berikut adalah contoh sederhana membalik sebuah booldigunakan di main():
Sunting pada Juni 2017 : Kepada siapa pun yang berkepentingan, khususnya mereka yang memiliki keinginan yang tak pernah puas untuk mengedit jawaban ini. Lihat, saya menulis jawaban ini tujuh tahun yang lalu. Ya, standar bahasa berubah. Jika Anda benar-benar harus memperbaiki dunia, silakan tambahkan jawaban baru Anda tetapi tinggalkan jawaban saya apa adanya. Karena jawabannya ada nama saya di atasnya, saya lebih suka mengandung kata-kata saya juga. Terima kasih.
Katakan bahwa kita perlu menyertakan # signal.h> agar ini berfungsi!
kristianlm
13
statis Seharusnya bool volatile keepRunning = true;100% aman. Kompiler bebas untuk melakukan cache keepRunningdalam register dan volatile akan mencegahnya. Dalam praktiknya, kemungkinan besar itu juga berfungsi tanpa kata kunci yang mudah menguap ketika loop sementara memanggil setidaknya satu fungsi non-inline.
Johannes Overmann
2
@DirkEddelbuettel Saya berdiri dikoreksi, saya pikir perbaikan saya akan lebih mencerminkan niat asli Anda, maaf jika itu tidak terjadi. Pokoknya, masalah yang lebih besar adalah bahwa, karena jawaban Anda mencoba menjadi cukup umum, dan karena IMO itu harus memberikan potongan yang juga berfungsi di bawah interupsi asinkron: Saya akan menggunakan sig_atomic_tatau atomic_boolmengetik di sana. Saya hanya merindukan yang itu. Sekarang, karena kita sedang berbicara: apakah Anda ingin saya mengembalikan suntingan terakhir saya? Tidak ada perasaan keras di sana, itu akan dapat dimengerti dari sudut pandang Anda :)
Peter Varo
2
Itu jauh lebih baik!
Dirk Eddelbuettel
2
@JohannesOvermann Bukannya saya ingin melakukan nitpick tetapi secara tegas, tidak masalah jika kode memanggil fungsi non-inline atau tidak, karena hanya ketika melintasi penghalang memori kompiler tidak diperbolehkan untuk mengandalkan nilai yang di-cache. Mengunci / Membuka kunci mutex akan menjadi penghalang memori. Karena variabel statis dan dengan demikian tidak terlihat di luar file saat ini, kompiler aman untuk menganggap bahwa suatu fungsi tidak pernah dapat mengubah nilainya, kecuali jika Anda meneruskan referensi ke variabel itu ke fungsi. Jadi volatile sangat disarankan di sini.
Catatan: Jelas, ini adalah contoh sederhana menjelaskan hanya bagaimana mendirikan sebuah CtrlChandler, tapi seperti biasa ada aturan yang perlu dipatuhi agar tidak merusak sesuatu yang lain. Silakan baca komentar di bawah ini.
Kode sampel dari atas:
#include<stdio.h>#include<signal.h>#include<stdlib.h>voidINThandler(int);int main(void){
signal(SIGINT,INThandler);while(1)
pause();return0;}voidINThandler(int sig){char c;
signal(sig, SIG_IGN);
printf("OUCH, did you hit Ctrl-C?\n""Do you really want to quit? [y/n] ");
c = getchar();if(c =='y'|| c =='Y')
exit(0);else
signal(SIGINT,INThandler);
getchar();// Get new line character}
@ Derrick Agree, int mainadalah hal yang tepat, tetapi gccdan kompiler lain mengkompilasi ini ke program yang berjalan dengan benar sejak tahun 1990-an. Dijelaskan dengan cukup baik di sini: eskimo.com/~scs/readings/voidmain.960823.html - ini pada dasarnya adalah "fitur", saya menganggapnya seperti itu.
icyrock.com
1
@ icyrock.com: Semua sangat benar (tentang void main () di C), tetapi ketika memposting secara publik mungkin juga untuk menghindari perdebatan sama sekali dengan menggunakan int main () agar tidak mengalihkan perhatian dari titik utama.
Clifford
21
Ada kelemahan besar dalam hal ini. Anda tidak dapat menggunakan printf dengan aman dalam konten penangan sinyal. Ini merupakan pelanggaran keamanan sinyal asinkron. Ini karena printf tidak reentrant. Apa yang terjadi jika program berada di tengah-tengah penggunaan printf ketika Ctrl-C ditekan, dan pengendali sinyal Anda mulai menggunakannya pada saat yang sama? Petunjuk: Ini kemungkinan akan pecah. tulis dan fwrite sama untuk digunakan dalam konteks ini.
Dylan Lukes
2
@ icyrock.com: Melakukan sesuatu yang rumit dalam penangan sinyal akan menyebabkan sakit kepala. Terutama menggunakan sistem io.
Martin York
2
@ stacker Terima kasih - Saya pikir itu sangat berharga di akhir. Jika seseorang menemukan kode ini di masa depan, lebih baik untuk memperbaikinya sebanyak mungkin, terlepas dari topik pertanyaannya.
icyrock.com
30
Tambahan tentang platform UN * X.
Menurut signal(2)halaman manual di GNU / Linux, perilaku signaltidak portabel seperti perilaku sigaction:
Perilaku sinyal () bervariasi antar versi UNIX, dan secara historis juga bervariasi di berbagai versi Linux. Hindari penggunaannya: gunakan sigaction (2) sebagai gantinya.
Pada Sistem V, sistem tidak menghalangi pengiriman instance sinyal lebih lanjut dan pengiriman sinyal akan mengatur ulang handler ke default. Di BSD semantik berubah.
Variasi berikut dari jawaban sebelumnya oleh Dirk Eddelbuettel menggunakan sigactionalih-alih signal:
Sekarang mungkin untuk membaca Ctrl+ Cmenggunakan penekanan tombol fgetc(stdin). Hati-hati menggunakan ini karena Anda tidak dapat Ctrl+ Z, Ctrl+ Q, Ctrl+ S, dll seperti biasanya lagi baik.
@Peter Varo memperbarui jawaban Dirk, tetapi Dirk menolak perubahan itu. Inilah jawaban baru dari Peter:
Meskipun cuplikan di atas adalah benar c89Sebagai contoh, seseorang harus menggunakan tipe dan jaminan yang lebih modern yang disediakan oleh standar nanti jika memungkinkan. Oleh karena itu, berikut adalah alternatif yang lebih aman dan modern bagi mereka yang mencaric99 dan c11 implementasi sesuai:
#include<signal.h>#include<stdlib.h>#include<stdio.h>staticvolatilesig_atomic_t keep_running =1;staticvoid sig_handler(int _){(void)_;
keep_running =0;}int main(void){
signal(SIGINT, sig_handler);while(keep_running)
puts("Still running...");
puts("Stopped by signal `SIGINT'");return EXIT_SUCCESS;}
C11 Standar: 7.14§2 Header <signal.h>menyatakan jenis ... sig_atomic_tyang merupakan jenis integer (mungkin yang memenuhi syarat) dari objek yang dapat diakses sebagai entitas atom, bahkan di hadapan interupsi asinkron.
Selanjutnya:
Standar C11: 7.14.1.1§5 Jika sinyal muncul selain dari hasil pemanggilan fungsi abortatau raise, perilaku tidak terdefinisi jika penangan sinyal mengacu pada objek apa pun dengan staticatau durasi penyimpanan ulir yang bukan objek atom bebas kunci lainnya selain dengan menetapkan nilai ke objek yang dinyatakan sebagai volatile sig_atomic_t...
Saya baru saja menemukan jawaban itu dan akan menempelkan tautan itu di sini! Terima kasih :)
Luis Paulo
5
Mengenai jawaban yang ada, perhatikan bahwa penanganan sinyal tergantung pada platform. Win32 misalnya menangani sinyal yang jauh lebih sedikit daripada sistem operasi POSIX; lihat di sini . Sementara SIGINT dideklarasikan dalam signal.h pada Win32, lihat catatan di dokumentasi yang menjelaskan bahwa itu tidak akan melakukan apa yang Anda harapkan.
#include<stdio.h>#include<signal.h>#include<unistd.h>void sig_handler(int signo){if(signo == SIGINT)
printf("received SIGINT\n");}int main(void){if(signal(SIGINT, sig_handler)== SIG_ERR)
printf("\ncan't catch SIGINT\n");// A long long wait so that we can easily issue a signal to this processwhile(1)
sleep(1);return0;}
Fungsi sig_handler memeriksa apakah nilai argumen yang diteruskan sama dengan SIGINT, maka printf dieksekusi.
Jawaban:
Dengan pengendali sinyal.
Berikut adalah contoh sederhana membalik sebuah
bool
digunakan dimain()
:Sunting pada Juni 2017 : Kepada siapa pun yang berkepentingan, khususnya mereka yang memiliki keinginan yang tak pernah puas untuk mengedit jawaban ini. Lihat, saya menulis jawaban ini tujuh tahun yang lalu. Ya, standar bahasa berubah. Jika Anda benar-benar harus memperbaiki dunia, silakan tambahkan jawaban baru Anda tetapi tinggalkan jawaban saya apa adanya. Karena jawabannya ada nama saya di atasnya, saya lebih suka mengandung kata-kata saya juga. Terima kasih.
sumber
bool volatile keepRunning = true;
100% aman. Kompiler bebas untuk melakukan cachekeepRunning
dalam register dan volatile akan mencegahnya. Dalam praktiknya, kemungkinan besar itu juga berfungsi tanpa kata kunci yang mudah menguap ketika loop sementara memanggil setidaknya satu fungsi non-inline.sig_atomic_t
atauatomic_bool
mengetik di sana. Saya hanya merindukan yang itu. Sekarang, karena kita sedang berbicara: apakah Anda ingin saya mengembalikan suntingan terakhir saya? Tidak ada perasaan keras di sana, itu akan dapat dimengerti dari sudut pandang Anda :)Periksa di sini:
Catatan: Jelas, ini adalah contoh sederhana menjelaskan hanya bagaimana mendirikan sebuah CtrlChandler, tapi seperti biasa ada aturan yang perlu dipatuhi agar tidak merusak sesuatu yang lain. Silakan baca komentar di bawah ini.
Kode sampel dari atas:
sumber
int main
adalah hal yang tepat, tetapigcc
dan kompiler lain mengkompilasi ini ke program yang berjalan dengan benar sejak tahun 1990-an. Dijelaskan dengan cukup baik di sini: eskimo.com/~scs/readings/voidmain.960823.html - ini pada dasarnya adalah "fitur", saya menganggapnya seperti itu.Tambahan tentang platform UN * X.
Menurut
signal(2)
halaman manual di GNU / Linux, perilakusignal
tidak portabel seperti perilakusigaction
:Pada Sistem V, sistem tidak menghalangi pengiriman instance sinyal lebih lanjut dan pengiriman sinyal akan mengatur ulang handler ke default. Di BSD semantik berubah.
Variasi berikut dari jawaban sebelumnya oleh Dirk Eddelbuettel menggunakan
sigaction
alih-alihsignal
:sumber
volatile sig_atomic_t
didefinisikan dengan baikAtau Anda dapat meletakkan terminal dalam mode mentah, seperti ini:
Sekarang mungkin untuk membaca Ctrl+ Cmenggunakan penekanan tombol
fgetc(stdin)
. Hati-hati menggunakan ini karena Anda tidak dapat Ctrl+ Z, Ctrl+ Q, Ctrl+ S, dll seperti biasanya lagi baik.sumber
Siapkan jebakan (Anda dapat menjebak beberapa sinyal dengan satu pengendali):
Tangani sinyalnya sesuai keinginan Anda, tetapi waspadai keterbatasan dan gotcha:
sumber
@Peter Varo memperbarui jawaban Dirk, tetapi Dirk menolak perubahan itu. Inilah jawaban baru dari Peter:
Meskipun cuplikan di atas adalah benar c89Sebagai contoh, seseorang harus menggunakan tipe dan jaminan yang lebih modern yang disediakan oleh standar nanti jika memungkinkan. Oleh karena itu, berikut adalah alternatif yang lebih aman dan modern bagi mereka yang mencaric99 dan c11 implementasi sesuai:
Selanjutnya:
sumber
(void)_;
... Apa tujuannya? Apakah begitu kompiler tidak memperingatkan tentang variabel yang tidak digunakan?Mengenai jawaban yang ada, perhatikan bahwa penanganan sinyal tergantung pada platform. Win32 misalnya menangani sinyal yang jauh lebih sedikit daripada sistem operasi POSIX; lihat di sini . Sementara SIGINT dideklarasikan dalam signal.h pada Win32, lihat catatan di dokumentasi yang menjelaskan bahwa itu tidak akan melakukan apa yang Anda harapkan.
sumber
Fungsi sig_handler memeriksa apakah nilai argumen yang diteruskan sama dengan SIGINT, maka printf dieksekusi.
sumber
Ini hanya mencetak sebelum keluar.
sumber