首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Linux 新一代 I/O 多路複用利器:signalfd、timerfd、eventfd 深度解析

     編輯:Java 綜合教學

Linux 文件描述符抽象化:signalfd、timerfd、eventfd 詳解

Linux 2.6.22+ 內核引入三種特殊文件描述符,將信號、定時器、事件通知統一抽象為「可讀的文件描述符」,使它們能無縫接入 select/poll/epoll 事件循環,極大簡化了異步編程模型。

三大 FD 核心對比

FD 類型引入版本核心作用典型應用
signalfd2.6.22信號 → 可讀 FD統一信號處理、避免信號處理函數的可重入問題
timerfd2.6.25定時器 → 可讀 FD高精度定時任務、事件循環整合定時器
eventfd2.6.22計數器 → 可讀 FD線程/進程間通知、用戶態與內核通信

signalfd:信號處理現代化

核心 API

#include 
int signalfd(int fd, const sigset_t *mask, int flags);
  • fd = -1:創建新 FD;fd > 0:修改現有 FD 監聽的信號集
  • flags:SFD_NONBLOCK (非阻塞)、SFD_CLOEXEC (執行 exec 時關閉)

完整示例:監聽 SIGINT/SIGQUIT

#include 
#include 
#include 
#include 
#include 

#define handle_error(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)

int main() {
    sigset_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);   // Ctrl+C
    sigaddset(&mask, SIGQUIT);  // Ctrl+\
    
    // 關鍵:阻塞信號,防止默認處理動作
    if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
        handle_error("sigprocmask");
    
    int sfd = signalfd(-1, &mask, 0);
    if (sfd == -1) handle_error("signalfd");
    
    struct signalfd_siginfo fdsi;
    for (;;) {
        ssize_t s = read(sfd, &fdsi, sizeof(fdsi));
        if (s != sizeof(fdsi)) handle_error("read");
        
        if (fdsi.ssi_signo == SIGINT)
            printf("Got SIGINT\n");
        else if (fdsi.ssi_signo == SIGQUIT) {
            printf("Got SIGQUIT\n");
            exit(EXIT_SUCCESS);
        }
    }
}

timerfd:定時器融入事件循環

核心 API

#include 
int timerfd_create(int clockid, int flags);
int timerfd_settime(int fd, int flags, const struct itimerspec *new, struct itimerspec *old);
int timerfd_gettime(int fd, struct itimerspec *curr);

關鍵結構體

struct itimerspec {
    struct timespec it_interval; // 週期間隔
    struct timespec it_value;    // 首次觸發時間
};
struct timespec {
    time_t tv_sec;  // 秒
    long tv_nsec;   // 納秒
};

示例:每 2 秒觸發,首次 5 秒後

int fd = timerfd_create(CLOCK_MONOTONIC, 0); // 單調時鐘,不受系統時間調整影響
struct itimerspec ts = {
    .it_value = {5, 0},      // 5 秒後首次觸發
    .it_interval = {2, 0}    // 之後每 2 秒觸發
};
timerfd_settime(fd, 0, &ts, NULL);

uint64_t exp;
for (;;) {
    read(fd, &exp, sizeof(exp)); // 阻塞等待,exp 為未處理的到期次數
    printf("Timer triggered %llu times\n", exp);
}

編譯:g++ -lrt timerfd.cc -o timerfd (需鏈接 rt 庫)

eventfd:輕量級事件通知

核心特性

  • 內核維護一個 uint64_t 計數器
  • write:計數器 += 寫入值 (8 字節)
  • read:讀取計數器值並**清零**
  • 計數器 > 0 時可讀,可接入 epoll
  • EFD_SEMAPHORE 模式:read 只減 1,不清零 (信號量語義)

進程間通信示例

int efd = eventfd(0, EFD_NONBLOCK);
if (fork() == 0) { // 子進程:寫入
    for (int i = 1; i < argc; i++) {
        uint64_t val = strtoull(argv[i], NULL, 0);
        write(efd, &val, sizeof(val));
    }
    exit(0);
} else { // 父進程:讀取
    sleep(2);
    uint64_t val;
    read(efd, &val, sizeof(val));
    printf("Parent read: %llu\n", val);
}

為什麼要用這三個 FD?

  1. 統一事件循環:信號、定時、自定義事件全部在 epoll_wait 一處處理,避免多線程同步複雜度
  2. 避免信號處理函數陷阱:signalfd 將異步信號同步化,可安全調用非可重入函數
  3. 高精度定時:timerfd 納秒精度,無需單獨定時器線程
  4. 極低開銷通知:eventfd 單次系統調用完成線程喚醒,比 pipe/futex 更輕量
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved