#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 */