1

我编写了一个启动 hrtimer 的 linux 模块,它的回调函数每 20us 调用一次。假设开始时间为0,那么回调函数的执行时间应该是20us、40us、60us,……,但是在我的程序中,结果是在某些情况下,回调函数的执行可能会延迟一个很久。我想知道为什么会这样。

我的模块在 x86_64 平台上运行,tsc 用于计算延迟。以下是我的代码:

static enum hrtimer_restart hwt_timer_fn(struct hrtimer *timer) {
    unsigned long long tick_start;

    rdtscll(tick_start);
    tick_err = tick_start - cmp_cycle;
    cmp_cycle += tick_cycle;                //cmp_cycle means the next execute time of the callback function 
    hrtimer_add_expires(timer, expires);

    count++;
    if(tick_err > max_err)
            max_err = tick_err;
    if(tick_err < min_err)
            min_err = tick_err;

    return HRTIMER_RESTART;
}

static int kthread_hrtimer(void *arg) {
    struct timespec val;
    unsigned long long tick_start;

    val.tv_sec = 0;
    val.tv_nsec = 20*1000;
    expires = timespec_to_ktime(val);

    count = 0;
    max_err = min_err = 0;
    tick_cycle = cpu_khz/50;        //the tsc cycle of 20us
    printk("cpu_freq:%u,tick_cycle:%llu\n",cpu_khz,tick_cycle);

    hrtimer_init(&timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
    timer.function = hwt_timer_fn;

    rdtscll(tick_start);
    hrtimer_start(&timer,expires,HRTIMER_MODE_REL);
    cmp_cycle = tick_start + tick_cycle;

    return 0;
}

int hwt_timer_init(void) {
    cpumask_t mask;

    cpus_clear(mask);
    cpu_set(0,mask);

    thread = kthread_create(kthread_hrtimer,NULL,"kthread_hrtimer");
    if(IS_ERR(thread))
    {
            printk("create failure\n");
            return 1;
    }
    set_cpus_allowed_ptr(thread,&mask);
    wake_up_process(thread);

    return 0;
}

void hwt_timer_exit(void) {
    while(hrtimer_try_to_cancel(&timer) < 0);
    printk("max_err:%lld,min_err:%lld\n",max_err,min_err);
    printk("count:%lld\n",count);
    printk("\n\n");
}
4

2 回答 2

2

首先,Linux 不是硬实时操作系统。虽然 hrtimer 保证在其设定的目标过去之后而不是之前触发,但响应时间没有保证的界限。具体来说,hrtimers 机制不保证从目标时间发生到计时器触发之间经过的时间量。

触发可能延迟的一个可能原因是内核可能处于涉及禁用中断的关键部分。在这种情况下,会导致调用 hrtimer 函数的定时中断被屏蔽,直到内核退出临界区并再次启用中断。

为了解决单面计时器保证的第一个问题,我建议如下:

  • 使用阈值,我们称之为 TIMER_THRESHOLD

  • 为目标时间设置计时器时,将实际目标设置得稍早一些。(例如,不要使用“expires”,而是使用“expires - (TIMER_THRESHOLD/2)”)

  • 在计时器回调中,根据“目标时间”检查“当前时间”,如果“当前时间”在目标时间的 TIMER_THRESHOLD 范围内,则假定已达到目标时间。

此外,对于周期性计时事件,我建议使用 hrtimer_forward_now 而不是 hrtimer_add_expires。原因是为了处理定时器错过其截止时间超过定时器周期的情况。在这种情况下, hrtimer_add_expires 仍然设置一个过去的目标。

最后,我的另一个建议是尽可能使用比 20us 更长的延迟。20us是很短的时间。在 GHz 处理器上,大约有 20000 条指令。

于 2013-07-25T21:56:34.293 回答
0

我支持 Safayet Ahmed 的回答。无论如何,我想添加一些评论。

检查系统上的 CONFIG_HZ 选项,通常 CONFIG_HZ 为 1000。这意味着系统计时器精度可以在 1ms 左右(通常优于 1ms,但无法达到“us”精度)。

尝试将 CONFIG_HZ 更改为更大的值,以检查您的 hrtimer 精度是否提高。如果没有,请检查 hrtimer API。也许这个问题与您对 hrtimer API 的误用有关。

于 2013-07-26T03:07:05.443 回答