Apa perbedaan antara sigaction dan sinyal?

143

Saya akan menambahkan penangan sinyal tambahan ke aplikasi yang kami miliki di sini dan saya perhatikan bahwa penulis telah sigaction()mengatur penangan sinyal lainnya. Saya akan menggunakan signal(). Untuk mengikuti konvensi saya harus menggunakan sigaction()tetapi jika saya menulis dari awal, mana yang harus saya pilih?

Matthew Smith
sumber

Jawaban:

167

Gunakan sigaction()kecuali Anda punya alasan yang sangat kuat untuk tidak melakukannya.

The signal()antarmuka memiliki kuno (dan karenanya ketersediaan) dalam mendukung, dan didefinisikan dalam standar C. Namun demikian, ia memiliki sejumlah karakteristik yang tidak diinginkan yang sigaction()menghindari - kecuali jika Anda menggunakan bendera yang ditambahkan secara eksplisit sigaction()untuk memungkinkannya mensimulasikan signal()perilaku lama dengan setia .

  1. The signal()Fungsi tidak (selalu) memblokir sinyal lainnya dari tiba sementara penangan saat mengeksekusi; sigaction()dapat memblokir sinyal lain sampai handler saat ini kembali.
  2. The signal()fungsi (biasanya) me-reset kembali aksi sinyal untuk SIG_DFL(default) untuk hampir semua sinyal. Ini berarti signal()pawang harus menginstal ulang dirinya sendiri sebagai tindakan pertama. Ini juga membuka jendela kerentanan antara waktu ketika sinyal terdeteksi dan pawang diinstal ulang di mana jika instance kedua sinyal tiba, perilaku default (biasanya berakhir, kadang-kadang dengan prasangka - alias dump inti) terjadi.
  3. Perilaku tepat signal()bervariasi di antara sistem - dan standar mengizinkan variasi tersebut.

Ini adalah alasan umumnya baik untuk menggunakan sigaction()bukan signal(). Namun, antarmuka sigaction()tidak bisa disangkal lebih fiddly.

Mana saja dari kedua Anda gunakan, jangan tergoda oleh antarmuka sinyal alternatif seperti sighold(), sigignore(), sigpause()dan sigrelse(). Mereka secara nominal alternatif sigaction(), tetapi mereka hanya nyaris tidak terstandarisasi dan hadir dalam POSIX untuk kompatibilitas mundur daripada untuk penggunaan serius. Perhatikan bahwa standar POSIX mengatakan perilaku mereka dalam program multi-utas tidak terdefinisi.

Program dan sinyal multi-utas adalah kisah rumit lainnya. AFAIK, keduanya signal()dan sigaction()OK di aplikasi multi-utas.

Cornstalks mengamati :

Halaman manual Linux untuk signal()mengatakan:

  Efek signal()dalam proses multi-utas tidak ditentukan.

Jadi, saya pikir sigaction()adalah satu-satunya yang dapat digunakan dengan aman dalam proses multi-threaded.

Itu menarik. Halaman manual Linux lebih membatasi dari POSIX dalam hal ini. POSIX menentukan untuk signal():

Jika prosesnya multi-threaded, atau jika prosesnya single-threaded dan penangan sinyal dieksekusi selain sebagai hasil dari:

  • Proses pemanggilan abort(), raise(), kill(), pthread_kill(), atau sigqueue()untuk menghasilkan sinyal yang tidak diblokir
  • Sinyal yang tertunda diblokir dan dikirim sebelum panggilan yang tidak diblokir kembali

perilaku tidak terdefinisi jika penangan sinyal merujuk ke objek apa pun selain errnodengan durasi penyimpanan statis selain dengan menetapkan nilai ke objek yang dinyatakan volatile sig_atomic_t, atau jika penangan sinyal memanggil fungsi yang didefinisikan dalam standar ini selain dari salah satu fungsi yang tercantum dalam Konsep Sinyal .

Jadi POSIX dengan jelas menentukan perilaku signal()dalam aplikasi multi-utas.

Namun demikian, sigaction()pada dasarnya akan lebih disukai dalam semua keadaan - dan kode multi-ulir portabel harus digunakan sigaction()kecuali ada alasan kuat mengapa tidak dapat (seperti "hanya menggunakan fungsi yang didefinisikan oleh Standar C" - dan ya, kode C11 dapat multi -baca). Yang pada dasarnya adalah apa yang paragraf pembuka dari jawaban ini juga mengatakan.

