使用timerfd的好处是可以结合epoll或poll或select使用,当超时时间到时epoll等会返回可读。
1.
int timerfd_create(int clockid,int flags)
timerfd_create返回一个文件描述符,该文件描述符和定时器所关联。
clockid:
clockid可以为CLOCK_REALTIME或者CLOCK_MONOTONIC。最好使用CLOCK_MONOTONIC,这样的话即使你改变了系统时钟的话也不会受到影响,但是CLOCK_REALTIME会受到硬性。如果对这两个选项感兴趣的话可以自行去网上查写关于他们的资料。
flags:
flags可以是TFD_NONBLOCK、TFD_CLOEXEC的或。设置TFD_NONBLOCK代表把返回的文件描述符设置为非阻塞模式,这个和用fcntl设置该文件描述符的O_NONBLOCK的效果一样。设置TFP_CLOEXEC代表当执行exec时该文件描述符会自动关闭。
2.
int timerfd_settime(int fd,int flags,const struct itimerspec *new_value,struct itimerspec *old_value)
timerfd_settime可以启动或者停止定时器。
让我们先看看两个结构体:
struct timespec{
time_t tv_sec;//秒数
long tv_nsec;//纳秒数
}
struct itimerspec{
struct timespec it_interval;
struct timespec it_value;
}
参数说明:
new_value.it_value:设置初始定时器时间。只要new_value.it_value中的tv_sec和tv_nsec有一个不为0,则启动定时器。把new_value.it_value中的tv_sec和tv_nsec都设为0,则停止定时器。(视flags而定,如果flags是0那么new_value.it_value就是相对值,如果flags是TFD_TIMER_ABSTIME,那么new_value.it_value就是绝对时间)
new_value.it_interval:设置定时时间间隔(定时器可以一直定时)。只要new_value.it_interval中的tv_sec和tv_nsec有一个不为0,那么就会在第一次超时之后把定时时间设为这个值。如果把tv_sec和tv_nsec都设为0,那么定时器只会定时一次,不会重复定时。(new_value.it_interval总是设置为相对时间)
flags:flags参数为0,表示启动相对计时器(new_value.it_value指定相对定时时间), TFD_TIMER_ABSTIME用于启动绝对定时器(当时钟的值达到new_value中指定的值时,定时器将到期)。
old_value.it_value:表示上一次设置的定时器还有多久就到时间了(返回相对时间,不管flags设置成什么)
old_value.it_interval:返回上一次设置的值(总是返回相对时间)
3.
int timerfd_gettime(int fd,struct itimerspec *curr_value);
参数:
curr_value:返回还有多久定时器要到期,以及当前设置的循环定时周期。如果it_value中的中的tv_sec和tv_nsec都为0,那么表示定时器已经停止了。curr_value->it_interval返回当前甚至的循环定时周期,如果it_value中的中的tv_sec和tv_nsec都为0,表示该定时器只定时一次,不会循环定时(it_value和it_interval返回的总是相对值)
4.read的返回值
read(timerfd,buf,sizeof(buf)); buf应该是uint64_t。如果有超时时间发生,则从buf返回超时的次数。
1.如果没有设定O_NONBLOCK,则read阻塞,知道下一次超时时间到来。
2.如果设定了O_NONBLOCK,且没有超时时间发生,则置error为EAGAIN。
3.如果buf的大小小于8个字节,那么read置error为EINVAL。
下面附上一段测试程序:第一个参数为第一次超时什么时候发生,第二个参数为超时间隔设为多少,第三个参数为你想要发生多少次超时时间。程序可以在man timerfd_create的最后面找到。
#include <sys/timerfd.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)
static void print_elapsed_time(void)
{
static struct timespec start;
struct timespec curr;
static int first_call = 1;
int secs, nsecs;
if (first_call) {
first_call = 0;
if (clock_gettime(CLOCK_MONOTONIC, &start) == -1)
handle_error("clock_gettime");
}
if (clock_gettime(CLOCK_MONOTONIC, &curr) == -1)
handle_error("clock_gettime");
secs = curr.tv_sec - start.tv_sec;
nsecs = curr.tv_nsec - start.tv_nsec;
if (nsecs < 0) {
secs--;
nsecs += 1000000000;
}
printf("%d.%03d: ", secs, (nsecs + 500000) / 1000000);
}
int main(int argc, char *argv[])
{
struct itimerspec new_value;
int max_exp, fd;
struct timespec now;
uint64_t exp, tot_exp;
ssize_t s;
if ((argc != 2) && (argc != 4)) {
fprintf(stderr, "%s init-secs [interval-secs max-exp]\n",
argv[0]);
exit(EXIT_FAILURE);
}
if (clock_gettime(CLOCK_REALTIME, &now) == -1)
handle_error("clock_gettime");
/* Create a CLOCK_REALTIME absolute timer with initial
expiration and interval as specified in command line */
new_value.it_value.tv_sec = now.tv_sec + atoi(argv[1]);
new_value.it_value.tv_nsec = now.tv_nsec;
if (argc == 2) {
new_value.it_interval.tv_sec = 0;
max_exp = 1;
} else {
new_value.it_interval.tv_sec = atoi(argv[2]);
max_exp = atoi(argv[3]);
}
new_value.it_interval.tv_nsec = 20;
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");
struct itimerspec old_value;
if (timerfd_settime(fd, TFD_TIMER_ABSTIME, &new_value, &old_value) == -1)
handle_error("timerfd_settime");
print_elapsed_time();
printf("timer started\n");
for (tot_exp = 0; tot_exp < max_exp;) {
s = read(fd, &exp, sizeof(uint64_t));
if (s != sizeof(uint64_t))
handle_error("read");
tot_exp += exp;
print_elapsed_time();
printf("read: %llu; total=%llu\n",
(unsigned long long) exp,
(unsigned long long) tot_exp);
}
exit(EXIT_SUCCESS);
}