确切地说,这部分和之前分析的定时器关系不大,但是由于其实现逻辑和定时器比较类似,因此在分析时也就同步看了。
代码入口
components\drivers\ktime
代码分析
确切的说,ktime模块分为3个组成部分,一个部分叫boottime,可以理解为启动后经历的时间相关的功能。另一个叫cputime,用于系统滴答时钟方式的计时相关功能,最后一个叫hrtime,粗看实现是利用硬件定时器实现的us级精度的延时,休眠等控制,但还需要详细分析。
cputime源码分析
cputime的实现很依赖芯片架构,从目前开源出来的代码看,只有支持aarch64和virt64两种架构的芯片可以使用该模块,而如果是非这两种架构的芯片,cputime也会利用系统的滴答时钟进行计时,但此计时的精度就没那么高了。出于简化分析的考量,后面的分析仅针对aarch64架构源码进行分析。
初始化入口
.globl rt_hw_get_cntpct_val rt_hw_get_cntpct_val: MRS X0, CNTPCT_EL0 RET static volatile unsigned long _init_cnt = 0; // aarch64架构实现 void rt_ktime_cputimer_init(void) { _init_cnt = rt_hw_get_cntpct_val(); } // 通用入口 rt_weak void rt_ktime_cputimer_init(void) { return; }
从实现上看,aarch64的cputimer初始化部分仅仅是读取了当前的芯片系统的滴答值并保存。
获取当前计数值入口
unsigned long rt_ktime_cputimer_getcnt(void) { return rt_hw_get_cntpct_val() - _init_cnt; } // 通用实现 rt_weak unsigned long rt_ktime_cputimer_getcnt(void) { return rt_tick_get(); }
可以很明显的看到,在初始化时读到的计数值,是用于初始化计数的部分。也就是说,在调用了rt_ktime_cputimer_init才开始从0往上计数。
获取滴答频率入口
.globl rt_hw_get_gtimer_frq rt_hw_get_gtimer_frq: MRS X0,CNTFRQ_EL0 RET unsigned long rt_ktime_cputimer_getfrq(void) { return rt_hw_get_gtimer_frq(); } // 通用实现 rt_weak unsigned long rt_ktime_cputimer_getfrq(void) { return RT_TICK_PER_SECOND; }
这个接口也就是直接读取芯片的滴答频率并上报。
获取步进值入口
// aarch64架构实现 unsigned long rt_ktime_cputimer_getstep(void) { return rt_ktime_cputimer_getfrq() / RT_TICK_PER_SECOND; } // 通用入口 rt_weak unsigned long rt_ktime_cputimer_getstep(void) { return 1; }
获取定时器的分辨率
#define RT_KTIME_RESMUL (1000000UL) unsigned long rt_ktime_cputimer_getres(void) { return ((1000UL * 1000 * 1000) * RT_KTIME_RESMUL) / rt_hw_get_gtimer_frq(); } // 通用实现 rt_weak unsigned long rt_ktime_cputimer_getres(void) { return ((1000UL * 1000 * 1000) * RT_KTIME_RESMUL) / RT_TICK_PER_SECOND; }
boottime分析
从功能上看,bootime实现的功能是获取启动后所经过的时间,其精度分为s,us和ns。而在实现上,boottime的实现是基于cputime的实现而实现的。
获取秒级计时入口
rt_weak rt_err_t rt_ktime_boottime_get_s(time_t *t) { RT_ASSERT(t != RT_NULL); unsigned long ns = (rt_ktime_cputimer_getcnt() * rt_ktime_cputimer_getres()) / RT_KTIME_RESMUL; *t = ns / (1000UL * 1000 * 1000); return RT_EOK; }
获取微妙级计时入口
rt_weak rt_err_t rt_ktime_boottime_get_us(struct timeval *tv) { RT_ASSERT(tv != RT_NULL); unsigned long ns = (rt_ktime_cputimer_getcnt() * rt_ktime_cputimer_getres()) / RT_KTIME_RESMUL; tv->tv_sec = ns / (1000UL * 1000 * 1000); tv->tv_usec = (ns % (1000UL * 1000 * 1000)) / 1000; return RT_EOK; }
获取纳秒级计时入口
hrtime分析
从hrtime暴露出来的接口看,此模块最主要的功能是实现us,ns和ms级别的延时。而其用法可以在nanosleep中看到,具体如下:
#if defined(RT_USING_POSIX_DELAY) && defined(RT_USING_KTIME) int nanosleep(const struct timespec *rqtp, struct timespec *rmtp) { struct timespec old_ts = {0}; struct timespec new_ts = {0}; struct rt_ktime_hrtimer timer; // 初始化结构体timer rt_ktime_hrtimer_delay_init(&timer); if (rqtp == RT_NULL) { rt_set_errno(EFAULT); return -1; } if (rqtp->tv_sec < 0 || rqtp->tv_nsec < 0 || rqtp->tv_nsec >= NANOSECOND_PER_SECOND) { rt_set_errno(EINVAL); return -1; } unsigned long ns = rqtp->tv_sec * NANOSECOND_PER_SECOND + rqtp->tv_nsec; rt_ktime_boottime_get_ns(&old_ts); // 获取ns精度的启动计时 rt_ktime_hrtimer_ndelay(&timer, ns); // 休眠ns的时长 if (rt_get_errno() == RT_EINTR) { // 如果是中断中返回,则说明是执行异常,此时通过rmtp将已执行的时间向上报,以便上层做后续处理 // 并销毁timer运行时占用的资源 if (rmtp) { rt_base_t rsec, rnsec; rt_ktime_boottime_get_ns(&new_ts); rsec = old_ts.tv_sec + rqtp->tv_sec - new_ts.tv_sec; rnsec = old_ts.tv_nsec + rqtp->tv_nsec - new_ts.tv_nsec; if (rnsec < 0) { rmtp->tv_sec = rsec - 1; rmtp->tv_nsec = NANOSECOND_PER_SECOND + rnsec; } else { rmtp->tv_sec = rsec; rmtp->tv_nsec = rnsec; } } rt_ktime_hrtimer_delay_detach(&timer); rt_set_errno(EINTR); return -1; } // 销毁timer运行时所产生的资源 rt_ktime_hrtimer_delay_detach(&timer); return 0; } RTM_EXPORT(nanosleep); #endif /* RT_USING_POSIX_DELAY && RT_USING_KTIME */
rt_ktime_hrtimer_delay_init
static void _sleep_timeout(void *parameter) { // 超时后通过完成量唤醒任务,通过nanosleep的实现看, // 这个等待完成量的操作就在函数rt_ktime_hrtimer_ndelay中执行 struct rt_ktime_hrtimer *timer = parameter; rt_completion_done(&timer->completion); } // 仅仅是做了资源的初始化,并未做具体功能的执行 void rt_ktime_hrtimer_init(rt_ktime_hrtimer_t timer, const char *name, unsigned long cnt, rt_uint8_t flag, void (*timeout)(void *parameter), void *parameter) { /* parameter check */ RT_ASSERT(timer != RT_NULL); RT_ASSERT(timeout != RT_NULL); RT_ASSERT(cnt < (_HRTIMER_MAX_CNT / 2)); /* set flag */ timer->parent.flag = flag; /* set deactivated */ timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; timer->timeout_func = timeout; timer->parameter = parameter; timer->timeout_cnt = cnt + rt_ktime_cputimer_getcnt(); timer->init_cnt = cnt; rt_list_init(&(timer->row)); rt_completion_init(&timer->completion); } void rt_ktime_hrtimer_delay_init(struct rt_ktime_hrtimer *timer) { // 可以发现,nanosleep的执行,依赖系统滴答时钟,本质上属于系统调度的一部分 rt_ktime_hrtimer_init(timer, "hrtimer_sleep", 0, RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_HARD_TIMER, _sleep_timeout, timer); }
rt_ktime_hrtimer_sleep
其实对外暴露的并不是这个入口,而是rt_ktime_hrtimer_ndelay,rt_ktime_hrtimer_udelay和rt_ktime_hrtimer_mdelay,但奈何这三个函数的实现,本质上都是rt_ktime_hrtimer_sleep,因此这里也就分析此函数了。
对外暴露的延时函数实现
会发现三个函数本质上都是基于rt_ktime_hrtimer_sleep实现的。
rt_err_t rt_ktime_hrtimer_ndelay(struct rt_ktime_hrtimer *timer, unsigned long ns) { unsigned long res = rt_ktime_cputimer_getres(); return rt_ktime_hrtimer_sleep(timer, (ns * RT_KTIME_RESMUL) / res); } rt_err_t rt_ktime_hrtimer_udelay(struct rt_ktime_hrtimer *timer, unsigned long us) { return rt_ktime_hrtimer_ndelay(timer, us * 1000); } rt_err_t rt_ktime_hrtimer_mdelay(struct rt_ktime_hrtimer *timer, unsigned long ms) { return rt_ktime_hrtimer_ndelay(timer, ms * 1000000); }
rt_ktime_hrtimer_sleep
#ifdef ARCH_CPU_64BIT #define _HRTIMER_MAX_CNT UINT64_MAX #else #define _HRTIMER_MAX_CNT UINT32_MAX #endif static rt_list_t _timer_list = RT_LIST_OBJECT_INIT(_timer_list); static rt_ktime_hrtimer_t _nowtimer = RT_NULL; static RT_DEFINE_SPINLOCK(_spinlock); rt_weak unsigned long rt_ktime_hrtimer_getres(void) { return ((1000UL * 1000 * 1000) * RT_KTIME_RESMUL) / RT_TICK_PER_SECOND; } static void (*_outcb)(void *param) = RT_NULL; static void _hrtimer_timeout(void *parameter) { if (_outcb) _outcb(parameter); } rt_weak rt_err_t rt_ktime_hrtimer_settimeout(unsigned long cnt, void (*timeout)(void *param), void *param) { static rt_timer_t timer = RT_NULL; _outcb = timeout; if (cnt == 0) { if (timer != RT_NULL) { if (timer->parent.flag & RT_TIMER_FLAG_ACTIVATED) { rt_timer_stop(timer); } } if (_outcb) _outcb(param); return RT_EOK; } if (timer == RT_NULL) { // 从此处可以发现,其实ktime的实现是依赖系统调度定时器实现的 timer = rt_timer_create("shrtimer", _hrtimer_timeout, param, cnt, RT_TIMER_FLAG_ONE_SHOT); } else { rt_tick_t tick = cnt; rt_timer_control(timer, RT_TIMER_CTRL_SET_TIME, &tick); rt_timer_control(timer, RT_TIMER_CTRL_SET_PARM, param); } if (timer->parent.flag & RT_TIMER_FLAG_ACTIVATED) { rt_timer_stop(timer); } rt_timer_start(timer); return RT_EOK; } // 将计数值换算成实际滴答周期数 static unsigned long _cnt_convert(unsigned long cnt) { unsigned long rtn = 0; unsigned long count = cnt - rt_ktime_cputimer_getcnt(); if (count > (_HRTIMER_MAX_CNT / 2)) return 0; rtn = (count * rt_ktime_cputimer_getres()) / rt_ktime_hrtimer_getres(); return rtn == 0 ? 1 : rtn; /* at least 1 */ } static void _set_next_timeout_n_unlock(rt_base_t level) { rt_ktime_hrtimer_t t; if (&_timer_list != _timer_list.prev) { // 如果列表非空,则说明存在定时器,此时需要启用定时器 t = rt_list_entry((&_timer_list)->next, struct rt_ktime_hrtimer, row); if (_nowtimer != RT_NULL) { // 如果当前已有定时器在执行,则判断新插入的定时器是否比已有定时器更早执行 // 若有,则将_nowtimer替换成新插入的定时器并执行 // 否则直接启用定时器 if (t != _nowtimer && t->timeout_cnt < _nowtimer->timeout_cnt) { _nowtimer = t; rt_spin_unlock_irqrestore(&_spinlock, level); rt_ktime_hrtimer_settimeout(_cnt_convert(t->timeout_cnt), _timeout_callback, t); } else { rt_spin_unlock_irqrestore(&_spinlock, level); } } else { // 如果当前没有定时器执行,则启用最新插入的定时器 _nowtimer = t; rt_spin_unlock_irqrestore(&_spinlock, level); rt_ktime_hrtimer_settimeout(_cnt_convert(t->timeout_cnt), _timeout_callback, t); } } else { // 如果定时器列表为空,则说明所有的定时器任务都已经执行完毕,此时仅需要恢复现场并停用定时器即可 _nowtimer = RT_NULL; rt_spin_unlock_irqrestore(&_spinlock, level); rt_ktime_hrtimer_settimeout(0, RT_NULL, RT_NULL); } } rt_err_t rt_ktime_hrtimer_start(rt_ktime_hrtimer_t timer) { rt_list_t *timer_list; rt_base_t level; /* 检查定时器 */ RT_ASSERT(timer != RT_NULL); level = rt_spin_lock_irqsave(&_spinlock); rt_list_remove(&timer->row); /* 将定时器从定时器执行列表中移除 */ /* 禁用定时器 */ timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; /* 从已有定时器列表中插入timer(列表时间从小到大排列)*/ timer_list = &_timer_list; for (; timer_list != _timer_list.prev; timer_list = timer_list->next) { rt_ktime_hrtimer_t t; rt_list_t *p = timer_list->next; t = rt_list_entry(p, struct rt_ktime_hrtimer, row); if ((t->timeout_cnt - timer->timeout_cnt) == 0) { continue; } else if ((t->timeout_cnt - timer->timeout_cnt) < (_HRTIMER_MAX_CNT / 2)) { break; } } rt_list_insert_after(timer_list, &(timer->row)); // 启用定时器 timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED; // 设置下一次定时器超时解锁时间 _set_next_timeout_n_unlock(level); return RT_EOK; } // 记录执行结果 rt_inline void rt_ktime_hrtimer_keep_errno(rt_ktime_hrtimer_t timer, rt_err_t err) { RT_ASSERT(timer != RT_NULL); timer->error = err; rt_set_errno(-err); } rt_err_t rt_ktime_hrtimer_sleep(struct rt_ktime_hrtimer *timer, unsigned long cnt) { rt_err_t err; if (cnt == 0) return -RT_EINVAL; // 初始化定时器参数 timer->timeout_cnt = cnt + rt_ktime_cputimer_getcnt(); timer->init_cnt = cnt; // 启用定时器 rt_ktime_hrtimer_start(timer); // 等待定时器超时 err = rt_completion_wait_flags(&(timer->completion), RT_WAITING_FOREVER, RT_INTERRUPTIBLE); // 记录定时器执行结果 rt_ktime_hrtimer_keep_errno(timer, err); return RT_EOK; }
rt_ktime_hrtimer_detach
其实对外暴露的是rt_ktime_hrtimer_delay_detach,而rt_ktime_hrtimer_delay_detach的实现,其实只是直接调用rt_ktime_hrtimer_detach,因此直接分析rt_ktime_hrtimer_detach。
rt_err_t rt_ktime_hrtimer_detach(rt_ktime_hrtimer_t timer) { rt_base_t level; /* 有效性检查 */ RT_ASSERT(timer != RT_NULL); /* 通知定时器结束等待 */ rt_completion_wakeup_by_errno(&timer->completion, RT_ERROR); level = rt_spin_lock_irqsave(&_spinlock); /* 关闭定时器 */ timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; /* 如果此时定时器在中断中,表明此时定时器已经执行了,需要将定时器从定时器列表中移除 */ if (timer->error == -RT_EINTR || timer->error == RT_EINTR) { _nowtimer = RT_NULL; rt_list_remove(&timer->row); _set_next_timeout_n_unlock(level); } else { rt_spin_unlock_irqrestore(&_spinlock, level); } return RT_EOK; }
其余接口
这些接口并没有直接暴露给应用,而是暴露到了ctime模块中的timer_create和timer_delete,具体调用实现如下:
#define TIMER_ID_MAX 50 static struct rt_spinlock _timer_id_lock = RT_SPINLOCK_INIT; static struct timer_obj *_g_timerid[TIMER_ID_MAX]; static void *timer_id[TIMER_ID_MAX]; static resource_id_t id_timer = RESOURCE_ID_INIT(TIMER_ID_MAX, timer_id); int timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid) { static int num = 0; int _timerid = 0; struct timer_obj *timer; char timername[RT_NAME_MAX] = {0}; if (evp == RT_NULL || timerid == RT_NULL) { rt_set_errno(EINVAL); return -1; } if (evp->sigev_notify == SIGEV_THREAD) // TODO need to implement { rt_set_errno(EINVAL); return -1; } switch (clockid) { case CLOCK_REALTIME: case CLOCK_REALTIME_ALARM: case CLOCK_MONOTONIC: case CLOCK_BOOTTIME: case CLOCK_BOOTTIME_ALARM: case CLOCK_PROCESS_CPUTIME_ID: case CLOCK_THREAD_CPUTIME_ID: break; // Only these ids are supported default: rt_set_errno(EINVAL); return -1; } // 申请并初始化timer资源,初始化信息来自evp timer = rt_malloc(sizeof(struct timer_obj)); if(timer == RT_NULL) { rt_set_errno(ENOMEM); return -1; } rt_snprintf(timername, RT_NAME_MAX, "psx_tm%02d", num++); num %= 100; timer->sigev_signo = evp->sigev_signo; #ifdef RT_USING_SMART // RT_SMART相关的代码,暂时不需要关注 struct rt_work *work; struct rt_lwp *lwp = lwp_self(); struct lwp_timer_event_param *param; param = rt_malloc(sizeof(struct lwp_timer_event_param)); work = ¶m->work; if (!work) { rt_set_errno(ENOMEM); return -1; } if (lwp) { timer->pid = lwp_self()->pid; rt_list_insert_after(&lwp->timer, &timer->lwp_node); } else { timer->pid = 0; /* pid 0 is never used */ } timer->work = work; #endif /* RT_USING_SMART */ timer->sigev_notify_function = evp->sigev_notify_function; timer->val = evp->sigev_value; timer->interval.tv_sec = 0; timer->interval.tv_nsec = 0; timer->reload = 0U; timer->status = NOT_ACTIVE; timer->clockid = clockid; // 初始化hrtimer rt_ktime_hrtimer_init(&timer->hrtimer, timername, 0, RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_HARD_TIMER, rtthread_timer_wrapper, timer); // 获取资源ID号并将定时器挂载在此ID号上 _timerid = resource_id_get(&id_timer); if (_timerid < 0) { // 如果ID号小于0,则说明timer已经注册满了,不能再注册新的timer了,此时需要销毁资源并返回错误信息 #ifdef RT_USING_SMART rt_free(param); #endif /* RT_USING_SMART */ rt_ktime_hrtimer_detach(&timer->hrtimer); rt_free(timer); rt_set_errno(ENOMEM); return -1; } _g_timerid[_timerid] = timer; timer->timer_id = (timer_t)(rt_ubase_t)_timerid; *timerid = (timer_t)(rt_ubase_t)_timerid; return 0; } RTM_EXPORT(timer_create); int timer_delete(timer_t timerid) { struct timer_obj *timer; rt_ubase_t ktimerid; ktimerid = (rt_ubase_t)timerid; if (ktimerid < 0 || ktimerid >= TIMER_ID_MAX) { // 如果定时器编号不再有效范围内,则报错返回 rt_set_errno(EINVAL); return -1; } RT_DEBUG_NOT_IN_INTERRUPT; // 要求不能在中断中删除定时器 // 从定时器列表中取出timer,并将维护的_g_timerid和id_timer标记为未使用 rt_spin_lock(&_timer_id_lock); timer = _g_timerid[ktimerid]; if (timer != NULL) { _g_timerid[ktimerid] = RT_NULL; resource_id_put(&id_timer, ktimerid); } rt_spin_unlock(&_timer_id_lock); if (timer == RT_NULL) { rt_set_errno(EINVAL); LOG_D("can not find timer %ld", ktimerid); return -1; } //如果拿出的定时器有效且处于激活状态,则置为非激活状态并停用该定时器 if (timer->status == ACTIVE) { timer->status = NOT_ACTIVE; rt_ktime_hrtimer_stop(&timer->hrtimer); } // 解注册该定时器 rt_ktime_hrtimer_detach(&timer->hrtimer); #ifdef RT_USING_SMART // RT_SMART的实现,需使用时针对看 if (timer->pid) rt_list_remove(&timer->lwp_node); rt_free(timer->work); #endif // 释放定时器资源 rt_free(timer); return 0; } RTM_EXPORT(timer_delete);
rt_ktime_hrtimer_stop
rt_err_t rt_ktime_hrtimer_stop(rt_ktime_hrtimer_t timer) { rt_base_t level; RT_ASSERT(timer != RT_NULL); level = rt_spin_lock_irqsave(&_spinlock); if (!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)) { // 如果定时器当前未启用,则不需要停止 rt_spin_unlock_irqrestore(&_spinlock, level); return -RT_ERROR; } // 移除当前定时器并将定时器标记置为停用 _nowtimer = RT_NULL; rt_list_remove(&timer->row); timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; // 启动下一个定时器并退出spin lock _set_next_timeout_n_unlock(level); return RT_EOK; }
rt_ktime_hrtimer_control
rt_err_t rt_ktime_hrtimer_control(rt_ktime_hrtimer_t timer, int cmd, void *arg) { rt_base_t level; /* parameter check */ RT_ASSERT(timer != RT_NULL); level = rt_spin_lock_irqsave(&_spinlock); switch (cmd) { case RT_TIMER_CTRL_GET_TIME: // 获取定时器的初始时间 *(unsigned long *)arg = timer->init_cnt; break; case RT_TIMER_CTRL_SET_TIME: // 设置定时器的超时时间 RT_ASSERT((*(unsigned long *)arg) < (_HRTIMER_MAX_CNT / 2)); timer->init_cnt = *(unsigned long *)arg; timer->timeout_cnt = *(unsigned long *)arg + rt_ktime_cputimer_getcnt(); break; case RT_TIMER_CTRL_SET_ONESHOT: // 设置为单次触发 timer->parent.flag &= ~RT_TIMER_FLAG_PERIODIC; break; case RT_TIMER_CTRL_SET_PERIODIC: // 设置为周期工作 timer->parent.flag |= RT_TIMER_FLAG_PERIODIC; break; case RT_TIMER_CTRL_GET_STATE: // 获取定时器的工作状态 if (timer->parent.flag & RT_TIMER_FLAG_ACTIVATED) { *(rt_uint32_t *)arg = RT_TIMER_FLAG_ACTIVATED; } else { *(rt_uint32_t *)arg = RT_TIMER_FLAG_DEACTIVATED; } break; case RT_TIMER_CTRL_GET_REMAIN_TIME: // 获取定时器的剩余时间 *(unsigned long *)arg = timer->timeout_cnt; break; case RT_TIMER_CTRL_GET_FUNC: // 获取该定时器的超时处理函数 arg = (void *)timer->timeout_func; break; case RT_TIMER_CTRL_SET_FUNC: // 设置该定时器的超时处理函数 timer->timeout_func = (void (*)(void *))arg; break; case RT_TIMER_CTRL_GET_PARM: // 获取定时器内部参数 *(void **)arg = timer->parameter; break; case RT_TIMER_CTRL_SET_PARM: // 设置定时器内部参数 timer->parameter = arg; break; default: break; } rt_spin_unlock_irqrestore(&_spinlock, level); return RT_EOK; }
总结
经过对这部分代码的分析,我们会得出以下结论:cputime本身的功能仅仅是提供更细颗粒度的计时信息,而他被boottime模块所使用。boottime模块,主要功能就是记录系统从cputime起来后到读取时间是系统所消耗的时间,其颗粒度与调用的接口有关。
hrtime模块,则是直接通过系统滴答时钟的方式实现了高于ms精度的延时,解决带RTOS时,只能使用比滴答时钟想等或者更长时间延时的问题。