原文地址:http://blog.csdn.net/gdutliuyun827/article/details/8460417
三种新的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:
- #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
这是三种新的fd的基本概念,接下来的几篇会通过例子程序来说明各种fd的用法。
上一篇介绍了三种fd的概念,今天看一下signalfd的例子程序,直接上代码,稍后做一点说明。
- #include <sys/signalfd.h>
- #include <signal.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #define handle_error(msg) \
- do { perror(msg); exit(EXIT_FAILURE); } while (0)
- int main(int argc, char *argv[])
- {
- sigset_t mask;
- int sfd;
- struct signalfd_siginfo fdsi;
- ssize_t s;
- sigemptyset(&mask);
- sigaddset(&mask, SIGINT);
- sigaddset(&mask, SIGQUIT);
- if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
- handle_error("sigprocmask");
- sfd = signalfd(-1, &mask, 0);
- if (sfd == -1)
- handle_error("signalfd");
- for (;;) {
- s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo));
- if (s != sizeof(struct signalfd_siginfo))
- 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);
- } else {
- printf("Read unexpected signal\n");
- }
- }
- }
L24:调用signalfd,把信号集与fd关联起来,第一个参数为-1表示新建一个signalfd,不是-1并且是一个合法的signalfd表示向其添加新的信号。
L29:阻塞等待信号的发生并读取。根据读取的结果可以知道发生了什么信号。
- struct signalfd_siginfo的结构
- struct signalfd_siginfo {
- uint32_t ssi_signo; /* Signal number */
- int32_t ssi_errno; /* Error number (unused) */
- int32_t ssi_code; /* Signal code */
- uint32_t ssi_pid; /* PID of sender */
- uint32_t ssi_uid; /* Real UID of sender */
- int32_t ssi_fd; /* File descriptor (SIGIO) */
- uint32_t ssi_tid; /* Kernel timer ID (POSIX timers)
- uint32_t ssi_band; /* Band event (SIGIO) */
- uint32_t ssi_overrun; /* POSIX timer overrun count */
- uint32_t ssi_trapno; /* Trap number that caused signal */
- int32_t ssi_status; /* Exit status or signal (SIGCHLD) */
- int32_t ssi_int; /* Integer sent by sigqueue(3) */
- uint64_t ssi_ptr; /* Pointer sent by sigqueue(3) */
- uint64_t ssi_utime; /* User CPU time consumed (SIGCHLD) */
- uint64_t ssi_stime; /* System CPU time consumed (SIGCHLD) */
- uint64_t ssi_addr; /* Address that generated signal
- (for hardware-generated signals) */
- uint8_t pad[X]; /* Pad size to 128 bytes (allow for
- additional fields in the future) */
- };
详细信息可以在linux下面:man signalfd,例子程序就是摘自man。
看一下timerfd的例子,上代码:
- #include <sys/timerfd.h>
- #include <sys/time.h>
- #include <time.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <stdint.h> /* Definition of uint64_t */
- #define handle_error(msg) \
- do { perror(msg); exit(EXIT_FAILURE); } while (0)
- void printTime()
- {
- struct timeval tv;
- gettimeofday(&tv, NULL);
- printf("printTime: current time:%ld.%ld ", tv.tv_sec, tv.tv_usec);
- }
- int main(int argc, char *argv[])
- {
- struct timespec now;
- if (clock_gettime(CLOCK_REALTIME, &now) == -1)
- handle_error("clock_gettime");
- struct itimerspec new_value;
- new_value.it_value.tv_sec = now.tv_sec + atoi(argv[1]);
- new_value.it_value.tv_nsec = now.tv_nsec;
- new_value.it_interval.tv_sec = atoi(argv[2]);
- new_value.it_interval.tv_nsec = 0;
- int fd = timerfd_create(CLOCK_REALTIME, 0);
- if (fd == -1)
- handle_error("timerfd_create");
- if (timerfd_settime(fd, TFD_TIMER_ABSTIME, &new_value, NULL) == -1)
- handle_error("timerfd_settime");
- printTime();
- printf("timer started\n");
- for (uint64_t tot_exp = 0; tot_exp < atoi(argv[3]);)
- {
- uint64_t exp;
- ssize_t s = read(fd, &exp, sizeof(uint64_t));
- if (s != sizeof(uint64_t))
- handle_error("read");
- tot_exp += exp;
- printTime();
- printf("read: %llu; total=%llu\n",exp, tot_exp);
- }
- exit(EXIT_SUCCESS);
- }
相关结构体如下:
- 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 */
- };
代码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
eventfd
好久没更新了,今天看一下第三种新的fd:eventfd类似于管道的概念,可以实现线程间的事件通知,所不同的是eventfd的缓冲区大小是sizeof(uint64_t)也就是8字节,它是一个64位的计数器,写入递增计数器,读取将得到计数器的值,并且清零。看一下代码:
- #include <sys/eventfd.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <stdint.h> /* Definition of uint64_t */
- #define handle_error(msg) \
- do { perror(msg); exit(EXIT_FAILURE); } while (0)
- int
- main(int argc, char *argv[])
- {
- uint64_t u;
- int efd = eventfd(10, 0);
- if (efd == -1)
- handle_error("eventfd");
- int ret = fork();
- if(ret == 0)
- {
- for (int j = 1; j < argc; j++) {
- printf("Child writing %s to efd\n", argv[j]);
- u = atoll(argv[j]);
- ssize_t s = write(efd, &u, sizeof(uint64_t));
- if (s != sizeof(uint64_t))
- handle_error("write");
- }
- printf("Child completed write loop\n");
- exit(EXIT_SUCCESS);
- }
- else
- {
- sleep(2);
- ssize_t s = read(efd, &u, sizeof(uint64_t));
- if (s != sizeof(uint64_t))
- handle_error("read");
- printf("Parent read %llu from efd\n",(unsigned long long)u);
- exit(EXIT_SUCCESS);
- }
- }
运行结果:
- ./eventfd 10 20 30
- Child writing 10 to efd
- Child writing 20 to efd
- Child writing 30 to efd
- Child completed write loop
- Parent read 70 from efd