Linux signalfd memberi sinyal terpilih jalur konsumsi berbasis descriptor. Alih-alih memindahkan kontrol ke handler asinkron, proses dapat memblokir sinyal tersebut, mengaitkannya dengan objek signalfd, lalu mengonsumsi instance yang pending melalui read(). Descriptor ini juga dapat masuk ke poll(), select(), dan epoll, sehingga penerimaan sinyal dapat berada bersama socket, timer, dan sumber readiness lain.
Antarmuka ini khusus Linux. Signal mask, aturan pending signal, dan operasi descriptor berasal dari semantik sinyal Linux serta POSIX pada bagian yang relevan; semuanya bukan properti bahasa C itu sendiri.
Blocking membentuk batas konsumsi
Mask yang diberikan ke signalfd() memilih sinyal yang dapat diterima descriptor. Mask tersebut tidak dengan sendirinya memblokir delivery sinyal biasa. Pada pemakaian normal, sinyal yang sama diblokir pada thread penerima sebelum descriptor digunakan:
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGHUP);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
int sfd = signalfd(-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK);Sinyal yang diblokir dapat tetap pending, alih-alih menjalankan handler normal atau aksi default. signalfd kemudian menyediakan operasi sinkron yang dapat mengonsumsi state pending yang cocok.
Pemisahan ini penting karena ada dua mask dalam desain. Signal mask milik thread mengendalikan delivery asinkron, sedangkan mask milik signalfd menentukan sinyal pending mana yang memenuhi syarat untuk dibaca dari descriptor tersebut. Menganggap mask descriptor sebagai pengganti blocking sinyal membuat jalur delivery lain tetap aktif.
SIGKILL dan SIGSTOP tidak dapat diterima melalui signalfd. Memasukkan salah satunya ke mask descriptor tidak memberi efek.
Read mengonsumsi state sinyal, bukan menyalin notifikasi
read() yang berhasil mengembalikan satu atau beberapa record struct signalfd_siginfo. Setiap record memiliki layout tetap 128 byte dan membawa field seperti nomor sinyal, identitas pengirim jika berlaku, serta metadata khusus sinyal.
Buffer harus cukup besar untuk setidaknya satu record. Jika beberapa sinyal yang memenuhi syarat sedang pending dan buffer memiliki ruang, satu read dapat mengembalikan beberapa record. Konsumsi sebuah record menghapus instance sinyal tersebut dari state pending; instance yang sama tidak lagi tersedia bagi handler atau sigwaitinfo().
Perilaku ini menjadikan descriptor sebagai konsumen mekanisme sinyal proses, bukan cerminnya. Aplikasi tidak dapat menganggap read pada signalfd sekadar mengamati event sambil mempertahankan sinyal asli untuk konsumen lain.
Sinyal standar dan real-time juga mempertahankan semantik antreannya. Sinyal standar umumnya menyatu saat pending, sehingga pembangkitan berulang untuk sinyal standar yang sama tidak berarti satu record signalfd untuk setiap pembangkitan. Sinyal real-time dapat mengantre beberapa instance. signalfd mengubah antarmuka penerimaan, bukan model antrean sinyal yang mendasarinya.
Readiness merepresentasikan state pending yang cocok
Descriptor signalfd menjadi readable ketika setidaknya satu sinyal yang dipilih mask sedang pending dan memenuhi syarat bagi caller. read() nonblocking tanpa sinyal pending yang memenuhi syarat gagal dengan EAGAIN; read blocking akan menunggu.
Properti readiness ini membuat signalfd cocok dengan event loop berbasis descriptor. Event loop dapat menunggu socket, timerfd, eventfd, dan signalfd melalui satu antarmuka multiplexing, lalu menjalankan control flow biasa setelah readiness dilaporkan.
Readiness tidak berarti delivery sinyal berubah menjadi protokol byte stream. State pending tetap menjadi bagian dari subsistem sinyal kernel, dan read mengonsumsi record sinyal terstruktur sesuai aturan sinyal. Notifikasi readiness juga dapat menjadi stale sebelum read berikutnya jika thread lain atau signalfd lain yang memenuhi syarat lebih dahulu mengonsumsi sinyal pending tersebut.
Thread mask tetap merupakan state per-thread
Setiap thread memiliki signal mask sendiri. Hal ini menciptakan batas penting pada level proses untuk program multithreaded: memblokir sinyal hanya pada thread yang membuat signalfd tidak otomatis mencegah thread lain menerima instance process-directed secara asinkron.
Arsitektur yang umum memblokir sinyal terpilih sebelum membuat worker thread. Thread baru mewarisi salinan signal mask thread pembuatnya, sehingga state blocked dimulai secara konsisten di seluruh thread group. Thread event loop yang ditunjuk kemudian dapat mengonsumsi sinyal process-directed melalui signalfd.
Ini merupakan aturan koordinasi yang dibentuk dari pewarisan signal mask, bukan jaminan khusus signalfd. Jika thread lain kemudian membuka blokir sinyal terpilih, thread tersebut dapat kembali menjadi target delivery asinkron sesuai aturan pemilihan sinyal biasa.
Sinyal thread-directed menambah batas lain. Thread yang membaca signalfd dapat mengonsumsi sinyal yang diarahkan kepadanya sendiri serta sinyal yang diarahkan ke proses secara keseluruhan. Read tersebut tidak dapat dipakai untuk mengonsumsi sinyal yang diarahkan secara khusus ke thread lain.
Mask descriptor dapat diganti di tempat
Memberikan -1 sebagai argumen pertama signalfd() membuat descriptor baru. Memberikan descriptor signalfd yang sudah ada mengganti signal set yang terkait dengan objek tersebut.
Operasi itu mengubah kelayakan read descriptor berikutnya, tetapi tidak menulis ulang thread mask milik proses. Jika aplikasi menambahkan sinyal ke mask signalfd tanpa mengatur blocking yang sesuai, delivery asinkron masih dapat terjadi. Menghapus sinyal dari mask descriptor juga tidak membuka blokir sinyal tersebut.
Karena itu, kedua lapisan perlu diperbarui secara terkoordinasi saat kebijakan sinyal berubah ketika runtime. Konfigurasi descriptor dan konfigurasi thread mask yang tidak lagi selaras dapat menghasilkan sinyal yang tetap diblokir tanpa konsumen yang dituju, atau sinyal yang melewati event loop.
Beberapa descriptor bersaing atas instance pending yang sama
Sebuah proses dapat membuat beberapa objek signalfd dengan mask berbeda atau saling tumpang tindih. Mask yang berbeda dapat memisahkan sinyal kontrol ke sumber readiness yang berbeda. Namun, pada mask yang tumpang tindih, instance pending yang cocok dengan lebih dari satu descriptor hanya dapat dikonsumsi sekali melalui salah satu descriptor yang memenuhi syarat.
Karena itu, objek signalfd yang tumpang tindih bukan subscription independen. Semuanya adalah konsumen alternatif atas state sinyal bersama. Desain yang memerlukan fan-out harus melakukan fan-out setelah satu komponen menerima sinyal.
Duplikasi descriptor memiliki bentuk berbeda. Descriptor hasil duplikasi merujuk ke objek signalfd yang sama, termasuk mask yang dikonfigurasi. Menutup satu duplikat tidak menghancurkan objek selama descriptor lain masih merujuk kepadanya.
fork dan exec mempertahankan bagian state yang berbeda
Setelah fork(), child mewarisi file descriptor dan salinan signal mask milik thread pemanggil. Read dari signalfd yang diwariskan pada child berkaitan dengan sinyal yang pending untuk child, bukan sinyal yang pending untuk parent.
Ada interaksi yang lebih sempit dengan epoll. Signalfd yang didaftarkan ke instance epoll sebelum fork() dapat diwariskan oleh child, dan child dapat membaca sinyalnya sendiri dari descriptor tersebut, tetapi registrasi epoll yang diwariskan tidak melaporkan readiness signalfd untuk sinyal yang dikirim ke child. Child yang memerlukan readiness sinyal berbasis epoll dapat membuat atau mendaftarkan signalfd yang sesuai setelah batas fork.
Melintasi execve(), signalfd tetap terbuka kecuali close-on-exec diaktifkan. Signal mask juga dipertahankan melintasi execve(). Hal ini dapat berguna jika pewarisan descriptor memang disengaja, tetapi pewarisan yang tidak disengaja dapat membawa state blocked-signal ke program yang mengharapkan penanganan sinyal konvensional. SFD_CLOEXEC membuat sisi descriptor dari batas tersebut eksplisit; kebijakan signal mask tetap memerlukan penanganan terpisah.
Sinyal fault sinkron tetap berada di luar mekanisme ini
signalfd bukan pengganti umum untuk setiap signal handler. Linux mendokumentasikan bahwa sinyal fault yang dihasilkan secara sinkron, seperti SIGSEGV akibat akses memori tidak valid atau SIGFPE akibat fault aritmetika, tidak dapat diterima melalui signalfd dalam pola blocked-signal biasa. Fault semacam itu memerlukan jalur signal handler jika aplikasi menanganinya.
Batas tersebut berasal dari hubungan antara fault dan execution context yang memicunya. Memindahkan sinyal kontrol proses seperti SIGTERM ke event loop merupakan operasi berbeda dari mengambil kembali kontrol pada instruksi yang mengalami fault.
Integrasi descriptor mengubah control flow, bukan semantik sinyal
Dampak utama signalfd bersifat struktural. Event kontrol asinkron terpilih dapat diterima melalui read biasa pada titik yang terkendali di event loop, sehingga entry ke handler secara arbitrer dapat dihindari untuk sinyal tersebut.
Kernel tetap menerapkan signal mask, aturan pending state, antrean sinyal standar versus real-time, targeting process-directed versus thread-directed, serta semantik pewarisan. Pemakaian yang kokoh karena itu perlu memperlakukan signalfd sebagai satu antarmuka konsumen di atas subsistem sinyal, dengan thread mask membentuk batas yang mengarahkan sinyal yang memenuhi syarat kepadanya.