signalfd Mengonsumsi Sinyal yang Diblokir Melalui Read File Descriptor

signalfd() mengubah notifikasi sinyal terpilih menjadi record yang dapat dikonsumsi dengan read(). Sinyal pending yang cocok membuat descriptor menjadi readable, sehingga pemrosesan sinyal dapat berada pada jalur poll(), select(), atau epoll yang sama dengan socket, timer, dan descriptor lain.

Descriptor tidak otomatis mengalihkan sinyal hanya karena mask miliknya memuat nomor sinyal tersebut. Aturan signal mask normal tetap berlaku. Pada susunan yang umum, sinyal terpilih diblokir agar tidak dikirim melalui disposition biasa, lalu sebuah signalfd membaca instance yang pending.

Mask descriptor dan signal mask thread adalah dua hal terpisah

Mask yang diberikan ke signalfd() memilih sinyal yang dapat diterima descriptor. Mask tersebut tidak dengan sendirinya memblokir delivery sinyal.

Sebuah proses biasanya menetapkan kedua bagian secara eksplisit:

sigset_t mask;

sigemptyset(&mask);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGINT);

if (pthread_sigmask(SIG_BLOCK, &mask, NULL) != 0) {
    /* handle error */
}

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

pthread_sigmask() mengubah blocked set milik thread pemanggil. signalfd() membuat descriptor dengan acceptance mask miliknya sendiri. Menganggap keduanya sebagai satu pengaturan menutupi batas penting: sinyal yang tetap unblocked pada thread yang eligible dapat dikirim melalui mekanisme sinyal biasa alih-alih tetap pending untuk dibaca melalui signalfd.

Batas ini makin penting ketika thread dibuat. Thread POSIX baru mewarisi salinan signal mask dari thread pembuatnya. Memblokir set sinyal sebelum worker dibuat memberi worker keadaan blocked yang diwariskan, kecuali worker tersebut kemudian mengubahnya.

Read yang berhasil mengonsumsi instance sinyal pending

Saat setidaknya satu sinyal yang cocok sedang pending, read() mengembalikan satu atau beberapa record struct signalfd_siginfo, dibatasi oleh ukuran buffer. Buffer harus cukup untuk setidaknya satu record.

struct signalfd_siginfo info[8];

ssize_t n = read(sfd, info, sizeof(info));
if (n > 0) {
    size_t count = (size_t)n / sizeof(info[0]);
    for (size_t i = 0; i < count; ++i) {
        /* inspect info[i].ssi_signo and related fields */
    }
}

Operasi read bukan salinan pasif dari status sinyal. Sinyal yang dikembalikan akan dikonsumsi dan tidak lagi pending untuk proses. Instance pending yang sama tidak dapat kemudian diterima lagi oleh sigwaitinfo() atau ditangkap handler.

Pada descriptor nonblocking, read tanpa sinyal pending yang cocok gagal dengan EAGAIN. Pada operasi blocking, read menunggu sampai sinyal yang diterima descriptor tersedia.

Beberapa objek signalfd dapat bersaing untuk sinyal yang sama

Satu proses dapat membuat beberapa objek signalfd dengan mask berbeda. Mask tersebut juga dapat saling overlap.

Jika sebuah sinyal berada dalam mask lebih dari satu descriptor, satu occurrence yang pending dapat dibaca sekali dari salah satu descriptor yang eligible. Occurrence yang sama tidak diduplikasi ke queue privat terpisah untuk setiap descriptor. signalfd mengonsumsi status sinyal pending milik proses atau thread, bukan membuat salinan privat setiap occurrence untuk setiap descriptor.

Properti ini penting saat beberapa event loop memantau set sinyal yang overlap. Descriptor terpisah tidak menghasilkan semantik broadcast. Jika beberapa komponen memerlukan notifikasi independen atas event yang sama, fan-out tersebut perlu dibangun di atas titik penerimaan sinyal.

Sinyal thread-directed dan process-directed mempertahankan scope normal

Routing sinyal tetap mengikuti semantik sinyal Linux. Sebuah sinyal dapat menargetkan proses secara keseluruhan atau thread tertentu.

Saat sebuah thread membaca signalfd, thread tersebut dapat menerima sinyal yang diarahkan kepadanya serta sinyal process-directed yang tersedia untuk thread group. Read itu tidak dapat mengambil sinyal yang diarahkan secara khusus ke thread lain.

Karena itu, penempatan mask menjadi bagian dari arsitektur program multithreaded. Susunan yang umum memblokir sinyal yang dikelola sebelum worker dibuat dan menetapkan satu jalur pemrosesan event untuk mengonsumsi notifikasi process-directed. Signaling yang spesifik ke thread tetap harus mengikuti target thread dan status pending miliknya.

SIGKILL dan SIGSTOP tetap berada di luar interface

Memasukkan SIGKILL atau SIGSTOP ke mask signalfd tidak memberi efek; Linux mengabaikan entry tersebut. Kedua sinyal itu juga tidak dapat diblokir melalui interface signal mask normal.

Sinyal fault sinkron membentuk batas lain. Fault seperti akses memori tidak valid yang menghasilkan SIGSEGV tidak cocok untuk pola signalfd dengan sinyal yang diblokir. Linux mendokumentasikan sinyal fault yang dihasilkan secara sinkron sebagai kasus yang memerlukan signal handler tradisional, bukan penerimaan melalui signalfd.

File descriptor menyatu dengan event loop tanpa mengubah semantik sinyal

Nilai struktural utama signalfd bukan model delivery sinyal baru. Interface ini menyediakan representasi file descriptor untuk penerimaan sinyal sehingga dapat ikut dalam readiness multiplexing yang sudah digunakan aplikasi.

Representasi tersebut dapat mengeluarkan eksekusi handler asinkron dari jalur sinyal terpilih, tetapi hanya jika signal mask di sekitarnya disusun secara konsisten. Mask descriptor memilih nomor sinyal yang diterima, mask thread mengontrol eligibility delivery biasa, dan read() mengonsumsi instance pending. Pemisahan peran tersebut menjaga batas semantik yang benar-benar diberikan oleh signalfd.