Signal yang masuk ke mask signalfd dapat muncul sebagai state readable pada file descriptor alih-alih menjalankan asynchronous signal handler, selama signal tersebut diblokir dari delivery biasa pada thread yang relevan. Batas interface pun berubah: kedatangan signal dapat ikut dalam event loop berbasis descriptor yang sama dengan socket, pipe, dan objek pollable lain.

Mekanisme ini tidak mengganti semantik signal Linux. Pembentukan signal, signal mask pada proses dan thread, state pending, penggabungan standard signal, antrean real-time signal, serta aturan delivery tetap berlaku. signalfd mengubah interface konsumsi untuk signal yang dipilih oleh mask-nya.

Blocking menetapkan jalur konsumsi

Konfigurasi umum lebih dulu memblokir sekumpulan signal dengan sigprocmask() atau pthread_sigmask(), lalu membuat descriptor dengan set yang sama:

sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGHUP);

pthread_sigmask(SIG_BLOCK, &mask, NULL);

int fd = signalfd(-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK);

Blocking bukan optimasi opsional. Jika signal terpilih tetap tidak diblokir pada thread yang memenuhi syarat untuk menerimanya, delivery signal biasa dapat mengonsumsi signal itu sebelum read pada descriptor mengamatinya. Pada proses multithreaded, kebijakan mask menjadi bagian dari desain proses, bukan hanya urusan thread pemilik event loop.

Signal mask bersifat per-thread. Thread baru mewarisi salinan mask dari thread pembuatnya, tetapi perubahan mask setelah itu bersifat independen. Program yang hendak memusatkan process-directed signal melalui signalfd umumnya menetapkan blocking sebelum membuat worker thread, lalu mempertahankan signal tersebut dalam keadaan blocked pada worker.

Readiness merepresentasikan signal terpilih yang pending

Descriptor signalfd menjadi readable ketika satu atau beberapa signal dalam mask yang dikonfigurasi berada dalam state pending dan dapat dikonsumsi melalui descriptor tersebut. Descriptor ini dapat dipantau dengan poll(), epoll, atau interface readiness descriptor sejenis.

read() yang berhasil mengembalikan satu atau beberapa record struct signalfd_siginfo. Setiap record memuat nomor signal dan field tambahan yang maknanya bergantung pada sumber signal. Struktur ini bukan tipe yang sama dengan siginfo_t, meski beberapa field membawa informasi terkait.

Dengan SFD_NONBLOCK, read tanpa signal pending yang cocok gagal dengan EAGAIN. Tanpa flag tersebut, read dapat memblokir. Dengan demikian, perilaku scheduling dan blocking tampil eksplisit pada API descriptor, bukan tersembunyi di dalam asynchronous handler.

Mask memilih signal, bukan membuat antrean privat

Mask pada descriptor menentukan signal yang dapat diterima melalui descriptor itu. Mask tersebut tidak membuat namespace signal terpisah. Signal pending tetap menjadi bagian dari model signal proses atau thread sampai dikonsumsi.

Perbedaan ini terlihat pada standard signal. Beberapa instance standard signal dapat bergabung ketika signal berada dalam state pending, sehingga aplikasi tidak dapat menyimpulkan jumlah kedatangan dari jumlah read. Real-time signal memiliki semantik antrean yang ditentukan subsistem signal dan dapat mempertahankan beberapa instance pending dengan tunduk pada batas sistem.

Karena itu, signalfd tidak tepat dimodelkan sebagai message queue generik dengan relasi satu write untuk satu read. Record yang terlihat tetap mewarisi aturan antrean dari kelas signal yang mendasarinya.

Pembaruan descriptor mengganti selection mask

Pemanggilan signalfd() dengan descriptor yang sudah ada mengganti signal mask yang terkait dengan descriptor tersebut:

sigaddset(&mask, SIGUSR1);

if (signalfd(fd, &mask, 0) == -1) {
    /* handle error */
}

