Sebuah signalfd di Linux menjadi readable ketika signal yang dipilih oleh mask miliknya berstatus pending bagi konteks pembaca. read() yang berhasil tidak sekadar mengamati status tersebut: operasi itu mengonsumsi kemunculan signal yang dikembalikan dan menghapusnya dari status pending.

Perilaku ini memberi signal jalur konsumsi berbasis descriptor. Mekanisme tersebut tidak mengubah subsistem signal menjadi byte stream, dan tidak menggantikan signal mask yang mengendalikan delivery biasa.

Mask descriptor memilih signal pending yang memenuhi syarat

signalfd() mengaitkan sebuah signal set dengan file descriptor. Ketika argumen pertama bernilai -1, Linux membuat objek signalfd baru. Jika descriptor signalfd yang sudah ada diberikan, mask pada objek tersebut diganti.

Mask descriptor menentukan signal mana yang dapat diterima melalui descriptor. Signal mask milik thread menjawab hal yang berbeda: signal mana yang diblokir dari delivery asynchronous biasa.

Pemakaian signalfd umumnya memblokir signal terpilih sebelum mengandalkan read dari descriptor. Tanpa pemblokiran tersebut, signal yang sesuai dapat mengikuti disposition miliknya dan dikirim di luar jalur konsumsi signalfd.

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

pthread_sigmask(SIG_BLOCK, &mask, NULL);

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

SIGKILL dan SIGSTOP tidak dapat diterima melalui signalfd. Linux mengabaikannya secara diam-diam jika keduanya dimasukkan ke mask descriptor.

Read mengonsumsi status signal

Read pada signalfd mengembalikan satu atau beberapa record struct signalfd_siginfo. Buffer harus dapat menampung setidaknya satu struktur utuh, sedangkan buffer yang lebih besar dapat menerima beberapa signal pending dalam satu pemanggilan.

struct signalfd_siginfo info[8];

ssize_t n = read(fd, info, sizeof(info));
if (n > 0) {
    size_t count = (size_t)n / sizeof(info[0]);
    /* info[0..count-1] mendeskripsikan signal yang dikonsumsi */
}

Setiap record mendeskripsikan satu kemunculan signal melalui field yang sepadan dengan siginfo_t, termasuk nomor signal dan, jika relevan, informasi pengirim atau payload.

Read bersifat destruktif terhadap status pending. Signal yang dikembalikan melalui signalfd tidak lagi pending dan tidak dapat kemudian diterima oleh sigwaitinfo() atau dikirim ke handler sebagai kemunculan pending yang sama.

Dengan SFD_NONBLOCK, read saat tidak ada signal terpilih yang pending gagal dengan EAGAIN. Tanpa mode nonblocking, read menunggu sampai signal yang memenuhi syarat tersedia.

Readiness mengikuti status pending

poll(), select(), dan epoll melaporkan signalfd sebagai readable ketika setidaknya satu signal dalam mask descriptor berstatus pending dan tersedia bagi konteks pembaca.

Hal ini memasukkan penerimaan signal ke loop yang juga menunggu socket, pipe, timerfd, atau eventfd. Notifikasi readiness tetap merepresentasikan status, bukan salinan record signal yang terpisah. Consumer lain dapat mengonsumsi status pending yang memenuhi syarat sebelum reader tertentu menjalankan read().

Karena itu, event loop memperlakukan hasil read() berikutnya sebagai status final. Readiness hanya menyatakan bahwa nonblocking read dapat berjalan pada titik waktu yang direpresentasikan oleh mekanisme readiness.

Signal standar dan real-time tetap mengikuti semantik signal

signalfd mengubah interface penerimaan, bukan aturan queueing signal Linux.

Signal standar tidak mengantrekan beberapa instance ketika signal yang sama sudah pending. Beberapa generation dari satu signal standar yang diblokir dapat menyatu menjadi satu instance pending. Read signalfd berikutnya dapat mengembalikan satu record meskipun signal tersebut dihasilkan beberapa kali.

