时间戳结构体定义在types.h,本质上是两个32位加上一个64位。根据宏PJ_HAS_INT64来决定是用一个64位存储还是用2个32位存储,该结构体存储的单位是系统频率,比如系统频率是纳秒,则pj_timestamp.u32.lo = 10表示经过了10个周期,也就是10ns。
/**
* This structure represents high resolution (64bit) time value. The time
* values represent time in cycles, which is retrieved by calling
* #pj_get_timestamp().
*/
typedef union pj_timestamp
{
struct
{
#if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN!=0
pj_uint32_t lo; /**< Low 32-bit value of the 64-bit value. */
pj_uint32_t hi; /**< high 32-bit value of the 64-bit value. */
#else
pj_uint32_t hi; /**< high 32-bit value of the 64-bit value. */
pj_uint32_t lo; /**< Low 32-bit value of the 64-bit value. */
#endif
} u32; /**< The 64-bit value as two 32-bit values. */
#if PJ_HAS_INT64
pj_uint64_t u64; /**< The whole 64-bit value, where available. */
#endif
} pj_timestamp;
时间戳的实现在os_timestamp_common.c和os_timestamp_posix.c
其中os_timestamp_posix.c实现了不同类型的两个函数pj_get_timestamp和pj_get_timestamp_freq,其中纳秒级的实现本质上调用clock_gettime
#elif defined(USE_POSIX_TIMERS) && USE_POSIX_TIMERS != 0
#include
#include
#include
#define NSEC_PER_SEC 1000000000
PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
{
struct timespec tp;
if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0) {
return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
}
ts->u64 = tp.tv_sec;
ts->u64 *= NSEC_PER_SEC;
ts->u64 += tp.tv_nsec;
return PJ_SUCCESS;
}
PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
{
freq->u32.hi = 0;
freq->u32.lo = NSEC_PER_SEC;
return PJ_SUCCESS;
}
微妙级实现
#else
#include
#include
#define USEC_PER_SEC 1000000
PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
{
struct timeval tv;
if (gettimeofday(&tv, NULL) != 0) {
return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
}
ts->u64 = tv.tv_sec;
ts->u64 *= USEC_PER_SEC;
ts->u64 += tv.tv_usec;
return PJ_SUCCESS;
}
PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
{
freq->u32.hi = 0;
freq->u32.lo = USEC_PER_SEC;
return PJ_SUCCESS;
}
os.h定义了几个时间戳加减比较的函数
(void) pj_set_timestamp32(pj_timestamp *t, pj_uint32_t hi, pj_uint32_t lo)
(int) pj_cmp_timestamp(const pj_timestamp *t1, const pj_timestamp *t2)
(void) pj_add_timestamp(pj_timestamp *t1, const pj_timestamp *t2)
(void) pj_add_timestamp32(pj_timestamp *t1, pj_uint32_t t2)
(void) pj_sub_timestamp(pj_timestamp *t1, const pj_timestamp *t2)
(void) pj_sub_timestamp32(pj_timestamp *t1, pj_uint32_t t2)
(pj_int32_t) pj_timestamp_diff32(const pj_timestamp *t1, const pj_timestamp *t2)
os_timestamp_common.c定义了elapsed_xxx运行时间计算的函数。
(pj_time_val) pj_elapsed_time( const pj_timestamp *start, const pj_timestamp *stop )
(pj_uint32_t) pj_elapsed_msec( const pj_timestamp *start, const pj_timestamp *stop )
(pj_uint64_t) pj_elapsed_msec64(const pj_timestamp *start,const pj_timestamp *stop )
(pj_uint32_t) pj_elapsed_usec( const pj_timestamp *start, const pj_timestamp *stop )
(pj_uint32_t) pj_elapsed_nanosec( const pj_timestamp *start, const pj_timestamp *stop )
(pj_uint32_t) pj_elapsed_cycle( const pj_timestamp *start, const pj_timestamp *stop )
这里用到一个自定义结构体pj_highprec_t,定义在high_precision.h。它可以在编译时决定是64位,32位,还是double浮点型。
#if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT != 0
/*
* The first choice for high precision math is to use double.
*/
# include
typedef double pj_highprec_t;
# define PJ_HIGHPREC_VALUE_IS_ZERO(a) (a==0)
# define pj_highprec_mod(a,b) (a=fmod(a,b))
#elif defined(PJ_HAS_INT64) && PJ_HAS_INT64 != 0
/*
* Next choice is to use 64-bit arithmatics.
*/
typedef pj_int64_t pj_highprec_t;
#else
# warning "High precision math is not available"
/*
* Last, fallback to 32-bit arithmetics.
*/
typedef pj_int32_t pj_highprec_t;
#endif
ecapsed_xxx函数都是先调用get_elapsed,计算运行了几个时间周期,然后再根据周期频率换算成毫秒或者微妙。ecapsed_xx只是起到计算起始到结束的运行时间,那么获取当前运行时间则是前面pj_get_timestamp。另外,pj_gettickcount先获取当前时间戳,再通过减去0,计算运行时间。
总结下Linux下时间使用场景,如果你只是要精确到秒或者更粗糙的时间,则time函数够用了。如果是程序中一般精确到毫秒,那么gettimeofday精度也够,但是容易因为系统时间的跳变而跳变。如果精度要求非常高,则使用clock_gettime(),其中CLOCK_MONOTONIC表示单调递增,虽然不会因为手动设置时间而跳变,但是受 adjtime(3) 影响。如果内核版本高于2.6.28,则可以使用CLOCK_MONOTONIC_RAW,真正的单调递增,不受adjtime(3) 影响。下面贴上man clock_gettime。
CLOCK_MONOTONIC
Clock that cannot be set and represents monotonic time since some unspecified starting point. This clock is not affected by
discontinuous jumps in the system time (e.g., if the system administrator manually changes the clock), but is affected by the
incremental adjustments performed by adjtime(3) and NTP.
CLOCK_MONOTONIC_RAW (since Linux 2.6.28; Linux-specific)
Similar to CLOCK_MONOTONIC, but provides access to a raw hardware-based time that is not subject to NTP adjustments or the
incremental adjustments performed by adjtime(3).