linux新的API signalfd、timerfd、eventfd使用说明

来源:互联网 发布:火四川方言版网络原唱 编辑:程序博客网 时间:2024/05/22 07:05

[+]

三种新的fd加入linux内核的的版本:

signalfd:2.6.22

timerfd:2.6.25

eventfd:2.6.22

三种fd的意义:

signalfd:传统的处理信号的方式是注册信号处理函数;由于信号是异步发生的,要解决数据的并发访问,可重入问题。signalfd可以将信号抽象为一个文件描述符,当有信号发生时可以对其read,这样可以将信号的监听放到select、poll、epoll等监听队列中。

timerfd:可以实现定时器的功能,将定时器抽象为文件描述符,当定时器到期时可以对其read,这样也可以放到监听队列的主循环中。

eventfd:实现了线程之间事件通知的方式,eventfd的缓冲区大小是sizeof(uint64_t);向其write可以递增这个计数器,read操作可以读取,并进行清零;eventfd也可以放到监听队列中,当计数器不是0时,有可读事件发生,可以进行读取。

三种新的fd都可以进行监听,当有事件触发时,有可读事件发生。

signalfd涉及API:
[cpp] view plain copy
  1. #include <sys/signalfd.h>  
  2. 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
[cpp] view plain copy
  1. #include <sys/timerfd.h>  
  2. int timerfd_create(int clockid, int flags);  
  3. int timerfd_settime(int fd, int flags,  
  4.                     const struct itimerspec *new_value,  
  5.                     struct itimerspec *old_value);  
  6. int timerfd_gettime(int fd, struct itimerspec *curr_value);  
[cpp] view plain copy
  1. timerfd_create:创建一个timerfd;返回的fd可以进行如下操作:read、select(poll、epoll)、close  
  2. timerfd_settime:设置timer的周期,以及起始间隔  
  3. timerfd_gettime:获取到期时间。  
[cpp] view plain copy
  1. 函数参数中数据结构如下:  
  2. struct timespec  
  3. {  
  4.     time_t tv_sec;                /* Seconds */  
  5.     long   tv_nsec;               /* Nanoseconds */  
  6. };  
  7.   
  8. struct itimerspec  
  9. {  
  10.     struct timespec it_interval;  /* Interval for periodic timer */  
  11.     struct timespec it_value;     /* Initial expiration */  
  12. };  
eventfd涉及API:
[cpp] view plain copy
  1. #include <sys/eventfd.h>  
  2. int eventfd(unsigned int initval, int flags);  

创建一个eventfd,这是一个计数器相关的fd,计数器不为零是有可读事件发生,read以后计数器清零,write递增计数器;返回的fd可以进行如下操作:read、write、select(poll、epoll)、close

 

这是三种新的fd的基本概念,接下来的几篇会通过例子程序来说明各种fd的用法。


上一篇介绍了三种fd的概念,今天看一下signalfd的例子程序,直接上代码,稍后做一点说明。

[cpp] view plain copy
  1. #include <sys/signalfd.h>  
  2. #include <signal.h>  
  3. #include <unistd.h>  
  4. #include <stdlib.h>  
  5. #include <stdio.h>  
  6.   
  7. #define handle_error(msg) \  
  8.    do { perror(msg); exit(EXIT_FAILURE); } while (0)  
  9.   
  10. int main(int argc, char *argv[])  
  11. {  
  12.    sigset_t mask;  
  13.    int sfd;  
  14.    struct signalfd_siginfo fdsi;  
  15.    ssize_t s;  
  16.   
  17.    sigemptyset(&mask);  
  18.    sigaddset(&mask, SIGINT);  
  19.    sigaddset(&mask, SIGQUIT);  
  20.   
  21.    if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)  
  22.        handle_error("sigprocmask");  
  23.   
  24.    sfd = signalfd(-1, &mask, 0);  
  25.    if (sfd == -1)  
  26.        handle_error("signalfd");  
  27.   
  28.    for (;;) {  
  29.        s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo));  
  30.        if (s != sizeof(struct signalfd_siginfo))  
  31.            handle_error("read");  
  32.   
  33.        if (fdsi.ssi_signo == SIGINT) {  
  34.            printf("Got SIGINT\n");  
  35.        } else if (fdsi.ssi_signo == SIGQUIT) {  
  36.            printf("Got SIGQUIT\n");  
  37.            exit(EXIT_SUCCESS);  
  38.        } else {  
  39.            printf("Read unexpected signal\n");  
  40.        }  
  41.    }  
  42. }  