Signal real-time memiliki semantik queueing dan dapat mempertahankan beberapa instance pending. Buffer read signalfd yang cukup besar dapat mengembalikan beberapa record yang berasal dari signal real-time dalam antrean.

Batas ini penting ketika signal dipakai sebagai counter. Signal standar tidak dapat merepresentasikan jumlah aksi producer secara andal hanya karena signalfd mengeksposnya sebagai record. Informasi jumlah dapat sudah hilang pada status pending sebelum descriptor menjadi readable.

Status yang diarahkan ke thread membatasi konsumsi reader

Linux membedakan signal yang diarahkan ke process dari signal yang ditargetkan ke thread tertentu. Thread yang membaca signalfd dapat menerima signal untuk dirinya sendiri dan signal process-directed yang tersedia bagi thread group. Read tersebut tidak dapat mengonsumsi signal yang secara khusus diarahkan ke thread lain.

Signal mask juga bersifat per-thread. Dalam process multithreaded, memblokir signal hanya pada thread pemilik event loop tetap memungkinkan thread lain menerima signal process-directed melalui aturan delivery signal biasa.

Desain yang memusatkan signal terpilih pada satu event loop signalfd karena itu lazim menetapkan blocking mask sebelum worker thread dibuat, sehingga thread baru mewarisi status blocked. Perubahan mask setelahnya tetap lokal pada thread kecuali diterapkan secara terpisah.

Referensi descriptor bersama tidak membuat antrean independen

Setelah fork(), child mewarisi descriptor signalfd, tetapi read pada child berkaitan dengan signal yang masuk ke antrean child. Mengirim descriptor signalfd ke process lain melalui UNIX domain socket juga membuat read pada process penerima berkaitan dengan signal yang masuk ke process penerima tersebut.

File descriptor tersebut bukan mailbox portabel yang menyimpan record signal untuk process asal. Perilaku read tetap terikat pada status pending signal Linux untuk process dan thread yang melakukan read.

Saat melewati execve(), signalfd tetap terbuka kecuali close-on-exec diaktifkan. Signal blocked yang pending juga dapat bertahan melewati penggantian image, sehingga descriptor yang sengaja dipertahankan dapat terus mengekspos status pending yang memenuhi syarat kepada program baru.

Signal fault sinkron berada di luar model

signalfd bukan jalur pengganti untuk signal fault yang dihasilkan secara sinkron, seperti SIGSEGV akibat akses memori invalid atau SIGFPE akibat arithmetic fault. Linux mendokumentasikan fault tersebut di luar model penerimaan signalfd; jika perlu ditangkap, fault semacam ini memakai jalur signal handler tradisional.

Batas tersebut memisahkan koordinasi signal eksternal atau asynchronous yang pending dari fault yang terkait langsung dengan eksekusi instruksi tertentu. Mengubah fault jenis kedua menjadi record event loop biasa tidak mempertahankan semantik eksekusi yang diperlukan untuk delivery fault.

Konsumsi descriptor mengubah struktur event loop, bukan identitas signal

Efek utama signalfd bersifat struktural. Signal blocked yang dipilih dapat ikut dalam fase readiness wait dan read yang sama dengan descriptor Linux lain, sementara identitas serta sifat queueing-nya tetap mengikuti signal.

Mask descriptor memilih kandidat, signal mask thread mencegah delivery biasa, status pending menentukan readability, dan read() mengonsumsi record dari status tersebut. Coalescing signal standar, queueing signal real-time, targeting thread, dan batas synchronous fault tetap berlaku.

Kombinasi ini sesuai untuk sistem yang perlu memasukkan control event berbasis signal ke event loop tanpa eksekusi handler asynchronous. Kontraknya tetap lebih sempit daripada message queue umum: read descriptor mengekspos dan mengonsumsi status signal; operasi tersebut tidak mendefinisikan ulang status itu.