Linux C menangkap sinyal mematikan untuk penghentian yang baik

Saya memiliki proses menggunakan soket, koneksi database dan sejenisnya. Ini pada dasarnya adalah proses server yang menyampaikan antara data sensor dan antarmuka web, dan oleh karena itu penting untuk memastikan aplikasi, jika dimatikan, akan berakhir dengan baik.

Bagaimana cara saya menangani pengecualian yang tidak terduga seperti segfault (setidaknya untuk debugging) serta mematikan sinyal sehingga saya dapat menutup koneksi apa pun dan menghentikan semua thread yang berjalan sehingga proses tidak meninggalkan kekacauan pada apa pun yang digunakannya?


person user623879    schedule 11.09.2011    source sumber
comment
Ingatlah untuk terus berlari, bahkan untuk membersihkan, mungkin berbahaya setelah terjadi kesalahan segmentasi.   -  person icktoofay    schedule 11.09.2011
comment
Perlu diingat juga bahwa Anda tidak dapat menangkap sinyal mematikan.   -  person Gabe    schedule 11.09.2011
comment
Tidak yakin kekacauan apa yang ingin Anda hindari. Membunuh thread, menutup file, dan mengosongkan memori biasanya dilakukan oleh OS dengan sangat efisien, jadi dalam banyak kasus, keluar saja sudah cukup. Apakah ada hal spesifik yang Anda khawatirkan?   -  person Soren    schedule 11.09.2011
comment
@gabe - lebih baik mengatakan Anda tidak dapat menangkap SIGKILL agar tidak membingungkan orang yang belum pernah menggunakan apa pun selain perintah kill dari shell;)   -  person Brian Roach    schedule 11.09.2011
comment
@Soren - menangkap SIGTERM dan SIGINT adalah praktik yang sangat umum untuk memungkinkan Anda keluar dengan wajar.   -  person Brian Roach    schedule 11.09.2011
comment
@Soren: Saya tidak tahu masalah OP, tetapi memastikan bahwa file selalu dalam keadaan konsisten, transaksi database dibatalkan, dll. sering kali memerlukan pembersihan yang tidak dapat ditangani oleh OS hanya dengan keluar dari proses Anda.   -  person Gabe    schedule 11.09.2011
comment
kemungkinan duplikat dari SIGKILL signal Handler   -  person Ben Voigt    schedule 11.09.2011


Jawaban (3)


Anda menginstal penangan sinyal untuk menangkap sinyal -- namun dalam 99% kasus Anda hanya ingin keluar dan membiarkan OS Linux mengurus pembersihannya -- ia akan dengan senang hati menutup semua file, soket, memori bebas, dan thread shutdown.

Jadi kecuali ada sesuatu yang spesifik yang ingin Anda lakukan, seperti mengirim pesan pada soket, maka Anda sebaiknya keluar saja dari proses dan tidak mencoba menangkap sinyalnya.

person Soren    schedule 11.09.2011
comment
Saya rasa Anda ada di sini...keandalan aplikasi lebih penting dalam kasus saya dan melakukan hal-hal gila dengan sinyal bisa berakibat buruk. Ketika segfault terjadi, jika saya mendapatkan coredumps, apakah ini memberi tahu Anda di mana segfault terjadi? - person user623879; 11.09.2011
comment
Tumpukan panggilan memberi tahu Anda apakah kesalahan seg terjadi, dengan asumsi tumpukan panggilan tidak rusak -- pertanyaan ini di sini: stackoverflow.com/questions/105659/ berbicara tentang cara mendapatkan jejak tumpukan - person Soren; 11.09.2011
comment
Saran yang bagus. Pengecualian terhadap aturan ini adalah ketika Anda perlu membersihkan atau mengganti perangkat keras saat program dimatikan (dalam sistem yang tertanam). - person Havok; 07.08.2014
comment
Pertanyaan lama tapi saya masih bertanya-tanya apa yang Anda maksud dengan 99%? - person Moritz Schmidt; 16.05.2018
comment
Maksud saya adalah kecuali Anda seorang programmer berpengalaman yang tahu persis alasan mengapa aplikasi tertentu harus menangkap sinyal, maka Anda mungkin lebih baik tidak melakukannya sama sekali. - person Soren; 17.05.2018
comment
Ada banyak kesalahan memori saat menghentikan suatu proses ketika kita menjalankan program besar menggunakan alat profil atau alat pemeriksaan memori untuk menangkap memori yang tidak dibersihkan. dengan pendekatan ini bagaimana kita bisa memperbaiki kesalahan memori? - person Dig The Code; 21.01.2019
comment
@DigTheCode -- ini sepertinya pertanyaan baru. Alat pengecekan memori ada untuk menunjukkan kebocoran memori, dan jelas Anda belum mengosongkan memori yang Anda alokasikan, jadi itulah yang ditandai oleh alat tersebut. Sebagian besar laporan alat, memori yang bocor terpisah dari memori yang tidak hilang tetapi tidak dibebaskan -- jadi dalam banyak kasus Anda hanya perlu menafsirkan dan memahami laporan Anda dengan benar. - person Soren; 31.01.2019