L17-L21:将感兴趣的信号加入到sigset_t中;

L24:调用signalfd,把信号集与fd关联起来,第一个参数为-1表示新建一个signalfd,不是-1并且是一个合法的signalfd表示向其添加新的信号。

L29:阻塞等待信号的发生并读取。根据读取的结果可以知道发生了什么信号。

[cpp] view plain copy
  1. struct signalfd_siginfo的结构  
  2. struct signalfd_siginfo {  
  3.     uint32_t ssi_signo;   /* Signal number */  
  4.     int32_t  ssi_errno;   /* Error number (unused) */  
  5.     int32_t  ssi_code;    /* Signal code */  
  6.     uint32_t ssi_pid;     /* PID of sender */  
  7.     uint32_t ssi_uid;     /* Real UID of sender */  
  8.     int32_t  ssi_fd;      /* File descriptor (SIGIO) */  
  9.     uint32_t ssi_tid;     /* Kernel timer ID (POSIX timers) 
  10.     uint32_t ssi_band;    /* Band event (SIGIO) */  
  11.     uint32_t ssi_overrun; /* POSIX timer overrun count */  
  12.     uint32_t ssi_trapno;  /* Trap number that caused signal */  
  13.     int32_t  ssi_status;  /* Exit status or signal (SIGCHLD) */  
  14.     int32_t  ssi_int;     /* Integer sent by sigqueue(3) */  
  15.     uint64_t ssi_ptr;     /* Pointer sent by sigqueue(3) */  
  16.     uint64_t ssi_utime;   /* User CPU time consumed (SIGCHLD) */  
  17.     uint64_t ssi_stime;   /* System CPU time consumed (SIGCHLD) */  
  18.     uint64_t ssi_addr;    /* Address that generated signal 
  19.                             (for hardware-generated signals) */  
  20.     uint8_t  pad[X];      /* Pad size to 128 bytes (allow for 
  21.                              additional fields in the future) */  
  22. };  
编译运行,可以看一下效果是不是很简单。

详细信息可以在Linux下面:man signalfd,例子程序就是摘自man。


看一下timerfd的例子,上代码:

[cpp] view plain copy
  1. #include <sys/timerfd.h>  
  2. #include <sys/time.h>  
  3. #include <time.h>  
  4. #include <unistd.h>  
  5. #include <stdlib.h>  
  6. #include <stdio.h>  
  7. #include <stdint.h>        /* Definition of uint64_t */  
  8.   
  9. #define handle_error(msg) \  
  10.        do { perror(msg); exit(EXIT_FAILURE); } while (0)  
  11.   
  12. void printTime()  
  13. {    
  14.     struct timeval tv;    
  15.     gettimeofday(&tv, NULL);    
  16.     printf("printTime:  current time:%ld.%ld ", tv.tv_sec, tv.tv_usec);  
  17. }  
  18.   
  19. int main(int argc, char *argv[])  
  20. {  
  21.    struct timespec now;  
  22.    if (clock_gettime(CLOCK_REALTIME, &now) == -1)  
  23.        handle_error("clock_gettime");  
  24.   
  25.    struct itimerspec new_value;  
  26.    new_value.it_value.tv_sec = now.tv_sec + atoi(argv[1]);  
  27.    new_value.it_value.tv_nsec = now.tv_nsec;  
  28.    new_value.it_interval.tv_sec = atoi(argv[2]);  
  29.    new_value.it_interval.tv_nsec = 0;  
  30.   
  31.    int fd = timerfd_create(CLOCK_REALTIME, 0);  
  32.    if (fd == -1)  
  33.        handle_error("timerfd_create");  
  34.   
  35.    if (timerfd_settime(fd, TFD_TIMER_ABSTIME, &new_value, NULL) == -1)  
  36.        handle_error("timerfd_settime");  
  37.   
  38.    printTime();  
  39.    printf("timer started\n");  
  40.      
  41.    for (uint64_t tot_exp = 0; tot_exp < atoi(argv[3]);)   
  42.    {  
  43.        uint64_t exp;  
  44.        ssize_t s = read(fd, &exp, sizeof(uint64_t));  
  45.        if (s != sizeof(uint64_t))  
  46.            handle_error("read");  
  47.   
  48.        tot_exp += exp;  
  49.        printTime();  
  50.        printf("read: %llu; total=%llu\n",exp, tot_exp);  
  51.    }  
  52.   
  53.    exit(EXIT_SUCCESS);  
  54. }  

