Linux下用于获取当前时间的函数有
- time(2) / time_t (秒)
- ftime(3) / struct timeb (毫秒)
- gettimeofday(2) / struct timeval (微秒)
- clock_gettime(2) / struct timespec (纳秒)
定时函数,用于让程序等待一段时间或安排计划任务
- sleep(3)
- alarm(2)
- usleep(3)
- nanosleep(2)
- clock_nanosleep(2)
- getitimer(2) / setitimer(2)
- timer_create(2) / timer_settime(2) / timer_gettime(2) / timer_delete(2)
- timerfd_create(2) / timerfd_gettime(2) / timerfd_settime(2)
muduo的取舍是
- (计时)只使用gettimeofday(2)来获取当前时间
- (定时)只使用timerfd_*系列函数来处理定时任务
gettimeofday入选的原因
- time的精度太低,ftime已被废弃,clock_gettime精度最高,但是系统调用开销比gettimeofday大
- 在x86-64平台上,gettimeofday不是系统调用,而是在用户态完成的,没有上下文切换和陷入内核的开销
- gettimeofday的分辨率在微秒级,足以满足日常计时的需要
timerfd*_入选的原因
- sleep / alarm / usleep在实现时有可能使用了SIGALRM信号,多线程程序中尽量避免使用信号,因为处理起来比较麻烦(信号通知进程,所有线程都将接收到这个信号,谁处理好)。另外,如果网络库定义了信号处理函数,用户代码(main函数等使用库的程序)也定义了信号处理函数,这不就冲突了,该调用哪个好
- nanosleep / clock_nanosleep是线程安全的,但是会让当前线程挂起等待一段时间,这会导致线程失去响应
- gettimer和timer_create也是用信号来传递超时信息
- timerfd_create把时间变成了一个文件描述符,该描述符在定时器超时的那一刻变为可读,可以很方便的融入到select/poll/epoll中,统一事件源
select/poll/epoll可以设置timeout来实现超时,但是poll/epoll的精度只有毫秒,远低于timerfd_settime
---------------------------------------摘自《Linux多线程服务器编程》
gettimeofday
#include <sys/time.h>
int gettimeofday(struct timeval* tv, struct timezone* tz);
/*
* 返回后将目前的时间存放在tv中,把时区信息存放在tz中
* tz和tz都可以为NULL
* 执行成功返回0,失败返回-1
*/
struct timeval{
long tv_sec; /* 秒 */
long tv_usec; /* 微秒 */
};
struct timeval timer;
gettimeofday(&timer, NULL);
timerfd_*系列函数
#include <sys/timerfd.h>
int timerfd_create(int clockid, int flags);
/*
* 创建一个用于定时器的文件描述符
* clockid可以为
* CLOCK_REALTIME(系统实时时间,可能会被用户手动更改)
* CLOCK_MONOTONIC(从系统启动那一刻开始计时的时间,无法被用户更改)
* flags可以为
* TFD_NONBLOCK(非阻塞)
* TFD_CLOEXEC(调用exec时自动close)
*/
int timerfd_settime(int fd, int flag,
const struct itimerspec* new_value,
const struct itimerspec* old_value);
/*
* 用于设置timerfd的超时时间
* fd, 通过timerfd_create返回的文件描述符
* flag, 0代表相对时间,TFD_TIMER_ABSTIME代表绝对时间
* new_value, 新设置的超时时间
* old_value, 以前设置的超时时间,值-结果参数
*
* struct timespec{
* time_t tv_sec;
* time_t tv_nsec;
* };
*
* struct itimerspec{
* struct timespec it_interval;
* struct timespce it_value;
* };
*
* it_value表示首次超时时间
* it_interval表示后续周期性超时时间
* it_interval不为0表示是周期性超时任务
* it_interval和it_value同时为0表示取消定时任务
*/
int timerfd_gettime(int fd, struct itimerspec *cur_value);
/*
* 返回距离下次超时还剩多长时间,保存在cur_value中
* 如果调用时定时器已经到期,同时定时器设置了周期性任务(it_interval不为0)
* 那么调用此函数之后定时器重新开始计时,超时时间是it_interval的值
*/
Timestamp
类用于保存超时时间,类中只有一个成员变量,保存当前UTC时间,即从Unix Epoch(1970-01-01 00:00:00)到现在的微秒数
Timer
类是一个超时任务,保存超时时间Timestamp
,回调函数,以及记录自己是否是周期性计时任务,回