python高精度定时器中断_0015 高精度定时器

本文详细介绍了如何在Linux内核中使用lapic timer创建高精度定时器,通过检测和计算lapic timer的频率,将定时器精度提升至纳秒级。文章还展示了初始化和配置lapic timer的步骤,并通过测试验证了定时器的精度和正确性。
摘要由CSDN通过智能技术生成

0012提过高精度时钟中断,这篇来讲下定时器。

传统操作系统内核一般都有个时间片tick,即相隔一定时间发生一次时钟中断,比如1毫秒,10毫秒,dos时代的1/18.5秒,有高精度时钟之后,我们通过定时器来模拟时间片。

在上一篇0014实验的时候用到了定时器,那个定时器是主cpu每1毫秒产生一个时钟中断,系统有个毫秒计数器msec_count。这个精度只有1毫秒。

现在来改造一下,把时钟中断改成单次触发而不是循环触发,精度改成纳秒(实际精度10纳秒)。

lapic timer每个cpu单独,比较适合做定时器。因此我们用lapic timer,而不是用hpet来做定时器。

lapic timer的频率是系统频率,需要检测一下。另外lapic timer有个tsc-deadline模式,这个模式更加精准,而且是64位计数,不像普通模式是32位计数器。但是也要计算一下频率。

增加一个文件,arch/lapic_timer.c

HPET可以读取到频率,因此利用HPET来计算lapic timer的频率。

lapic_timer分2次初始化。

core_initcall_sync(init_apic_timer_call);

postcore_initcall_sync(init_apic_timer_second);

第一次是init_apic_timer_call

static __init int init_apic_timer_call(bool isbp)

{

if(isbp) {

ASSERT(hpet_period);

lapic_write(APIC_SPIV, APIC_SPIV_APIC_ENABLED | SPURIOUS_APIC_VECTOR);

lapic_write(APIC_TDCR, APIC_TDR_DIV_16);

lapic_write(APIC_LVTT, LOCAL_TIMER_VECTOR);

lapic_write(APIC_TMICT, 0xFFFFFFFF);

start_uptime = hpet_uptime();

start_tsc = rdtsc();

printk("cpuid_apic_id:%d,isbp:%d\n",cpuid_apic_id(),isbp);

}

return 0;

}

只初始化主cpu(其他cpu频率相同)。

这个初始化要在hpet之后,因此用的是core_initcall_sync,hpet用的是coreinitcall,后面带sync的比不带的要晚(同一个初始化循环,但是排在后面,在kernel.ld里面设置)。

lapic timer先写入0xffffffff,最大值,然后读取当前时间跟tsc。hpet_uptime()放回hpet初始化之后的纳秒计数。

在第二次初始化的时候,读取新的纳秒计数,再对比lapic_timer里面的计数器就可以算出lapic_timer的频率。包括tsc的频率。

static __init int init_apic_timer_second(bool isbp)

{

return (isbp && calibrate_APIC_clock()) || setup_APIC_timer();

}

第二次同样只处理主cpu,先计算频率,再设置

static __init int calibrate_APIC_clock()

{

unsigned long flags;

flags = local_irq_save();

ulong now_uptime = hpet_uptime();

ulong now_tsc = rdtsc();

ulong remain = lapic_read(APIC_TMCCT);

local_irq_restore(flags);

ulong start = start_uptime;

ulong delta = now_uptime - start;

ASSERT(delta<0x400000000);//纳秒单位大概16秒,超过可能apic_timer32位寄存器溢出

ulong clock = delta*1000/(APIC_DIVISOR*(0xffffffff-remain));

clock+=5;

clock/=10;

clock*=10000UL;

clk_fsecs= clock;

clk_khz = FSEC_PER_MSEC / clk_fsecs;

lapic_timer_period = FSEC_PER_MSEC / APIC_DIVISOR / clock; //1ms clocks

clock = 1000*delta/(now_tsc - start_tsc);

clock*=1000UL;

tsc_fsecs = clock;

tsc_khz = FSEC_PER_MSEC / tsc_fsecs;

return 0;

}

由于lapic_timer是32位寄存器,为了避免溢出,使用主频除16,APIC_DIVISOR为16。

第一次初始化的时候设置了lapic_write(APIC_TDCR, APIC_TDR_DIV_16);

ulong clock = delta*1000/(APIC_DIVISOR*(0xffffffff-remain));

clock算出来的是计数1次所需要的皮秒。+5再除以/10是四舍五入,最后算出来的是计数1次所需要的飞秒(1/1000000纳秒)。khz是千赫兹,即1秒内计数器跳到多少千次。

static int setup_APIC_timer(void)

{

struct clock_event_device *levt = this_cpu_ptr(&the_cev);

memcpy(levt, &lapic_clockevent, sizeof(*levt));

if (boot_cpu_has(X86_FEATURE_TSC_DEADLINE_TIMER)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值