相关结构体如下:

[cpp] view plain copy
  1. struct timespec {  
  2.    time_t tv_sec;                /* Seconds */  
  3.    long   tv_nsec;               /* Nanoseconds */  
  4. };  
  5.   
  6. struct itimerspec {  
  7.    struct timespec it_interval;  /* Interval for periodic timer */  
  8.    struct timespec it_value;     /* Initial expiration */  
  9. };  

代码L25-L29:初始化定时器的参数,初始间隔与定时间隔。

L32:创建定时器fd,CLOCK_REALTIME:真实时间类型,修改时钟会影响定时器;CLOCK_MONOTONIC:相对时间类型,修改时钟不影响定时器。

L35:设置定时器的值。

L44:阻塞等待定时器到期。返回值是未处理的到期次数。比如定时间隔为2秒,但过了10秒才去读取,则读取的值是5。

编译运行:编译时要加rt库(g++ -lrt timerfd.cc -o timerfd)

[root@localhost appTest]# ./timerfd 5 2 10
printTime:  current time:1357391736.146196 timer started
printTime:  current time:1357391741.153430 read: 1; total=1
printTime:  current time:1357391743.146550 read: 1; total=2
printTime:  current time:1357391745.151483 read: 1; total=3
printTime:  current time:1357391747.161155 read: 1; total=4
printTime:  current time:1357391749.153934 read: 1; total=5
printTime:  current time:1357391751.157309 read: 1; total=6
printTime:  current time:1357391753.158384 read: 1; total=7
printTime:  current time:1357391755.150470 read: 1; total=8
printTime:  current time:1357391757.150253 read: 1; total=9
printTime:  current time:1357391759.149954 read: 1; total=10
[root@localhost appTest]#

第一个参数5为第一次定时器到期间隔,第二个参数2为定时器的间隔,第三个参数为定时器到期10次则退出。程序运行(5+2*10)S退出。

详细信息可以:man timerfd_create

好久没更新了,今天看一下第三种新的fd:eventfd类似于管道的概念,可以实现线程间的事件通知,所不同的是eventfd的缓冲区大小是sizeof(uint64_t)也就是8字节,它是一个64位的计数器,写入递增计数器,读取将得到计数器的值,并且清零。看一下代码:

[cpp] view plain copy
  1. #include <sys/eventfd.h>  
  2. #include <unistd.h>  
  3. #include <stdlib.h>  
  4. #include <stdio.h>  
  5. #include <stdint.h>             /* Definition of uint64_t */  
  6.   
  7. #define handle_error(msg) \  
  8.    do { perror(msg); exit(EXIT_FAILURE); } while (0)  
  9.   
  10. int  
  11. main(int argc, char *argv[])  
  12. {  
  13.    uint64_t u;  
  14.       
  15.    int efd = eventfd(10, 0);  
  16.    if (efd == -1)  
  17.        handle_error("eventfd");  
  18.      
  19.    int ret = fork();  
  20.    if(ret == 0)  
  21.    {  
  22.        for (int j = 1; j < argc; j++) {  
  23.            printf("Child writing %s to efd\n", argv[j]);  
  24.            u = atoll(argv[j]);  
  25.            ssize_t s = write(efd, &u, sizeof(uint64_t));  
  26.            if (s != sizeof(uint64_t))  
  27.                handle_error("write");  
  28.        }  
  29.        printf("Child completed write loop\n");  
  30.   
  31.        exit(EXIT_SUCCESS);  
  32.    }  
  33.    else  
  34.    {  
  35.        sleep(2);  
  36.   
  37.        ssize_t s = read(efd, &u, sizeof(uint64_t));  
  38.        if (s != sizeof(uint64_t))  
  39.            handle_error("read");  
  40.        printf("Parent read %llu from efd\n",(unsigned long long)u);  
  41.        exit(EXIT_SUCCESS);  
  42.    }  
  43. }  
比较简单,不做过解释。子进程写入命令行中传入的参数,父进程读取其中计数器的值。

运行结果:

[cpp] view plain copy
  1. ./eventfd 10 20 30  
  2. Child writing 10 to efd  
  3. Child writing 20 to efd  
  4. Child writing 30 to efd  
  5. Child completed write loop  
  6. Parent read 70 from efd  
命令行传入的是10、20、30其和应为60,为啥读取的是70呢?请看15行调用eventfd时第一个参数是10,这个参数是创建eventfd时初始化计数器的值。



阅读全文
0 0
原创粉丝点击