Linux 新的API signalfd、timerfd、eventfd使用說明

三種新的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

signalfd涉及API:

#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

timerfd涉及的API

    

#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 */  
};

 

eventfd涉及API:

#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;
}
相關文章
相關標籤/搜索