body, table{font-family: 微软雅黑; font-size: 13.5pt}
table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;}
th{border: 1px solid gray; padding: 4px; background-color: #DDD;}
td{border: 1px solid gray; padding: 4px;}
tr:nth-child(2n){background-color: #f8f8f8;}
timerfd是Linux提供的一个定时器接口。这个接口基于文件描述符,通过文件描述符的可读事件进行超时通知,所以能够被用于select/poll/epoll的应用场景。timerfd是linux内核2.6.25版本中加入的接口。 可以实现定时器的功能,将定时器抽象为文件描述符,当定时器到期时可以对其read,这样也可以放到监听队列的主循环中。timerfd有数据可读要把它读走,不然定时器失效 |
#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); |
timerfd_create
●功能:该函数生成一个定时器对象,返回与之关联的文件描述符。 ●参数详解: -clockid: 可设置为 CLOCK_REALTIME:相对时间,从1970.1.1到目前的时间。更改系统时间会更改获取的值,它以系统时间为坐标。 CLOCK_MONOTONIC:绝对时间,获取的时间为系统重启到现在的时间,更改系统时间对其没有影响。 -flags: 可设置为 TFD_NONBLOCK(非阻塞), TFD_CLOEXEC(同O_CLOEXEC) linux内核2.6.26版本以上都指定为0 |
timerfd_settime
●功能:该函数能够启动和停止定时器 ●参数详解: -fd: timerfd对应的文件描述符 -flags: 0表示是相对定时器 TFD_TIMER_ABSTIME表示是绝对定时器 -new_value:设置超时时间,如果为0则表示停止定时器。 -old_value: 一般设为NULL, 不为NULL,则返回定时器这次设置之前的超时时间 |
操作
●read:读取缓冲区中的数据,其占据的存储空间为sizeof(uint_64),表示超时次数。 |
●select/poll/epoll:当定时器超时时,会触发定时器相对应的文件描述符上的读操作,IO复用操作会返回,然后再去对该读事件进行处理。 |
struct itimerspec { struct timespec it_interval; /* Interval for periodic timer */间隔时间 struct timespec it_value; /* Initial expiration */初始到期时间 }; |
struct timespec { time_t tv_sec; /* Seconds */ long tv_nsec; /* Nanoseconds */ }; |
eventfd的主要是用于进程或者线程间通信(如通知/等待机制的实现)。 实现了线程之间事件通知的方式,eventfd的缓冲区大小是sizeof(uint64_t);向其write可以递增这个计数器,read操作可以读取,并进行清零;eventfd也可以放到监听队列中,当计数器不是0时,有可读事件发生,可以进行读取。 |
#include <sys/eventfd.h> int eventfd(unsigned int initval, int flags); |
参数解释:
●如果是2.6.26或之前版本的内核,flags 必须设置为0。 ●initval:初始化计数器值,该值保存在内核. ●flags支持以下标志位: - EFD_NONBLOCK 类似于使用O_NONBLOCK标志设置文件描述符。 - EFD_CLOEXEC 类似open以O_CLOEXEC标志打开,O_CLOEXEC 应该表示执行exec()时,之前通过open()打开的文件描述符会自动关闭. ●返回值:函数返回一个文件描述符,与打开的其他文件一样,可以进行读写操作。 |
操作:
●read:如果计数器A的值不为0时,读取成功,获得该值。如果A的值为0,非阻塞模式时,会直接返回失败,并把error置为EINVAL;如果为阻塞模式,一直会阻塞到A为非0为止。 ●write:将缓冲区写入的8字节整形值加到内核计数器上,即会增加8字节的整数在计数器A上,如果其值达到0xfffffffffffffffe时,就会阻塞(在阻塞模式下),直到A的值被read。 write操作,写入的数据会加到计数器上,read操作读走计数器上的值后会把计数器置为0. |
#include <poll.h> int poll(struct pollfd *fds, nfds_t nfds, int timeout); fds 可以传递多个结构体,也就是说可以监测多个驱动设备所产生的事件,只要有一个产生了请求事件,就能立即返回 nfds 监测驱动文件的个数 timeout 超时时间,单位为ms -1永久等待,0立即返回 events: POLLIN 有数据可读 POLLRDNORM 有普通数据可读,等效与POLLIN POLLPRI 有紧迫数据可读 POLLOUT 写数据不会导致阻塞 POLLER 指定的文件描述符发生错误 POLLHUP 指定的文件描述符挂起事件 POLLNVAL 无效的请求,打不开指定的文件描述符 返回值: 有事件发生 返回revents域不为0的文件描述符个数(也就是说事件发生,或者错误报告) 超时 返回0; 失败 返回-1,并设置errno为错误类型 失败返回-1的错误码->EINTR 请求的事件之前产生一个信号,调用可以重新发起。 // #define EINTR 4 /* Interrupted system call */ |
struct pollfd { int fd; /* file descriptor *//* 文件描述符 */ short events; /* requested events *//* 请求的事件类型,监视驱动文件的事件掩码 */ short revents; /* returned events *//* 驱动文件实际返回的事件 */ }; poll机制就是给定一段时间,在这一段时间内程序处于睡眠状态一直等待某一个资源,它会在两种情况下返回 ①时间到了. ②等到了资源. 等待期间将进程休眠,利用事件驱动来唤醒进程,将更能提高CPU的效率。 |
#include <stdlib.h> int atoi(const char *nptr); |
long atol(const char *nptr); long long atoll(const char *nptr); |
example:
eventfd.cpp #include<iostream> #include<unistd.h> #include<stdio.h> #include<stdlib.h> #include<sys/eventfd.h> #include<sys/poll.h> #define handle_error(msg) \ do{\ perror(msg);\ exit(EXIT_FAILURE);\ }while(0) using namespace std; int main(int argc,char** argv) { uint64_t u; int efd = eventfd(10,0); // 计数器的初值为10,第二个参数写0 //最新的进程/线程通信 if(-1==efd) { handle_error("eventfd"); } int ret = fork(); if(0==ret) { for(int j=1;j<argc;++j) { u = atoll(argv[j]); cout<<"child writing "<<u<<" to efd"<<endl; ssize_t s = write(efd,&u,sizeof(uint64_t)); // 会把写入的数据累加到计数器上 if(s!=sizeof(uint64_t)) { handle_error("write"); } } cout<<"child completed write loop\n"; exit(EXIT_SUCCESS); } else { //sleep(2); // 等子进程创建好,并循环完毕 for(int i=0;i<argc-1;++i) { ssize_t s = read(efd,&u,sizeof(uint64_t)); // 读完计数器会自动清0 if(s!=sizeof(uint64_t)) { handle_error("read"); } cout<<"parent read "<<u<<" from efd\n"; } exit(EXIT_SUCCESS); } } //父进程在睡眠2s的过程中,子进程已经完成了for循环,向内核的计数器上写了1,2,3; //加上初值10,一共就是16 //child writing 1 to efd //child writing 2 to efd //child writing 3 to efd //child completed write loop //parent read 16 from efd //父进程不睡眠,for循环读走所有输入 //父进程先于子进程运行,读走了计数器的值10,计数器此时值变为0,后面父进程读操作处于阻塞状态 //等到子进程写入1的时候唤醒父进程,父进程接着读。 //$>./a.out 1 2 3 //parent read 10 from efd //child writing 1 to efd //parent read 1 from efd //child writing 2 to efd //parent read 2 from efd //$>child writing 3 to efd // 结果这样是因为父进程循环次数,第一次来就读了一次,所有最后子进程写3的时候父进程已经退出了 |
eventfd+poll.cpp #include<iostream> #include<unistd.h> #include<stdio.h> #include<stdlib.h> #include<sys/eventfd.h> #include<sys/poll.h> #include<sys/wait.h> #define handle_error(msg) do{ perror(msg); exit(EXIT_FAILURE); }while(0) using namespace std; int main(int argc,char** argv) { uint64_t u; int efd = eventfd(0,0); // 计数器的初值为0,第二个参数写0 //最新的进程/线程通信 if(-1==efd) { handle_error("eventfd"); } int ret = fork(); if(0==ret) { for(int j=1;j<argc;++j) { u = atoll(argv[j]); cout<<"child writing "<<u<<" to efd"<<endl; ssize_t s = write(efd,&u,sizeof(uint64_t)); if(s!=sizeof(uint64_t)) { handle_error("write"); } } cout<<"child completed write loop\n"; //exit(EXIT_SUCCESS); } else { struct pollfd ppfd; ppfd.fd = efd; ppfd.events = POLLIN; for(int i=1;i<argc;++i) { poll(&ppfd,1,-1); // 事件在等待ppfd可读,等待过程中父进程睡眠(休眠) // 事件发生就唤醒,接着向下执行 // 没有这个读一次就会卡住 ssize_t s = read(efd,&u,sizeof(uint64_t)); if(s!=sizeof(uint64_t)) { handle_error("read"); } cout<<"parent read "<<u<<" from efd\n"; } wait(NULL); } } //child writing 1 to efd //parent read 1 from efd //child writing 2 to efd //parent read 2 from efd //child writing 3 to efd //parent read 3 from efd //child completed write loop |
timerfd.cpp #include<iostream> #include<sys/timerfd.h> #include<unistd.h> #include<stdio.h> #include<string.h> #include<time.h> using namespace std; int main() { int timerfd = timerfd_create(CLOCK_REALTIME,0); if(-1==timerfd) { perror("timerfd_create"); return -1; } struct itimerspec new_value; memset(&new_value,0,sizeof(itimerspec)); new_value.it_value.tv_sec = 5; //初始到期时间 new_value.it_interval.tv_sec = 3; //间隔时间 int ret = timerfd_settime(timerfd,0,&new_value,NULL); if(-1==ret) { perror("timerfd_settimer"); return -1; } long int timeNum; time_t t; time(&t); // 获取当前秒数 // 读到的是个8位的整数 cout<<ctime(&t)<<endl; while(ret = read(timerfd,&timeNum,8),time(&t),cout<<ctime(&t)) // 时间到,timerfd可读,要把数据读走,不然阻塞,定时器没法正常工作了 { cout<<"timerfd read cnt "<<timeNum<<endl<<endl; } } //Fri Apr 20 11:12:22 2018 // //Fri Apr 20 11:12:27 2018 //timerfd read cnt 1 // //Fri Apr 20 11:12:30 2018 //timerfd read cnt 1 // //Fri Apr 20 11:12:33 2018 //timerfd read cnt 1 // //Fri Apr 20 11:12:36 2018 //timerfd read cnt 1 // //Fri Apr 20 11:12:39 2018 //timerfd read cnt 1 // //Fri Apr 20 11:12:42 2018 //timerfd read cnt 1 // //^C |
timerfd+poll.cpp #include<iostream> #include<time.h> #include<poll.h> #include<sys/timerfd.h> #include<string.h> #include<unistd.h> using namespace std; int main() { int timerfd = timerfd_create(CLOCK_REALTIME,0); struct itimerspec new_value; memset(&new_value,0,sizeof(new_value)); new_value.it_interval.tv_sec = 1; //时间间隔 new_value.it_value.tv_sec = 5; //初始时间 timerfd_settime(timerfd,0,&new_value,NULL); struct pollfd fd; bzero(&fd,sizeof(pollfd)); fd.fd = timerfd; fd.events = POLLIN; time_t t; time(&t); cout<<ctime(&t)<<endl; while(1) { int ret = poll(&fd,1,-1); // 永久等待时间发生 if(ret>0) { time(&t); cout<<ctime(&t)<<endl; long long int tmp; read(timerfd,&tmp,sizeof(long long int)); // 读走数据,不然会一直触发 } else { return -1; } } } //Fri Apr 20 11:29:48 2018 // //Fri Apr 20 11:29:53 2018 // //Fri Apr 20 11:29:54 2018 // //Fri Apr 20 11:29:55 2018 // //Fri Apr 20 11:29:56 2018 // //^C |
原文地址:https://www.cnblogs.com/meihao1203/p/9368398.html