使用 timerfd
创建出的定时器是基于文件描述符进行管理的,在达到超时时间时,描述符将置为可读,并可以从中读取到超时次数(启动定时器后或上次
read
之后的超时次数)。
1 2 3 4 5 6 7
| #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);
int timerfd_gettime(int fd, struct itimerspec *curr_value);
|
接口
timerfd_create--创建一个定时器文件描述符
1 2 3
| 返回值是一个新的文件描述符,失败时返回 -1
int timerfd_create(int clockid, int flags);
|
clockid
: 定时器使用的时钟
CLOCK_REALTIME
:系统的实时时钟,可被系统设置改变。
CLOCK_MONOTONIC
:单调递增的时钟,不会受系统时间调整的影响。
flags
: 控制选项
TFD_NONBLOCK
:非阻塞模式。
TFD_CLOEXEC
:文件描述符在执行 exec
时关闭。
timerfd_settime--配置定时器
1 2 3
| int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value);
|
重点看 struct itimerspec 结构体,配置定时器的核心参数:
1 2 3 4 5 6 7 8 9
| struct itimerspec { struct timespec it_interval; struct timespec it_value; };
struct timespec { time_t tv_sec; long tv_nsec; };
|
然后再对其他三个参数进行说明:
fd:前面 timerfd_create 创建成功的返回值。
old_value:可以为 NULL;如果不是 NULL 将返回上次定时器设置的
new_value
参数。
flags:0 代表相对事件;TFD_TIMER_ABSTIME 代表绝对时间。
timerfd_gettime--知晓定时器的当前状态
1
| int timerfd_gettime(int fd, struct itimerspec *curr_value);
|
获取定时器的当前剩余时间(it_value
)以及定时器的周期时间(it_interval
)。
如何把 timerfd 用起来?
前面讲 timerfd 在达到超时时间时,描述符将置为可读。那我们调用 read
接口即可。
1
| ssize_t read(int fd, void *buf, size_t count);
|
阻塞和非阻塞的处理方式
默认情况下,time_fd 是阻塞模式。如果要 设置为非阻塞模式,可以通过
timerfd_create 设置 flags 选项为 TFD_NONBLOCK。
如果是阻塞模式下调用 read,read
调用会阻塞线程,直到定时器事件发生(不发生就一直阻塞)。
如果是非阻塞模式下调用 read,read 会立即返回,如果没有事件触发,返回
-1
,并设置 errno
为 EAGAIN
。
正因为 阻塞模式下必然是在
定时器有事件发生才解除阻塞,也就保证代码执行到 read
之后必然有事件发生:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| while ( 1 ) { int nfds = epoll_wait( epfd, events, 10, -1 ); for ( int i = 0; i < nfds; i++ ) { if ( events[i].data.fd == tfd ) { uint64_t expirations; read( tfd, &expirations, sizeof(expirations) ); printf( "Timer expired %llu times\n", (unsigned long long) expirations ); } } }
|
而非阻塞模式并不能保证代码往下执行是因为确保有事件发生,我们需要有判断逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| while ( 1 ) { ssize_t s = read( tfd, &expirations, sizeof(expirations) ); if ( s == -1 ) { if ( errno == EAGAIN ) { printf( "No timer event yet, doing other work...\n" ); continue; } else { perror( "read" ); return(-1); } } printf( "Timer expired %llu times\n", (unsigned long long) expirations ); break; }
|