本文源码基于muduo v2.0.2分析
类图
通读源码之前的几点说明
class Timestamp : public muduo::copyable,
public boost::equality_comparable<Timestamp>,
public boost::less_than_comparable<Timestamp>
Timestamp继承的3个基类,下面将分别介绍
copyable & nocopyable
值语义:可以拷贝,拷贝之后与原对象脱离关系
对象语义:要么不能拷贝,要么可以拷贝,拷贝之后与原对象仍然有联系,比如共享底层资源(要实现自己的拷贝构造函数)
class copyable
{
protected:
copyable() = default;
~copyable() = default;
};
class noncopyable
{
public:
noncopyable(const noncopyable&) = delete;
void operator=(const noncopyable&) = delete;
protected:
noncopyable() = default;
~noncopyable() = default;
};
equality_comparable & less_than_comparable
头文件boost/operators.hpp
要派生自 boost::less_than_comparable, 派生类(T)必须提供:
bool operator<(const T&, const T&);
less_than_comparable 将依照 operator< 实现其余的三个操作符
bool operator>(const T&,const T&);
bool operator<=(const T&,const T&);
bool operator>=(const T&,const T&);
同理,要派生自 boost::equality_comparable, 派生类(T)必须提供:
bool operator==(const T&,const T&);
将自动完成bool operator!=(const T&,const T&);
静态断言static_assert
static_assert(sizeof(Timestamp) == sizeof(int64_t),
"Timestamp is same size as int64_t");
编译时断言,条件为true不打印信息,false时打印
多平台PRid64占位
snprintf(buf, sizeof(buf)-1, "%" PRId64 ".%06" PRId64 "", seconds, microseconds);
64位类型的printf占位符是ld,而32位是lld。为适应32位和64位的系统,使用了PRId64
PRId64位于头文件inttypes.h
# if __WORDSIZE == 64
# define __PRI64_PREFIX "l"
# else
# define __PRI64_PREFIX "ll"
# endif
# define PRId64 __PRI64_PREFIX "d"
同时,inttypes头文件可能需要定义宏__STDC_FORMAT_MACROS后才能include,具体根据编译器
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#undef __STDC_FORMAT_MACROS
#endif
通读源码
Timestamp整体实现比较简单。从静态函数now()开始看起,其余函数围绕now得到的时间,进行格式化输出等操作。
Timestamp Timestamp::now()
{
struct timeval tv;
gettimeofday(&tv, NULL);
int64_t seconds = tv.tv_sec;
return Timestamp(seconds * kMicroSecondsPerSecond + tv.tv_usec);
}
gettimeofday原型
#include <sys/time.h>
int gettimeofday(struct timeval *restrict tv,
struct timezone *restrict tz);
int settimeofday(const struct timeval *tv,
const struct timezone *tz);
/*
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
struct timezone {
int tz_minuteswest; /* minutes west of Greenwich */
int tz_dsttime; /* type of DST correction */
};
@ret: return 0 for success.On error, -1 is returned and errno is set to indicate the error.
*/
gettimeofday是C库提供的函数,其封装了sys_gettimeofday系统调用。但在x64结构上,gettimeofday可以不通过系统调用拿到系统时间。先看2个概念:
-
墙上时间:即实际时间(1970/1/1号以来的时间),它是由我们主板电池供电的(装过PC机的同学都了解)RTC单元存储的,这样即使机器断电了时间也不用重设。当操作系统启动时,会用这个RTC来初始化墙上时间,接着,内核会在一定精度内根据jiffies维护这个墙上时间。
-
jiffies:就是操作系统启动后经过的时间,它的单位是节拍数。有些体系架构,1个节拍数是10ms,但我们常用的x86体系下,1个节拍数是1ms。也就是说,jiffies这个全局变量存储了操作系统启动以来共经历了多少毫秒。
x64下do_gettimeofday
void do_gettimeofday(struct timeval *tv)
{
unsigned long seq, t;
unsigned int sec, usec;
do {
seq = read_seqbegin(&xtime_lock);
sec = xtime.tv_sec;
usec = xtime.tv_nsec / 1000;
t = (jiffies - wall_jiffies) * (1000000L / HZ) +
do_gettimeoffset();
usec += t;
} while (read_seqretry(&xtime_lock, seq));
tv->tv_sec = sec + usec / 1000000;
tv->tv_usec = usec % 1000000;
}
可以看出是通过xtime和jiffies共同得出的tv,没有经过系统调用。 更多的讨论请看浅谈时间函数gettimeofday的成本-陶辉
其他函数不再讨论
unittest代码
benchmark部分:
调用Timestamp::now()填充一个大小为1M的std::vector