Jonathan Leffler
sumber
12
Deskripsi tentang signalini sebenarnya dari perilaku Unix System V. POSIX memungkinkan perilaku ini atau perilaku BSD yang lebih waras, tetapi karena Anda tidak bisa memastikan mana yang akan Anda dapatkan, masih lebih baik digunakan sigaction.
R .. GitHub BERHENTI MEMBANTU ICE
1
kecuali jika Anda menggunakan flag yang secara eksplisit ditambahkan ke sigaction () untuk memungkinkannya mensimulasikan perilaku sinyal lama () dengan setia. Bendera apakah itu (secara spesifik)?
ChristianCuevas
@AlexFritz: Terutama SA_RESETHAND, juga SA_NODEFER.
Jonathan Leffler
2
@BulatM. Jika Anda tidak dapat menggunakan sigaction(), maka pada dasarnya Anda wajib menggunakan spesifikasi Standar C untuk signal(). Namun, itu memberi Anda satu set opsi yang sangat miskin untuk apa yang dapat Anda lakukan. Anda dapat: memodifikasi (cakupan file) variabel tipe volatile sig_atomic_t; panggil salah satu fungsi 'keluar cepat' ( _Exit(), quick_exit()) atau abort(); panggilan signal()dengan nomor sinyal saat ini sebagai argumen sinyal; kembali. Dan itu saja. Hal lain tidak dijamin portabel. Itu sangat ketat sehingga kebanyakan orang mengabaikan aturan itu - tetapi kode yang dihasilkannya cerdik.
Jonathan Leffler
1
Sangat baik sigaction()demo dari GCC sendiri: gnu.org/software/libc/manual/html_node/... ; dan signal()demo yang sangat baik dari GCC sendiri: gnu.org/software/libc/manual/html_node/… . Perhatikan bahwa dalam signaldemo mereka menghindari mengubah handler dari Abaikan ( SIG_IGN) jika itu memang sengaja diatur sebelumnya.
Gabriel Staples
8

Bagi saya, baris di bawah ini sudah cukup untuk memutuskan:

Fungsi sigaction () menyediakan mekanisme yang lebih komprehensif dan andal untuk mengendalikan sinyal; aplikasi baru harus menggunakan sigaction () daripada signal ()

http://pubs.opengroup.org/onlinepubs/009695399/functions/signal.html#tag_03_690_07

Baik Anda memulai dari awal atau memodifikasi program lama, sigaction harus menjadi pilihan yang tepat.

San
sumber
5

Mereka adalah antarmuka yang berbeda untuk fasilitas sinyal OS. Seseorang harus lebih suka menggunakan sigaction untuk memberi sinyal jika memungkinkan karena sinyal () memiliki perilaku yang ditentukan-implementasinya (seringkali cenderung rentan) dan berperilaku berbeda pada Windows, OS X, Linux dan sistem UNIX lainnya.

Lihat catatan keamanan ini untuk detailnya.

ididak
sumber
1
Saya hanya melihat kode sumber glibc dan sinyal () hanya memanggil sigaction (). Juga lihat di atas di mana halaman manual MacOS mengklaim hal yang sama.
bmdhacks
Itu baik untuk diketahui. Saya hanya pernah melihat penangan sinyal yang digunakan untuk menutup hal-hal dengan rapi sebelum keluar jadi saya biasanya tidak akan bergantung pada perilaku yang harus dilakukan dengan menginstal ulang penangan.
Matthew Smith
5

sinyal () adalah standar C, sigaction () tidak.

Jika Anda dapat menggunakan keduanya (yaitu, Anda menggunakan sistem POSIX), maka gunakan sigaction (); tidak ditentukan apakah sinyal () me-reset handler, artinya agar portabel Anda harus memanggil sinyal () lagi di dalam handler. Yang lebih buruk adalah ada perlombaan: jika Anda mendapatkan dua sinyal secara berurutan, dan yang kedua dikirim sebelum Anda menginstal ulang pawang, Anda akan memiliki tindakan default, yang mungkin akan mematikan proses Anda. sigaction () , di sisi lain, dijamin menggunakan semantik sinyal "andal". Anda tidak perlu menginstal ulang handler, karena itu tidak akan pernah diatur ulang. Dengan SA_RESTART, Anda juga bisa mendapatkan beberapa panggilan sistem untuk memulai ulang secara otomatis (jadi Anda tidak perlu memeriksa EINTR secara manual). sigaction () memiliki lebih banyak opsi dan dapat diandalkan, sehingga penggunaannya dianjurkan.

