timerfd API 是Linux系统特有的,Linux是在内核版本2.6.25中提供的。它可以从文件描述符中读取所创建定时器的到期通知。因为可以使用select()、poll()和epoll()将这种文件描述符同其他描述符一同进行监控。
这组API中的3个系统调用,其操作与timer_create()、timer_settime()和timer_gettime()相类似。
新加入的第1个系统调用时timerfd_create(),它会创建一个新的定时器对象,并返回一个指代该对象的文件描述符。
#include <sys/timerfd.h> int timerfd_create(int clockid, int flags); 返回:若成功返回对应的描述符,若失败返回-1参数clockid的值可以设置为CLOCK_REALTIME或CLOCK_MONOTONIC。参考下表。时钟类型 时 钟 I D描 述CLOCK_REALTIME可设定的系统级实时时钟CLOCK_MONOTONIC不可设定的恒定态时钟
timerfd_create()最初实现将参数flags预留供未来使用,必须设置为0.不过,Linux内核从2.6.27开始支持下面两种flags标志。
TFD_CLOEXEC::为新的文件描述符设置运行时关闭标志(FD_CLOEXEC)。TFD_NONBLOCK:为底层的打开文件描述符设置O_NONBLOCK标志,随后的读操作将是非阻塞式地。 timerfd_create()创建的定时器使用完毕后,应调用close()关闭相应的文件描述符,以便内核能够释放与定时器相关的资源。 系统调用timerfd_settime()可以配备(启动)或解除(停止)由文件描述符fd所指代的定时器。 #include <sys/timerfd.h> int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspc *old_value); 返回:若成功返回0,若失败返回-1参数new_value为定时器指定新设置。参数old_value可以用来返回定时器的前一设置。如果不关心定时器的前一设置,可将old_value置为NULL。两个参数均指向itimerspec结构。 参数flags可以是0,此时将new_value.it_value的值视为相对于调用timerfd_settime()时间点的相对时间,也可以设为TFD_TIMER_ABSTIME,将其视为一个绝对时间。 系统调用timerfd_gettime()返回文件描述符fd所标识定时器的间隔及剩余时间。 其中,itimerspec结构如下所示: struct itimerspec { struct timespec it_interval; /* Interval for periodic timer */ struct timespec it_value; /* First expiration */ }; struct timerspec { time_t tv_sec; /* Seconds */ long tv_nsec; /* Nanoseconds */ };it_value指定了定时器首次到期的时间。如果it_interval的任一子字段非0,那么就是一个周期性定时器。在经历了由it_value指定的 初次到期后,会按这些子字段指定的频率周期性到期。 #include <sys/timerfd.h> int timerfd_gettime(int fd, struct itimerspec *curr_value); 返回:若成功返回0,若失败返回-1间隔以及距离下次到期的时间均返回curr_value指向的结构itimerspec中。即使是以TFD_TIMER_ABSTIME标志创建的绝对时间定时器,curr_value.it_vale字段中返回值的意义也会保持不变。如果返回的结构curr_value.it_value中所有字段值均为0,那么该定时器已经被解除。如果返回的结构curr_value.it_interval中的两字段值均为0,那么定时器只会到期一次,到期时间在curr_value.it_value中给出。 注意:调用fork()期间,子进程会继承timerfd_create()所创建文件描述符的拷贝。 一旦timerfd_settime()启动了定时器,就可以从相应文件描述符中调用read()来读取定时器的到期信息,出于这一目的,传给read()的缓冲区必须足以容纳一个无符号8字节整型(uint64_t)数。 在上次使用timerfd_settime()修改设置之后,或是最后一次执行read()后,如果发生了一起到多起定时器到期事件,那么read()会立即返回,且返回的缓冲区中包含了已发送的到期次数。如果并无定时器到期,read()会一直阻塞直至产生下一个到期。也可以执行fcntl()的F_SETFL操作为文件描述符设置O_NONBLOCK标志,这时的读动作的非阻塞的,且如果没有定时器到期,则返回错误,并将errno值置为EAGAIN。 如前所述,可以利用select()、pool()和epoll()对timerfd文件描述符进行监控。如果定时器到期,会将对应的文件描述符标记为可读。 下面给一个定时5s的例子,来看看这些API的使用: #include <stdio.h> #include <poll.h> #include <unistd.h> #include <stdint.h> #include <sys/timerfd.h> #ifndef INFTIM #define INFTIM -1 #endif //#ifndef INFTIM int main(void) { int iFd; iFd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); if (iFd < 0) { printf("timerfd_create error\n"); return -1; } struct itimerspec new_value; new_value.it_interval.tv_sec = 5; new_value.it_interval.tv_nsec = 0; new_value.it_value.tv_sec = 5; new_value.it_value.tv_nsec = 0; if (timerfd_settime(iFd, 0, &new_value, NULL) != 0) { printf("timerfd_settime error\n"); close(iFd); return -1; } struct pollfd fdPoll; fdPoll.fd = iFd; fdPoll.events = POLLIN; while (1) { if (poll(&fdPoll, 1, INFTIM) < 0) { printf("poll error\n"); break; } if (fdPoll.revents & POLLIN) { uint64_t howmany; if (read(iFd, &howmany, sizeof(howmany)) != sizeof(howmany)) { printf("read error\n"); break; } printf("Now, Timer 5s\n"); } } close(iFd); return 0; }