计时变量
SylixOS里用到的大部分时间接口都是针对TOD(time of date)日期时间的,TOD时间通过心跳中断来更新,但并不是用的心跳中断计数变量实现的,而是另外两个全局变量_K_tvTODCurrent和_K_tvTODMono实现。
__KERNEL_EXT struct timespec _K_tvTODCurrent; /* 当前TOD时间 CLOCK_REALTIME */
__KERNEL_EXT struct timespec _K_tvTODMono; /* MonoTOD时间 CLOCK_MONOTONIC */
这两个时间变量都是纳秒精度的额,其中_K_tvTODMono是单调递增的,记录的是系统启动后累计运行的纳秒数;而_K_tvTODCurrent才是实际的TOD时间,它在运行中可能会被微调,也可能会配修改。
_K_tvTODCurrent被微调时还用用到下面两个全局变量。
KERNEL_EXT INT32 _K_iTODDelta; /* 系统时钟微调 tick 参数 */
__KERNEL_EXT INT32 _K_iTODDeltaNs; /* 系统时钟微调 ns 参数 */
初始化
这两个时间变量在系统启动时会被_RtcInit 函数初始化,代码位于libsylixos\SylixOS\kernel\core_RtcInit.c。其中_K_tvTODMono会被初始化为0,_K_tvTODCurrent被初始化为2000.1.1 0:0:0
,如果系统具备RTC硬件则会同步为RTC 时间。
VOID _RtcInit (VOID)
{
/*
* 初始化为 2000.1.1 0:0:0
*/
struct tm tmNow = {
0, 0, 0, 1, 0, (2000 - 1900), 6, 0, 0
};
time_t timeNow = lib_timegm(&tmNow);
timezone = (-(3600 * 8)); /* 默认为东8区 CST-8:00:00 */
_K_tvTODCurrent.tv_sec = timeNow;
_K_tvTODCurrent.tv_nsec = 0;
_K_tvTODMono.tv_sec = 0;
_K_tvTODMono.tv_nsec = 0;
}
时间更新
在系统心跳中断中每次这两个时间变量加LW_NSEC_PER_TICK纳秒时间,而不是计数加一。同时根据修正参数还会微调_K_tvTODCurrent时间变量。
#define TOD_UPDATE(tod, step_nsec) \
do { \
(tod)->tv_nsec += step_nsec; \
if ((tod)->tv_nsec >= __TIMEVAL_NSEC_MAX) { \
(tod)->tv_nsec -= __TIMEVAL_NSEC_MAX; \
(tod)->tv_sec++; \
} \
} while (0)
static LW_INLINE VOID __kernelTODUpdate (VOID)
{
LONG lNsec;
TOD_UPDATE(&_K_tvTODMono, LW_NSEC_PER_TICK); /* CLOCK_MONOTONIC */
if (_K_iTODDelta > 0) { /* 需要加快系统时钟一个 TICK */
_K_iTODDelta--;
lNsec = LW_NSEC_PER_TICK << 1; /* 加快一个 tick */
} else if (_K_iTODDelta < 0) {
_K_iTODDelta++;
return; /* 系统 tod 时间停止一个 tick */
} else if (_K_iTODDeltaNs) {
lNsec = LW_NSEC_PER_TICK + _K_iTODDeltaNs; /* 进行微调 */
_K_iTODDeltaNs = 0;
} else {
lNsec = LW_NSEC_PER_TICK;
}
TOD_UPDATE(&_K_tvTODCurrent, lNsec); /* CLOCK_REALTIME */
}
获取时间
POSIX标准的时间获取接口,最终都是读取的这两个时间变量。
time_t lib_time (time_t *time)
{
INTREG iregInterLevel;
time_t timetmp;
LW_SPIN_KERN_LOCK_QUICK(&iregInterLevel);
timetmp = _K_tvTODCurrent.tv_sec;
LW_SPIN_KERN_UNLOCK_QUICK(iregInterLevel);
if (time) {
*time = timetmp;
}
return (timetmp);
}
time_t lib_timelocal (time_t *time)
{
INTREG iregInterLevel;
time_t timetmp;
LW_SPIN_KERN_LOCK_QUICK(&iregInterLevel);
timetmp = UTC2LOCAL(_K_tvTODCurrent.tv_sec);
LW_SPIN_KERN_UNLOCK_QUICK(iregInterLevel);
if (time) {
*time = timetmp;
}
return (timetmp);
}
如果是要获取高精度时间,则还会调用底层的bspTickHighResolution接口函数,以获取心跳节拍以内的精度。
#if LW_CFG_TIME_HIGH_RESOLUTION_EN > 0
#define LW_TIME_HIGH_RESOLUTION(tv) bspTickHighResolution(tv)
#else
#define LW_TIME_HIGH_RESOLUTION(tv)
#endif
INT lib_clock_gettime (clockid_t clockid, struct timespec *tv)
{
INTREG iregInterLevel;
PLW_CLASS_TCB ptcbCur;
if (tv == LW_NULL) {
_ErrorHandle(EINVAL);
return (PX_ERROR);
}
switch (clockid) {
case CLOCK_REALTIME:
LW_SPIN_KERN_LOCK_QUICK(&iregInterLevel);
*tv = _K_tvTODCurrent;
LW_TIME_HIGH_RESOLUTION(tv);
LW_SPIN_KERN_UNLOCK_QUICK(iregInterLevel);
break;
case CLOCK_MONOTONIC:
LW_SPIN_KERN_LOCK_QUICK(&iregInterLevel);
*tv = _K_tvTODMono;
LW_TIME_HIGH_RESOLUTION(tv);
LW_SPIN_KERN_UNLOCK_QUICK(iregInterLevel);
break;
case CLOCK_PROCESS_CPUTIME_ID:
{
LW_LD_VPROC *pvproc = __LW_VP_GET_CUR_PROC();
if (pvproc == LW_NULL) {
_ErrorHandle(ESRCH);
return (PX_ERROR);
}
LW_SPIN_KERN_LOCK_QUICK(&iregInterLevel);
__tickToTimespec(pvproc->VP_clockUser + pvproc->VP_clockSystem, tv);
LW_TIME_HIGH_RESOLUTION(tv);
LW_SPIN_KERN_UNLOCK_QUICK(iregInterLevel);
} /* LW_CFG_MODULELOADER_EN > 0 */
break;
case CLOCK_THREAD_CPUTIME_ID:
LW_SPIN_KERN_LOCK_QUICK(&iregInterLevel);
LW_TCB_GET_CUR(ptcbCur);
__tickToTimespec(ptcbCur->TCB_ulCPUTicks, tv);
LW_TIME_HIGH_RESOLUTION(tv);
LW_SPIN_KERN_UNLOCK_QUICK(iregInterLevel);
break;
default:
_ErrorHandle(EINVAL);
return (PX_ERROR);
}
return (ERROR_NONE);
}
时间微调
/*********************************************************************************************************
** 函数名称: API_TimeTodAdj
** 功能描述: 微调 TOD 时间.
** 输 入 : piDelta TOD 调整时间 正数表示时间加速多少个对应的 ticks
** 负数表示时间减速多少个对应的 ticks
** 例如想让 TOD 时间加速一秒, 而且时钟 hz 为 100, 则此参数为 100
** 想让 TOD 时间减速一秒, 而且时钟 hz 为 100, 则此参数为 -100
** piOldDelta 上次没有调整完的剩余调整值
** 输 出 :
** 注 意 : 为了避免时光倒流, TOD 调回以前的时间系统将自动减速 TOD 的运算.
*********************************************************************************************************/
LW_API
VOID API_TimeTodAdj (INT32 *piDelta, INT32 *piOldDelta)
{
INTREG iregInterLevel;
LW_SPIN_KERN_LOCK_QUICK(&iregInterLevel);
if (piOldDelta) {
*piOldDelta = _K_iTODDelta;
}
if (piDelta) {
_K_iTODDelta = *piDelta;
}
LW_SPIN_KERN_UNLOCK_QUICK(iregInterLevel);
}
/*********************************************************************************************************
** 函数名称: API_TimeTodAdjEx
** 功能描述: 微调 TOD 时间.
** 输 入 : piDelta TOD 调整时间 正数表示时间加速多少个对应的 ticks
** 负数表示时间减速多少个对应的 ticks
** 例如想让 TOD 时间加速一秒, 而且时钟 hz 为 100, 则此参数为 100
** 想让 TOD 时间减速一秒, 而且时钟 hz 为 100, 则此参数为 -100
** piDeltaNs 一个 tick 以内 ns 数的调整
** piOldDelta 上次没有调整完的剩余调整值
** piOldDeltaNs 一个 tick 以内 ns 数的调整
** 输 出 :
** 注 意 : 为了避免时光倒流, TOD 调回以前的时间系统将自动减速 TOD 的运算.
*********************************************************************************************************/
LW_API
INT API_TimeTodAdjEx (INT32 *piDelta, INT32 *piDeltaNs, INT32 *piOldDelta, INT32 *piOldDeltaNs)
{
INTREG iregInterLevel;
if (piDeltaNs && (lib_abs(*piDeltaNs) > LW_NSEC_PER_TICK)) {
_ErrorHandle(E2BIG);
return (PX_ERROR);
}
LW_SPIN_KERN_LOCK_QUICK(&iregInterLevel);
if (piOldDelta) {
*piOldDelta = _K_iTODDelta;
}
if (piOldDeltaNs) {
*piOldDeltaNs = _K_iTODDeltaNs;
}
if (piDelta) {
_K_iTODDelta = *piDelta;
}
if (piDeltaNs) {
_K_iTODDeltaNs = *piDeltaNs;
}
LW_SPIN_KERN_UNLOCK_QUICK(iregInterLevel);
return (ERROR_NONE);
}