三種新的fd加入linux內核的的版本:linux
signalfd:2.6.22數據結構
timerfd:2.6.25併發
eventfd:2.6.22app
三種fd的意義:異步
signalfd:傳統的處理信號的方式是註冊信號處理函數;因爲信號是異步發生的,要解決數據的併發訪問,可重入問題。signalfd能夠將信號抽象爲一個文件描述符,當有信號發生時能夠對其read,這樣能夠將信號的監聽放到select、poll、epoll等監聽隊列中。函數
timerfd:能夠實現定時器的功能,將定時器抽象爲文件描述符,當定時器到期時能夠對其read,這樣也能夠放到監聽隊列的主循環中。oop
eventfd:實現了線程之間事件通知的方式,eventfd的緩衝區大小是sizeof(uint64_t);向其write能夠遞增這個計數器,read操做能夠讀取,並進行清零;eventfd也能夠放到監聽隊列中,當計數器不是0時,有可讀事件發生,能夠進行讀取。ui
三種新的fd均可以進行監聽,當有事件觸發時,有可讀事件發生。spa
#include <sys/signalfd.h> int signalfd(int fd, const sigset_t *mask, int flags);
參數fd:若是是-1則表示新建一個,若是是一個已經存在的則表示修改signalfd所關聯的信號;線程
參數mask:信號集合;
參數flag:內核版本2.6.27之後支持SFD_NONBLOCK、SFD_CLOEXEC;
成功返回文件描述符,返回的fd支持如下操做:read、select(poll、epoll)、close
#include <sys/timerfd.h> int timerfd_create(int clockid, int flags); int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value); int timerfd_gettime(int fd, struct itimerspec *curr_value);
timerfd_create:建立一個timerfd;返回的fd能夠進行以下操做:read、select(poll、epoll)、close
timerfd_settime:設置timer的週期,以及起始間隔
timerfd_gettime:獲取到期時間。
//函數參數中數據結構以下: struct timespec { time_t tv_sec; /* Seconds */ long tv_nsec; /* Nanoseconds */ }; struct itimerspec { struct timespec it_interval; /* Interval for periodic timer */ struct timespec it_value; /* Initial expiration */ };
#include <sys/eventfd.h> int eventfd(unsigned int initval, int flags);
建立一個eventfd,這是一個計數器相關的fd,計數器不爲零是有可讀事件發生,read之後計數器清零,write遞增計數器;返回的fd能夠進行以下操做:read、write、select(poll、epoll)、close
這個函數會建立一個 事件對象 (eventfd object), 用來實現,進程(線程)間 的 等待/通知(wait/notify) 機制. 內核會爲這個對象維護一個64位的計數器(uint64_t)。
而且使用第一個參數(initval)初始化這個計數器。調用這個函數就會返回一個新的文件描述符(event object)。2.6.27版本開始能夠按位設置第二個參數(flags)。
有以下的一些宏可使用:
EFD_NONBLOCK , 功能同open(2) 的O_NONBLOCK,設置對象爲非阻塞狀態,若是沒有設置這個狀態的話,read(2)讀eventfd,而且計數器的值爲0 就一直堵塞在read調用當中,要是設置了這個標誌, 就會返回一個 EAGAIN 錯誤(errno = EAGAIN)。效果也如同 額外調用select(2)達到的效果。
EFD_CLOEXEC 個人理解是,這個標識被設置的話,調用exec後會自動關閉文件描述符,防止泄漏。
若是是2.6.26或以前版本的內核,flags 必須設置爲0。
建立這個對象後,能夠對其作以下操做。
write 將緩衝區寫入的8字節整形值加到內核計數器上。
read 讀取8字節值, 並把計數器重設爲0. 若是調用read的時候計數器爲0, 要是eventfd是阻塞的, read就一直阻塞在這裏,不然就獲得 一個EAGAIN錯誤。
若是buffer的長度小於8那麼read會失敗, 錯誤代碼被設置成 EINVAL。
poll select epoll
close 當不須要eventfd的時候能夠調用close關閉, 當這個對象的全部句柄都被關閉的時候,內核會釋放資源。 爲何不是close就直接釋放呢, 若是調用fork 建立
進程的時候會複製這個句柄到新的進程,並繼承全部的狀態。
eventfd例子
#include <sys/eventfd.h> #include <unistd.h> #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <errno.h> #define handle_error(msg) \ do { perror(msg); exit(1); } while (0) int main( int argc, char **argv ) { uint64_t u; ssize_t s;5 int j; if ( argc < 2 ) { fprintf(stderr, "input <num> in command argument"); exit(1); } int efd; if ( (efd = eventfd(0, EFD_NONBLOCK)) == -1 ) handle_error("eventfd failed"); switch (fork()) { case 0: for( j = 1; j < argc; j ++ ) { printf("Child writing %s to efd\n", argv[j] ); u = strtoull(argv[j], NULL, 0); /* analogesly atoi */ s = write(efd, &u, sizeof(uint64_t)); /* append u to counter */ if ( s != sizeof(uint64_t) ) handle_error("write efd failed"); } printf("child completed write loop\n"); exit(0); default: sleep (2); printf("parent about to read\n"); s = read(efd, &u, sizeof(uint64_t)); if ( s != sizeof(uint64_t) ) { if (errno = EAGAIN) { printf("Parent read value %d\n", s); return 1; } handle_error("parent read failed"); } printf("parent read %d , %llu (0x%llx) from efd\n", s, (unsigned long long)u, (unsigned long long) u); exit(0); case -1: handle_error("fork "); } return 0; }