Call tersebut mengubah signal pending mana yang dapat dikonsumsi descriptor. Call itu tidak mengubah blocked-signal mask milik thread pemanggil. Kode yang memperbarui satu sisi tanpa sisi lain dapat menghasilkan ketidaksesuaian antara signal yang diblokir dari delivery biasa dan signal yang diterima melalui descriptor.

Pemisahan ini berguna, tetapi kontraknya perlu eksplisit. Thread mask mengontrol kelayakan delivery biasa; descriptor mask mengontrol kelayakan konsumsi melalui descriptor.

Process-directed dan thread-directed signal mempertahankan scope

Linux membedakan signal yang diarahkan ke proses dari signal yang diarahkan ke thread tertentu. signalfd tidak menghapus perbedaan tersebut.

Sebuah thread dapat mengonsumsi signal yang pending dan memenuhi syarat menurut aturan signal kernel. Read pada descriptor bukan mekanisme bagi satu thread sembarang untuk mengambil setiap thread-directed signal milik thread lain. API seperti pthread_kill() tetap menargetkan thread tertentu, dan penempatan signal mask tetap memengaruhi state pending yang dihasilkan.

Hal ini relevan bagi arsitektur event loop yang memusatkan signal terminasi atau reload. Pemusatan bekerja dengan jelas ketika signal terpilih dan scope pembentukannya selaras dengan kebijakan masking lintas thread.

Batas handler hilang, semantik signal tetap ada

Asynchronous handler tradisional berjalan pada titik interupsi dan dibatasi pada operasi async-signal-safe. Event signalfd dikonsumsi oleh kode biasa setelah descriptor menjadi readable, sehingga kode itu berjalan dalam control flow normal event loop dan tidak dibatasi pada kumpulan fungsi async-signal-safe hanya karena event berasal dari signal.

Constraint lain tetap ada. Event loop perlu menguras record dengan benar, menangani EAGAIN pada mode nonblocking, dan memperhitungkan lifetime descriptor. Menutup descriptor tidak otomatis membuka blokir signal pada thread mana pun. Jika program kemudian mengharapkan delivery biasa, thread mask harus diubah secara eksplisit.

SIGKILL dan SIGSTOP juga tidak dapat diblokir sehingga tidak dapat dikonsumsi melalui signalfd; memasukkan keduanya ke mask tidak memberi efek untuk delivery melalui descriptor.

Lifetime descriptor dan fork tetap menjadi concern terpisah

Setelah fork(), child mewarisi file descriptor dengan aturan biasa, tetapi state signal pending dan aturan delivery melewati batas fork memiliki semantik tersendiri. Descriptor yang diwariskan tidak boleh diperlakukan sebagai snapshot yang dapat dipindahkan dari signal pending milik parent.

Pewarisan descriptor melewati execve() dikontrol oleh state close-on-exec. SFD_CLOEXEC meminta state tersebut secara atomik saat pembuatan, sehingga tidak ada window fcntl() terpisah pada kode multithreaded.

Properti ini menempatkan signalfd pada pertemuan dua model kernel: lifetime file descriptor dan delivery signal. Pemakaian yang tepat mempertahankan kedua model tersebut secara eksplisit.

Interface descriptor mempersempit control flow asinkron

Efek struktural utama signalfd bukan mengubah signal menjadi byte biasa. Signal tetap memiliki perilaku pending dan antrean yang ditetapkan kernel. Descriptor menyediakan batas read sinkron bagi signal terpilih yang diblokir serta surface readiness yang kompatibel dengan multiplexing descriptor.

Batas tersebut dapat merapikan control flow event loop karena terminasi, reload, dan event proses sejenis dapat didispatch bersama descriptor readable lain. Desain yang dihasilkan tetap presisi hanya jika signal blocking, selection mask descriptor, scope thread, dan lifetime descriptor diperlakukan sebagai satu kontrak yang terkoordinasi.