Menangkap sinyal itu sulit. Kamu harus Berhati-hati. Langkah pertama Anda adalah menggunakan sigaction untuk memasang pengendali sinyal untuk sinyal yang diinginkan.

  • Pilih serangkaian sinyal untuk ditanggapi dan pilih apa artinya bagi proses Anda. Misalnya, SIGTERM berhenti, SIGHUP memulai ulang, SIGUSR1 memuat ulang konfigurasi, dll.

  • Jangan mencoba merespons semua sinyal dan jangan mencoba "membersihkan" sinyal yang menunjukkan kesalahan dalam program Anda. SIGKILL tidak dapat ditangkap. SIGSEGV, SIGBUS, dan lainnya yang serupa tidak boleh ditangkap kecuali Anda memiliki alasan yang SANGAT bagus. Jika Anda ingin melakukan debug, naikkan ulimit untuk core dumps — melampirkan debugger ke image inti jauh lebih efektif daripada apa pun yang Anda atau saya dapat kodekan. (Jika Anda mencoba membersihkan setelah SIGSEGV atau sesuatu seperti itu, sadari bahwa kode pembersihan mungkin menyebabkan SIGSEGV tambahan dan segalanya bisa menjadi buruk dengan cepat. Hindari saja seluruh kekacauan dan biarkan SIGSEGV menghentikan program Anda.)

  • Cara Anda menangani sinyal itu rumit. Jika aplikasi Anda memiliki loop utama (mis., select atau poll) maka penangan sinyal cukup menyetel flag atau menulis byte ke pipa khusus untuk memberi sinyal agar loop utama berhenti. Anda juga dapat menggunakan siglongjmp untuk keluar dari pengendali sinyal, tetapi ini SANGAT sulit untuk dilakukan dengan benar dan biasanya bukan yang Anda inginkan.

Sulit untuk merekomendasikan sesuatu tanpa mengetahui bagaimana aplikasi Anda terstruktur dan apa fungsinya.

Ingat juga bahwa pengendali sinyal itu sendiri hampir tidak melakukan apa pun. Banyak fungsi yang tidak aman untuk dipanggil dari pengendali sinyal.

person Dietrich Epp    schedule 11.09.2011

Saya terkadang ingin menelusuri kembali SIGSEGV, bagian menariknya seperti:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void sig_handler(int);

int main() {
    signal(SIGSEGV, sig_handler);
    int *p = NULL;
    return *p;
}

void sig_handler(int sig) {
    switch (sig) {
    case SIGSEGV:
        fprintf(stderr, "give out a backtrace or something...\n");
        abort();
    default:
        fprintf(stderr, "wasn't expecting that!\n");
        abort();
    }
}

Anda memang ingin sangat berhati-hati dalam menangani hal-hal ini, mis. pastikan Anda tidak dapat memicu sinyal lain.

person daniel    schedule 11.09.2011
comment
Bukankah lebih mudah menggunakan ulimit untuk mendapatkan core dumps dan mendapatkan jejak tumpukan dengan cara itu? - person Dietrich Epp; 11.09.2011
comment
tidak familiar dengan pendekatan itu, bisakah Anda menjelaskannya? Saya hanya menggunakan barang ini untuk debugging, yaitu saya membuat kesalahan dan ingin langsung melihat dari mana sinyal itu berasal. - person daniel; 11.09.2011
comment
Anda sebaiknya tidak menggunakan signal. Selama bertahun-tahun disarankan untuk tidak melakukannya dan menggunakan sigaction sebagai gantinya. Dari man signal: Perilaku signal() bervariasi antar versi Unix, dan juga bervariasi secara historis di berbagai versi Linux. Hindari penggunaannya: gunakan sigaction(2) sebagai gantinya.. Saya tidak meremehkan ... tapi hampir saja. Anda tidak boleh merekomendasikan siapa pun untuk menggunakannya. - person Brian Roach; 11.09.2011
comment
Perintah ulimit memungkinkan Anda membuat program membuang inti ke SIGSEGV. File inti adalah salinan gambar memori program Anda, dan Anda dapat melampirkan debugger ke dalamnya dan melihat-lihat memori (mendapatkan jejak tumpukan, memeriksa variabel), atau mengirim file inti ke pengembang jika itu bukan program Anda. - person Dietrich Epp; 11.09.2011