#include <sys/timerfd.h> /* timerfd_create 调用成功,返回一个文件描述符; 调用失败,返回-1, errno被设置为具体的错误码 */ int timerfd_create(int clockid, int flags); /* timerfd_settime 和 timerfd_gettime 调用成功,返回0; 调用失败,返回-1,errno被设置为具体的错误码 */ 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
| 参数值 | 描述 |
|---|---|
| CLOCK_REALTIME | |
| CLOCK_MONOTONIC | |
| CLOCK_BOOTTIME (Since Linux 3.15) | |
| CLOCK_REALTIME_ALARM (since Linux 3.11) | |
| CLOCK_BOOTTIME_ALARM (since Linux 3.11) |
timerfd_settime
struct timespec {
time_t tv_sec; /* Seconds */
long tv_nsec; /* Nanoseconds */
};
struct itimerspec {
struct timespec it_interval; /* 定时器的周期间隔 */
struct timespec it_value; /* 初始过期时间 */
};
对于参数
默认情况下(flags 设置为0),定时器使用相对时间计时,即从调用
需要注意,可以多次调用 timerfd_settime,若上次调用 timerfd_settime 启动的定时器还为超时,则这次调用的 timerfd_settime 会覆盖掉上次还没超时的定时器,即上次设置的定时器会失效。
下面看两个例子:
示例一:设置一个定时器,死等定时器超时(通过read进行阻塞读)
#include <iostream>
#include <sys/timerfd.h>
#include <cstring>
#include <unistd.h>
int main()
{
struct timespec curr_time;
bzero(&curr_time, sizeof(curr_time));
// timerfd 为阻塞的
int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
// 获取调用 timerfd_create 时的时间
clock_gettime(CLOCK_MONOTONIC, &curr_time);
std::cout << "timerfd_create time: " << "sec-" << curr_time.tv_sec
<< " nano-" << curr_time.tv_nsec << std::endl;
sleep(3);
struct itimerspec new_val;
bzero(&new_val, sizeof(new_val));
new_val.it_value.tv_sec = 5; // 5s后定时器超时
timerfd_settime(timerfd, 0, &new_val, nullptr);
// 获取调用 timerfd_settime 时的时间
bzero(&curr_time, sizeof(curr_time));
clock_gettime(CLOCK_MONOTONIC, &curr_time);
std::cout << "timerfd_settime time: " << "sec-" << curr_time.tv_sec
<< " nano-" << curr_time.tv_nsec << std::endl;
uint64_t howmany;
// 阻塞读
ssize_t n = read(timerfd, &howmany, sizeof(howmany));
// 获取定时器超时时刻
bzero(&curr_time, sizeof(curr_time));
clock_gettime(CLOCK_MONOTONIC, &curr_time);
std::cout << "timerfd timeout time: " << "sec-" << curr_time.tv_sec
<< " nano-" << curr_time.tv_nsec << std::endl;
}
/*
运行结果:
timerfd_create time: sec-101418 nano-833894291
timerfd_settime time: sec-101421 nano-834455725
timerfd timeout time: sec-101426 nano-834540240
*/
示例二:设置一个周期定时器
// 省略相关头文件
int main()
{
struct itimerspec expire_time;
bzero(&expire_time, sizeof(expire_time));
int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
int epollfd = epoll_create1(EPOLL_CLOEXEC);
struct epoll_event events[5];
bzero(events, sizeof(events));
struct epoll_event timer_event;
bzero(&timer_event, sizeof(timer_event));
timer_event.events = EPOLLIN;
timer_event.data.fd = timerfd;
expire_time.it_value.tv_sec = 5;
// 5s 的定时周期
expire_time.it_interval.tv_sec = 5;
if (timerfd_settime(timerfd, 0, &expire_time, nullptr) < 0) {
error_message("timerfd_create error");
}
epoll_ctl(epollfd, EPOLL_CTL_ADD, timerfd, &timer_event);
struct timespec curr_time;
while (true) {
int num = epoll_wait(epollfd, events, 5, -1);
bzero(&curr_time, sizeof(curr_time));
clock_gettime(CLOCK_MONOTONIC, &curr_time);
std::cout << "curr time: sec-" << curr_time.tv_sec << ", nano-" << curr_time.tv_nsec << std::endl;
for (int i = 0; i < num; ++i) {
int fd = events[i].data.fd;
if (fd == timerfd) {
std::cout << "timer timeout once." << std::endl;
uint64_t howmany;
ssize_t n = read(timerfd, &howmany, sizeof(howmany));
std::cout << "howmany: " << howmany << std::endl;
}
}
}
}
/*
运行输出:
curr time: sec-103737, nano-656002232
timer timeout once.
howmany: 1
curr time: sec-103742, nano-655951279
timer timeout once.
howmany: 1
curr time: sec-103747, nano-655957241
timer timeout once.
howmany: 1
curr time: sec-103752, nano-655909670
timer timeout once.
howmany: 1
...
*/
若要使用绝对时间设置超时时间,可以设置第二个参数
flags 参数可以由下列值进行或运算设置,若使用默认行为,则设置为0。
TFD_TIMER_ABSTIME 若被设置,则当 new_value.it_value 所指定的时刻到达时,定时器超时。TFD_TIMER_CANCEL_ON_SET 若被设置。If this flag is specified along with TFD_TIMER_ABSTIME and the clock for this timer is CLOCK_REALTIME or CLOCK_REALTIME_ALARM, then mark this timer as cancelable if the real-time clock undergoes a discontinuous change (settimeofday(2), clock_settime(2), or similar). When such changes occur, a current or future read(2) from the file descriptor will fail with the error ECANCELED.
下面给出一个使用绝对时间设置定时器超时的例子:
int main()
{
int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
struct itimerspec new_val;
struct timespec curr_time;
bzero(&new_val, sizeof(new_val));
bzero(&curr_time, sizeof(curr_time));
clock_gettime(CLOCK_MONOTONIC, &curr_time);
std::cout << "curr_time: " << curr_time.tv_sec
<< " seconds, " << curr_time.tv_nsec << "nanoseconds" << std::endl;
// 使用绝对时间设置定时器的超时时间
new_val.it_value.tv_sec = curr_time.tv_sec + 5;
timerfd_settime(timerfd, TFD_TIMER_ABSTIME, &new_val, nullptr);
uint64_t howmany;
ssize_t n = read(timerfd, &howmany, sizeof(howmany));
bzero(&curr_time, sizeof(curr_time));
clock_gettime(CLOCK_MONOTONIC, &curr_time);
std::cout << "curr_time: " << curr_time.tv_sec
<< " seconds, " << curr_time.tv_nsec << "nanoseconds" << std::endl;
close(timerfd);
}
timerfd_gettime
int main()
{
int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
struct itimerspec new_val, old_val;
bzero(&new_val, sizeof(new_val));
bzero(&old_val, sizeof(old_val));
clock_gettime(CLOCK_MONOTONIC, &old_val.it_value);
// 调用 timerfd_settiem 时的时间
std::cout << "sec: " << old_val.it_value.tv_sec << ", nano: " << old_val.it_value.tv_nsec << std::endl;
bzero(&old_val, sizeof(old_val));
new_val.it_value.tv_sec = 10;
timerfd_settime(timerfd, 0, &new_val, &old_val);
// 因为 timerfd_settime 在此处是第一次调用,因此 old_val 的值为0
std::cout << "sec: " << old_val.it_value.tv_sec << ", nano: " << old_val.it_value.tv_nsec << std::endl;
sleep(3);
bzero(&old_val, sizeof(old_val));
clock_gettime(CLOCK_MONOTONIC, &old_val.it_value);
// sleep 3s 后,获取当前时刻的时间
std::cout << "sec: " << old_val.it_value.tv_sec << ", nano: " << old_val.it_value.tv_nsec << std::endl;
bzero(&old_val, sizeof(old_val));
timerfd_settime(timerfd, 0, &new_val, &old_val);
// 第二次调用 timerfd_settime,
// 因为上一次调用 timerfd_settime 设置的定时器还未超时,因此上次的定时器失效
// 距离上次设置的定时器超时还剩的时间间隔会被设置到 old_val 中
std::cout << "sec: " << old_val.it_value.tv_sec << ", nano: " << old_val.it_value.tv_nsec << std::endl;
uint64_t howmany;
// 阻塞读
ssize_t n = read(timerfd, &howmany, sizeof(howmany));
// 定时器超时时刻
bzero(&old_val, sizeof(old_val));
clock_gettime(CLOCK_MONOTONIC, &old_val.it_value);
std::cout << "sec: " << old_val.it_value.tv_sec << ", nano: " << old_val.it_value.tv_nsec << std::endl;
}
// 观察输出结果:
/*
sec: 105111, nano: 956075955
sec: 0, nano: 0
sec: 105114, nano: 956573191
sec: 6, nano: 999455855
sec: 105124, nano: 956734201
*/