Linux时间子系统之clocksource的注册

来源:互联网 发布:ubuntu gcc 安装包 编辑:程序博客网 时间:2024/05/21 11:06

clocksource注册流程

clocksource_register / clocksource_register_hz / clocksource_register_khz    __clocksource_register_scale        __clocksource_updatefreq_scale            clocks_calc_mult_shift   /* 计算->mult和->shift */            clocksource_max_adjustment   /* 计算->maxadj 11% */            clocksource_max_deferment   /* 计算->max_idle_ns */        clocksource_enqueue        clocksource_enqueue_watchdog        clocksource_select
一个问题是clocksource的->mult和->shift是如何确定的。clocksource_register由调用者直接指定,clocksource_register_hz和clocksource_register_khz则根据时钟源频率和->mult的最大位数按以下算法计算得到。

->mult与->shift的算法

nsecs = (cycles / F) * NSEC_PER_SEC = (cycles * mult) >> shift
其中F为时钟频率,NSEC_PER_SEC为纳秒时钟频率,即10^9。将cycles=F带入上式,得
NSEC_PER_SEC = (F * mult) >> shift
根据频率单位可缩放scale(1为Hz,1000为KHz),得
NSEC_PER_SEC / scale = ((F / scale) * mult) >> shift
令to = NSEC_PER_SEC / scale,from = F / scale,得
to = (from * mult) >> shift
mult = (to << shift) / from
->shift的取值范围为[32:1],优先取大值,但须要确保->mult不超过有效位数。

for (sft = 32; sft > 0; sft--) {    tmp = (u64)to << sft;    tmp += from / 2;    do_div(tmp, from);    if ((tmp >> sftacc) == 0)        break;}*mult = tmp;*shift = sft;

现在的问题是->mult的最大位数sftacc如何确定。

->mult最大位数的算法

若cycles小于32位,->mult最大32位;
若cycles大于32位,->mult最大为64 - 最大10分钟cylces的位数。

u64 sec = (cs->mask - (cs->mask >> 3)); /* 最大cycles,减去12.5% */do_div(sec, freq); /*转换成秒或毫秒 */do_div(sec, scale); /* 转换成秒*/if (!sec)    sec = 1;else if (sec > 600 && cs->mask > UINT_MAX) /* 若cycles大于32位,有10分钟限制;若cycles不大于32位,没有时间限制(因为最大32位左移不会导致溢出) */    sec = 600;u32 maxsec = sec * scale; /* 转换成秒或毫秒 */u32 sftacc = 32;u64 tmp = ((u64)maxsec * freq) >> 32;while (tmp) {    tmp >>= 1;    sftacc--;}

为什么有10分钟限制

首先看clocksource计数值与实际时间单位的转换关系

s64 clocksource_cyc2ns(cycle_t cycles, u32 mult, u32 shift){    return ((u64)cycles * mult) >> shift;}
这里cycles是两次计数值增量,即->read()返回值与->cycle_last之差。

cycles和->mult都不能无限大,否则乘法结果溢出。内核需要限定一个更新->cycle_last的最大周期,10分钟被认为是合理的强制限定。因为从idle状态退出一定会更新->cycle_last,最大休眠时间->max_idle_ns可以通过以上得到的->mult计算出来。

max_cycles = 1ULL << (63 - (ilog2(cs->mult + cs->maxadj) + 1));   /* ->mult与cycles共同占用63位 */max_cycles = min_t(u64, max_cycles, (u64)cs->mask);   /* 不超过有效位数 */max_nsecs = clocksource_cyc2ns(max_cycles, cs->mult - cs->maxadj, cs->shift);return max_nsecs - (max_nsecs >> 3);
这里+1,11%的修正值和12.5%的余量都是防止溢出的保守的算法。

参考并感谢

http://blog.csdn.net/droidphone/article/details/7975694


原创粉丝点击