Linux内核于内核2.6之后提供了一种创建定时器的方法,那就是Linux特有的timerfd文件描述符(一切皆文件),这也意味着可以用多路复用来监听并读取。该接口共有3个API。
-
timerfd_create
#include <sys/timerfd.h> int timerfd_create(int clockid, int flags); 成功返回一个指代该对象的文件描述符, 失败返 回-1及errno
- 第一个cokckid 可以设置为:
CLOCK_REALTIME
和CLOCK_MONOTONIC
。-
CLOCK_REALTIME
表示可设定的系统级别时钟,更改系统时间会更改获取的值。也就是说,它以系统时间为坐标。 -
CLOCK_MONOTONIC
不可设定的恒定态时钟,更改系统时间对齐没有影响。
-
- 第二个参数flags, 支持
TFD_CLOEXEC
和TFD_NONBLOCK
和0
-
TFD_CLOEXEC
为新的文件描述符设置运行时关闭标志。(待补充) -
TFD_NONBLOCK
为底层的打开文件描述符设置O_NONBLOCK
标志, 随后的读操作将是非阻塞的,
-
- 第一个cokckid 可以设置为:
-
timerfd_settime
#include <sys/timerfd.h> int timerfd_settime(int fd, int flags, const struct itimerspec* new_value, struct itimerspec* old_value); 成功返回0, 失败返回-1和 errno
它的作用是启动或者停止由文件描述符fd指定的定时器。第二个参数和第三个参数都是
struct timespec
类型。struct itimerspec { struct timespec it_interval; //间隔时间 struct timespec it_value; //第一次到期时间 }; struct timespec { time_t tv_sec; //秒 long tv_nsec; //纳秒 };
-
new_value
为定时器指定新设置新的超时时间,若newValue.it_value
非 0 则启动定时器,否则关闭定时器。若newValue.it_interval
为 0 则定时器只定时一次,否则之后每隔设定时间超时一次。 -
old_value
用来返回定时器的前一设置, 如果不关心, 可将其设置为 NULL,不为 NULL 时则返回定时器这次设置之前的超时时间。
-
-
timerfd_gettime
#include <sys/timerfd.h> int timerfd_gettime(int fd, struct itimerspec *curr_value); 成功返回0, 失败返回-1和errno
返回文件描述符 fd 所标识定时器的间隔及剩余时间。
间隔和距离下次到期的时间均返回到 curr_value 指向的结构体。
如果返回的结构中 curr_value.it_value 中所有字段值均为0, 那么该定时器已经解除, 如果返回的结构 curr_value.it_interval 中两字段值均为0, 那么该定时器只会到期一次, 到期时间在 curr_value.it_value 中给出
配合使用epoll监听定时器
#include <time.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <assert.h>
using namespace std;
const int MAXNUM = 20;
int main(int argc, char *argv[])
{
struct itimerspec new_value;
struct timespec now;
uint64_t exp;
ssize_t s;
int ret = clock_gettime(CLOCK_REALTIME, &now);//获取时钟时间
assert(ret != -1);
new_value.it_value.tv_sec = 5; //第一次到期的时间
new_value.it_value.tv_nsec = now.tv_nsec;
new_value.it_interval.tv_sec = 1; //之后每次到期的时间间隔
new_value.it_interval.tv_nsec = 0;
int timefd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK); // 构建了一个定时器
assert(timefd != -1);
ret = timerfd_settime(timefd, 0, &new_value, NULL);//启动定时器
assert(ret != -1);
cout << "timer started" << endl; // 定时器开启啦!
int epollfd = epoll_create(1); //创建epoll实例对象
struct epoll_event ev;
struct epoll_event events[MAXNUM];
ev.data.fd = timefd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epollfd, EPOLL_CTL_ADD, timefd, &ev); //添加到epoll事件集合
for (; ;)
{
int num = epoll_wait(epollfd, events, MAXNUM, 0);
assert(num >= 0);
for (int i = 0; i < num; i++)
{
if (events[i].events & EPOLLIN)
{
//....处理其他事件
if (events[i].data.fd == timefd)
{
s = read(events[i].data.fd, &exp, sizeof(uint64_t)); //需要读出uint64_t大小, 不然会发生错误
assert(s == sizeof(uint64_t));
cout << "here is timer" << endl;
}
}
}
}
close(timefd);
close(epollfd);
return 0;
}
参考资料
1.https://blog.csdn.net/weixin_36888577/article/details/81570793
网友评论