Psst ... jangan beri tahu siapa pun yang saya katakan ini, tetapi POSIX saat ini memiliki fungsi bsd_signal () yang bertindak seperti sinyal () tetapi memberikan semantik BSD, yang berarti dapat diandalkan. Penggunaan utamanya adalah untuk porting aplikasi lama yang dianggap sinyal yang dapat diandalkan, dan POSIX tidak merekomendasikan menggunakannya.

Huzzefakhan
sumber
POSIX tidak memiliki fungsi bsd_signal()- beberapa implementasi POSIX mungkin memiliki fungsi, tetapi POSIX sendiri tidak mengandung fungsi seperti itu (lihat POSIX ).
Jonathan Leffler
4

Pendeknya:

sigaction()baik dan terdefinisi dengan baik, tetapi merupakan fungsi Linux dan hanya berfungsi di Linux. signal()buruk dan kurang didefinisikan, tetapi merupakan fungsi standar C dan berfungsi pada apa pun.

Apa yang dikatakan halaman manual Linux tentang hal itu?

man 2 signal(lihat online di sini ) menyatakan:

Perilaku sinyal () bervariasi antar versi UNIX, dan secara historis juga bervariasi di berbagai versi Linux. Hindari penggunaannya: gunakan sigaction(2)saja. Lihat Portabilitas di bawah ini.

Portabilitas Satu-satunya penggunaan sinyal portabel () adalah untuk mengatur disposisi sinyal ke SIG_DFL atau SIG_IGN. Semantik saat menggunakan sinyal () untuk membentuk pengendali sinyal bervariasi di seluruh sistem (dan POSIX.1 secara eksplisit memungkinkan variasi ini); jangan menggunakannya untuk tujuan ini.

Dengan kata lain: jangan gunakan signal(). Gunakan sigaction()sebagai gantinya!

Apa yang dipikirkan GCC?

Catatan Kompatibilitas: Seperti disebutkan di atas untuk signal, fungsi ini harus dihindari bila memungkinkan. sigactionadalah metode yang disukai.

Sumber: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling

Jadi, jika Linux dan GCC mengatakan untuk tidak menggunakan signal(), tetapi untuk menggunakan sigaction(), itu menimbulkan pertanyaan: bagaimana kita menggunakan hal yang membingungkan ini sigaction()!?

Contoh Penggunaan:

Baca signal()contoh EXCELLENT GCC di sini: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling

Dan sigaction()contoh EXCELLENT mereka di sini: https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html

Setelah membaca halaman-halaman itu, saya menemukan teknik berikut untuk sigaction():

1. sigaction(), karena ini adalah cara yang tepat untuk memasang penangan sinyal, seperti dijelaskan di atas:

#include <errno.h>  // errno
#include <signal.h> // sigaction()
#include <stdio.h>  // printf()
#include <string.h> // strerror()

#define LOG_LOCATION __FILE__, __LINE__, __func__ // Format: const char *, unsigned int, const char *
#define LOG_FORMAT_STR "file: %s, line: %u, func: %s: "

/// @brief      Callback function to handle termination signals, such as Ctrl + C
/// @param[in]  signal  Signal number of the signal being handled by this callback function
/// @return     None
static void termination_handler(const int signal)
{
    switch (signal)
    {
    case SIGINT:
        printf("\nSIGINT (%i) (Ctrl + C) signal caught.\n", signal);
        break;
    case SIGTERM:
        printf("\nSIGTERM (%i) (default `kill` or `killall`) signal caught.\n", signal);
        break;
    case SIGHUP:
        printf("\nSIGHUP (%i) (\"hang-up\") signal caught.\n", signal);
        break;
    default:
        printf("\nUnk signal (%i) caught.\n", signal);
        break;
    }

    // DO PROGRAM CLEANUP HERE, such as freeing memory, closing files, etc.


    exit(signal);
}

