Proses Linux dapat memblokir sinyal tertentu lalu menerimanya dengan membaca file descriptor alih-alih menjalankan handler asinkron. signalfd() membuat sinyal pending tersebut terlihat melalui antarmuka readiness yang sama dengan socket, pipe, dan descriptor lain, termasuk poll() dan epoll.
Konversi ini bukan pengganti signal masking. Descriptor memiliki himpunan sinyal sendiri, sementara setiap thread tetap memiliki signal mask yang mengendalikan delivery biasa. Desain yang konsisten bergantung pada keselarasan kedua status tersebut.
Descriptor mengamati sinyal pending dari himpunan tertentu
signalfd baru dibuat dengan sebuah himpunan sinyal:
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGINT);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
int sfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);Mask yang diberikan ke signalfd() menentukan sinyal yang dapat dikembalikan melalui descriptor tersebut. Dalam pemakaian normal, sinyal yang sama diblokir terlebih dahulu agar menjadi pending, bukan dikirim sesuai disposition masing-masing.
Kondisi ini menghasilkan dua kontrol yang berbeda. Signal mask milik thread mengatur delivery asinkron. Mask milik signalfd mengatur sinyal pending yang dapat dikonsumsi descriptor. Perubahan pada salah satunya tidak otomatis menulis ulang yang lain.
SIGKILL dan SIGSTOP tidak dapat diterima melalui signalfd. Linux mengabaikannya secara diam-diam jika keduanya dimasukkan ke mask descriptor, sesuai status khusus keduanya dalam model sinyal.
Readiness mewakili status pending, bukan eksekusi handler
signalfd menjadi readable saat setidaknya satu sinyal yang tercakup mask-nya berada dalam status pending untuk konteks pembaca. read() yang berhasil mengembalikan satu atau beberapa record signalfd_siginfo, dibatasi oleh ukuran buffer yang diberikan.
Pembacaan memiliki efek semantik di luar penyalinan metadata. Kemunculan sinyal yang dikembalikan akan dikonsumsi dan tidak lagi pending. Kemunculan yang sama tidak dapat kemudian diterima oleh sigwaitinfo() atau dikirim ke handler.
Hal ini membuat readiness bersifat level-sensitive terhadap status sinyal yang dapat dikonsumsi. Jika beberapa sinyal yang memenuhi syarat sedang pending dan satu operasi read menyisakan sebagian, descriptor dapat tetap readable. Dengan SFD_NONBLOCK, read tanpa sinyal pending yang memenuhi syarat gagal dengan EAGAIN dan tidak menunggu.
Konsekuensi bagi event loop bersifat spesifik: readiness menyatakan bahwa read dapat mengonsumsi status sinyal pada saat itu. Readiness tidak mencadangkan kemunculan tertentu bagi waiter tertentu.
Sinyal standar dan real-time mempertahankan semantik antrean yang berbeda
signalfd mengubah antarmuka penerimaan, bukan semantik dasar sinyal Linux. Sinyal standar pada umumnya tidak mengantrekan banyak instance saat diblokir. Jika sinyal standar yang sama dihasilkan berulang kali sebelum diterima, status pending dapat mewakili hanya satu kemunculan.
Sinyal real-time diantrekan dan mempertahankan beberapa instance yang dihasilkan, dengan tetap tunduk pada batas sumber daya yang berlaku. Pembaca signalfd karena itu dapat menerima beberapa record untuk sinyal real-time yang diantrekan, sedangkan sinyal standar berulang dapat menyatu menjadi satu indikasi pending.
Perbedaan ini penting saat aplikasi memperlakukan jumlah sinyal sebagai data. Memindahkan penerimaan ke event loop tidak menjadikan sinyal standar sebagai counter yang andal. Jika setiap kemunculan harus tetap terpisah, kelas sinyal dan semantik antreannya tetap menjadi bagian kontrak.
Signal mask thread menentukan batas pada proses multithread
Signal mask bersifat per-thread di Linux. Sinyal yang diarahkan ke proses dapat dikirim ke thread mana pun yang tidak memblokirnya dan memenuhi syarat untuk delivery. Memblokir sinyal hanya pada thread pemilik event loop tidak cukup jika thread lain membiarkan sinyal tersebut tidak diblokir.
Pola signalfd yang umum memblokir sinyal terpilih sebelum membuat worker thread. Thread baru mewarisi salinan signal mask milik thread pembuat, sehingga status blocked tersebar melalui pembuatan thread biasa. Thread event loop kemudian dapat mengonsumsi sinyal pending yang diarahkan ke proses melalui signalfd.
Sinyal yang diarahkan ke thread memiliki batas lebih sempit. Thread yang membaca signalfd dapat menerima sinyal yang diarahkan kepada dirinya sendiri dan sinyal yang diarahkan ke proses, tetapi tidak sinyal yang secara khusus diarahkan ke thread lain. Descriptor tidak menggabungkan seluruh himpunan pending privat milik semua thread menjadi satu antrean tingkat proses.
Batas ini mencegah signalfd bertindak sebagai mailbox sinyal universal untuk event yang diarahkan secara arbitrer ke thread tertentu.
Beberapa signalfd bersaing atas kemunculan yang tumpang tindih
Sebuah proses dapat membuat beberapa descriptor signalfd dengan mask berbeda. Mekanisme ini dapat memisahkan kelas sinyal menjadi sumber readiness yang berbeda. Pemisahan tersebut bersih hanya ketika mask tidak tumpang tindih.
Jika dua mask signalfd memuat sinyal yang sama, satu kemunculan pending tidak digandakan untuk kedua descriptor. Kemunculan itu dapat dikonsumsi satu kali melalui salah satu descriptor yang memenuhi syarat. Readiness yang terlihat pada keduanya tidak boleh dianggap sebagai dua salinan event yang independen.
Persaingan serupa berlaku pada antarmuka penerimaan sinyal sinkron lainnya. Sinyal yang dikonsumsi melalui signalfd dihapus dari status pending, sehingga consumer lain tidak dapat menerima kemunculan yang sama sesudahnya.
Ini merupakan batas ownership yang dibentuk oleh konsumsi, bukan oleh registrasi. Consumer yang tumpang tindih berbagi akses ke status pending; mereka tidak menerima delivery fan-out.
Lifetime file descriptor berbeda dari disposition sinyal
Objek signalfd mengikuti aturan lifetime descriptor biasa. Descriptor hasil duplikasi mengacu pada objek signalfd dasar yang sama, dan sumber dayanya tetap ada sampai descriptor terkait terakhir ditutup. SFD_CLOEXEC dapat mencegah descriptor yang baru dibuat bertahan melewati execve() yang berhasil.
Signal mask dan sinyal pending mengikuti aturan proses dan thread masing-masing. Signal mask dipertahankan melewati execve(), dan sinyal blocked yang pending dapat tetap pending. Jika signalfd sengaja diwariskan melewati batas exec, sinyal pending yang memenuhi syarat masih dapat tersedia bagi program baru.
Kombinasi itu dapat berguna, tetapi juga menjadikan pewarisan descriptor dan pewarisan signal mask sebagai satu batas antarmuka gabungan. Pewarisan hanya pada salah satu sisi secara tidak sengaja dapat menghasilkan proses dengan sinyal blocked tanpa consumer yang dimaksud, atau signalfd warisan dengan mask yang tidak lagi sesuai kebijakan event program baru.
Fault sinkron tetap berada di luar model
signalfd cocok untuk sinyal yang dihasilkan secara asinkron dan dapat diblokir lalu diterima kemudian. Mekanisme ini bukan pengganti penanganan sinyal fault hardware sinkron seperti SIGSEGV akibat akses memori tidak valid atau SIGFPE akibat operasi aritmetika yang memicu fault.
Sinyal tersebut muncul sebagai bagian dari eksekusi instruksi yang memicu fault dan memerlukan jalur signal handler ketika aplikasi memilih untuk menangkapnya. Merutekan sinyal kontrol biasa melalui descriptor tidak mengubah fault eksekusi sinkron menjadi pesan event loop.
Batasnya tetap sempit: signalfd mengintegrasikan status sinyal pending terpilih dengan multiplexing file descriptor, sementara model sinyal yang lebih luas tetap mengatur generation, targeting, masking, queueing, dan fault delivery.