/// @brief      Set a new signal handler action for a given signal
/// @details    Only update the signals with our custom handler if they are NOT set to "signal ignore" (`SIG_IGN`),
///             which means they are currently intentionally ignored. GCC recommends this "because non-job-control
///             shells often ignore certain signals when starting children, and it is important for children
///             to respect this." See
///             https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
///             and https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html.
///             Note that termination signals can be found here:
///             https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html#Termination-Signals
/// @param[in]  signal  Signal to set to this action
/// @param[in]  action  Pointer to sigaction struct, including the callback function inside it, to attach to this signal
/// @return     None
static inline void set_sigaction(int signal, const struct sigaction *action)
{
    struct sigaction old_action;

    // check current signal handler action to see if it's set to SIGNAL IGNORE
    sigaction(signal, NULL, &old_action);
    if (old_action.sa_handler != SIG_IGN)
    {
        // set new signal handler action to what we want
        int ret_code = sigaction(signal, action, NULL);
        if (ret_code == -1)
        {
            printf(LOG_FORMAT_STR "sigaction failed when setting signal to %i;\n"
                   "  errno = %i: %s\n", LOG_LOCATION, signal, errno, strerror(errno));
        }
    }
}

int main(int argc, char *argv[])
{
    //...

    // Register callbacks to handle kill signals; prefer the Linux function `sigaction()` over the C function
    // `signal()`: "It is better to use sigaction if it is available since the results are much more reliable."
    // Source: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
    // and /programming/231912/what-is-the-difference-between-sigaction-and-signal/232711#232711.
    // See here for official gcc `sigaction()` demo, which this code is modeled after:
    // https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html

    // Set up the structure to specify the new action, per GCC's demo.
    struct sigaction new_action;
    new_action.sa_handler = termination_handler; // set callback function
    sigemptyset(&new_action.sa_mask);
    new_action.sa_flags = 0;

    // SIGINT: ie: Ctrl + C kill signal
    set_sigaction(SIGINT, &new_action);
    // SIGTERM: termination signal--the default generated by `kill` and `killall`
    set_sigaction(SIGTERM, &new_action);
    // SIGHUP: "hang-up" signal due to lost connection
    set_sigaction(SIGHUP, &new_action);

    //...
}

2. Dan untuk signal(), meskipun itu bukan cara yang baik untuk memasang penangan sinyal, seperti dijelaskan di atas, masih baik untuk mengetahui cara menggunakannya.

Inilah kode demonstrasi GCC yang disalin, karena hampir sebagus yang akan didapat:

#include <signal.h>

void
termination_handler (int signum)
{
  struct temp_file *p;

  for (p = temp_file_list; p; p = p->next)
    unlink (p->name);
}

int
main (void)
{
  
  if (signal (SIGINT, termination_handler) == SIG_IGN)
    signal (SIGINT, SIG_IGN);
  if (signal (SIGHUP, termination_handler) == SIG_IGN)
    signal (SIGHUP, SIG_IGN);
  if (signal (SIGTERM, termination_handler) == SIG_IGN)
    signal (SIGTERM, SIG_IGN);
  
}

Tautan utama yang harus diperhatikan:

  1. Sinyal Standar: https://www.gnu.org/software/libc/manual/html_node/Standard-Signals.html#Standard-Signals
    1. Sinyal Pengakhiran: https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html#Termination-Signals
  2. Penanganan Sinyal Dasar, termasuk signal()contoh penggunaan GCC resmi : https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
  3. sigaction()Contoh penggunaan GCC resmi : https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html
  4. Kumpulan sinyal, termasuk sigemptyset()dan sigfillset(); Saya masih tidak mengerti persis ini, tetapi tahu itu penting: https://www.gnu.org/software/libc/manual/html_node/Signal-Sets.html

Lihat juga:

  1. TutorialsPoint C ++ Penanganan Sinyal [dengan kode demo luar biasa]: https://www.tutorialspoint.com/cplusplus/cpp_signal_handling.htm
  2. https://www.tutorialspoint.com/c_standard_library/signal_h.htm
Gabriel Staples
sumber
2

Dari signal(3)halaman manual:

DESKRIPSI

 This signal() facility is a simplified interface to the more
 general sigaction(2) facility.

Keduanya memohon fasilitas dasar yang sama. Anda mungkin seharusnya tidak memanipulasi respons sinyal tunggal dengan keduanya, tetapi mencampurkannya seharusnya tidak menyebabkan apa pun rusak ...

dmckee --- mantan kucing moderator
sumber
Itu tidak ada di halaman manual saya! Yang saya dapatkan hanyalah "DESKRIPSI Panggilan sistem sinyal () menginstal pengendali sinyal baru untuk sinyal dengan signum angka." Saya perlu memutakhirkan ke paket halaman manual yang berguna .
Matthew Smith
1
Itu keluar dari halaman Mac OS X 10.5.
dmckee --- ex-moderator kitten
Juga diverifikasi dari kode sumber glibc. sinyal () hanya memanggil sigaction ()
bmdhacks
2
Namun ini tidak benar pada semua implementasi sinyal. Jika Anda ingin mandat perilaku "sigaction", jangan mengandalkan asumsi ini.
Ben Burns
1

Saya juga menyarankan menggunakan sigaction () di atas sinyal () dan ingin menambahkan satu titik lagi. sigaction () memberi Anda lebih banyak opsi seperti pid dari proses yang mati (mungkin menggunakan struct siginfo_t).

pradyumna kaushik
sumber
0

Saya akan menggunakan sinyal () karena lebih portabel, secara teori setidaknya. Saya akan memilih komentator mana pun yang dapat membuat sistem modern yang tidak memiliki lapisan kompatibilitas POSIX dan mendukung sinyal ().

Mengutip dari dokumentasi GLIBC :

Dimungkinkan untuk menggunakan kedua fungsi sinyal dan sigaction dalam satu program, tetapi Anda harus berhati-hati karena mereka dapat berinteraksi dengan cara yang agak aneh.

Fungsi sigaction menentukan lebih banyak informasi daripada fungsi sinyal, sehingga nilai balik dari sinyal tidak dapat mengungkapkan kisaran penuh kemungkinan sigaction. Karena itu, jika Anda menggunakan sinyal untuk menyimpan dan kemudian membangun kembali suatu tindakan, itu mungkin tidak dapat membangun kembali dengan benar pawang yang didirikan dengan sigaction.

Untuk menghindari masalah sebagai akibatnya, selalu gunakan sigaction untuk menyimpan dan mengembalikan handler jika program Anda menggunakan sigaction sama sekali. Karena sigaction lebih umum, sigaction dapat menyimpan dan membangun kembali tindakan dengan benar, terlepas dari apakah sigaction dibuat dengan sinyal atau sigaction.

Pada beberapa sistem jika Anda menetapkan suatu tindakan dengan sinyal dan kemudian memeriksanya dengan sigaction, alamat pawang yang Anda dapatkan mungkin tidak sama dengan apa yang Anda tentukan dengan sinyal. Bahkan mungkin tidak cocok untuk digunakan sebagai argumen aksi dengan sinyal. Tapi Anda bisa mengandalkan menggunakannya sebagai argumen untuk sigaction. Masalah ini tidak pernah terjadi pada sistem GNU.

Jadi, Anda lebih baik menggunakan satu atau yang lain dari mekanisme secara konsisten dalam satu program.

Catatan Portabilitas: Fungsi sinyal dasar adalah fitur ISO C, sedangkan sigaction adalah bagian dari standar POSIX.1. Jika Anda khawatir tentang portabilitas ke sistem non-POSIX, maka Anda harus menggunakan fungsi sinyal sebagai gantinya.

Hak Cipta (C) 1996-2008 Free Software Foundation, Inc.

Izin diberikan untuk menyalin, mendistribusikan, dan / atau memodifikasi dokumen ini berdasarkan ketentuan Lisensi Dokumentasi Bebas GNU, Versi 1.2 atau versi yang lebih baru yang diterbitkan oleh Free Software Foundation; tanpa Bagian Invarian, tanpa Teks Sampul Depan, dan tanpa Teks Sampul Belakang. Salinan lisensi dimasukkan dalam bagian yang berjudul "Lisensi Dokumentasi Bebas GNU".

bmdhacks
sumber
0

Dari sinyal halaman manual (7)

Sinyal yang diarahkan pada proses dapat dikirimkan ke salah satu utas yang saat ini tidak memiliki sinyal yang diblokir. Jika lebih dari satu utas memiliki sinyal tidak terblokir, maka kernel memilih utas yang sewenang-wenang untuk mengirimkan sinyal.

Dan saya akan mengatakan "masalah" ini ada untuk sinyal (2) dan sigaction (2) . Jadi berhati-hatilah dengan sinyal dan pthreads.

... dan sinyal (2) tampaknya memanggil sigaction (2) di bawahnya di Linux dengan glibc.

robert.berger
sumber