You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux内核4.18中mod_timer函数10%计时漂移问题求助

Linux内核4.18定时器10%漂移问题解决

问题说明

在Linux内核4.18版本中使用mod_timer实现100ms周期定时器时,出现约10%的计时漂移,导致驱动同步异常。CONFIG_HZ配置为100,且该问题在不同硬件上表现一致。

原测试代码

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/gpio.h>

MODULE_LICENSE("GPL");

void simple_timer_function(struct timer_list *);

struct timer_list simple_timer;
struct timespec64 ts;

static int __init simple_timer_module_init(void)
{
    timer_setup(&simple_timer, simple_timer_function, 0);
    mod_timer(&simple_timer, jiffies + msecs_to_jiffies(100));
    printk(KERN_INFO "simple_timer_module_init init %ld\n", jiffies);
    return 0;
}

void simple_timer_function(struct timer_list *timer)
{
    printk(KERN_INFO "simple_timer_function Entry %d ms %ld\n", 
           jiffies_to_msecs(jiffies), jiffies);
    ktime_get_ts64(&ts);
    printk(KERN_INFO "Timerfunction %llu.%09u seconds\n", 
           (u64)ts.tv_sec, (u32)ts.tv_nsec);
    mod_timer(&simple_timer, (jiffies + msecs_to_jiffies(100)));
}

static void __exit simple_timer_module_exit(void)
{
    del_timer(&simple_timer);
}

module_init(simple_timer_module_init);
module_exit(simple_timer_module_exit);

测试输出

simple_timer_function Entry 64206210 ms 4301387917
Timerfunction 64506.210291016 seconds
simple_timer_function Entry 64206320 ms 4301387928
Timerfunction 64506.320284514 seconds
simple_timer_function Entry 64206430 ms 4301387939
Timerfunction 64506.430298059 seconds
simple_timer_function Entry 64206540 ms 4301387950
Timerfunction 64506.540331204 seconds
simple_timer_function Entry 64206650 ms 4301387961
Timerfunction 64506.650281875 seconds
simple_timer_function Entry 64206760 ms 4301387972
Timerfunction 64506.760230434 seconds
simple_timer_function Entry 64206870 ms 4301387983
Timerfunction 64506.870294392 seconds
simple_timer_function Entry 64206980 ms 4301387994
Timerfunction 64506.980259710 seconds
simple_timer_function Entry 64207090 ms 4301388005
Timerfunction 64507.090272193 seconds
simple_timer_function Entry 64207200 ms 4301388016

从输出可见,每次定时器触发间隔为110ms(如64206210到64206320),与预期的100ms存在10%漂移。

问题根源

CONFIG_HZ=100意味着内核时钟每10ms更新一次jiffies。原代码中每次在定时器回调里用jiffies + msecs_to_jiffies(100)计算下一次到期时间:

  • 若定时器因内核调度延迟,在预期时间后1个jiffies(10ms)才被执行,下一次到期时间会在当前延迟后的jiffies基础上加10个jiffies,导致间隔变为110ms;
  • 这种基于当前jiffies的累加方式会持续累积误差,最终形成固定10%的漂移。

解决方法

方法1:基于固定间隔累加到期时间

不再使用当前jiffies计算下一次到期时间,而是记录初始到期时间,每次回调中直接累加固定间隔的jiffies值,确保长期周期准确。

修改后的代码:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/gpio.h>
#include <linux/container_of.h>

MODULE_LICENSE("GPL");

// 用结构体封装定时器和到期时间,避免全局变量滥用
struct timer_data {
    struct timer_list timer;
    unsigned long next_expire; // 记录下一次到期的jiffies值
};

struct timer_data simple_timer_data;
struct timespec64 ts;

void simple_timer_function(struct timer_list *timer);

static int __init simple_timer_module_init(void)
{
    struct timer_data *data = container_of(&simple_timer_data.timer, struct timer_data, timer);
    timer_setup(&data->timer, simple_timer_function, 0);
    // 初始化第一次到期时间
    data->next_expire = jiffies + msecs_to_jiffies(100);
    mod_timer(&data->timer, data->next_expire);
    printk(KERN_INFO "simple_timer_module_init init %ld\n", jiffies);
    return 0;
}

void simple_timer_function(struct timer_list *timer)
{
    struct timer_data *data = container_of(timer, struct timer_data, timer);
    
    printk(KERN_INFO "simple_timer_function Entry %d ms %ld\n", 
           jiffies_to_msecs(jiffies), jiffies);
    ktime_get_ts64(&ts);
    printk(KERN_INFO "Timerfunction %llu.%09u seconds\n", 
           (u64)ts.tv_sec, (u32)ts.tv_nsec);
    
    // 基于上次到期时间累加固定间隔,避免漂移
    data->next_expire += msecs_to_jiffies(100);
    mod_timer(&data->timer, data->next_expire);
}

static void __exit simple_timer_module_exit(void)
{
    del_timer(&simple_timer_data.timer);
}

module_init(simple_timer_module_init);
module_exit(simple_timer_module_exit);

方法2:使用高精度定时器(hrtimer)

如果需要更高精度的计时(突破CONFIG_HZ的限制),可以使用内核的hrtimer接口,它支持纳秒级精度,适合对同步要求极高的场景。

示例代码框架:

#include <linux/hrtimer.h>
#include <linux/ktime.h>

static struct hrtimer simple_hrtimer;
static ktime_t interval;

enum hrtimer_restart simple_hrtimer_callback(struct hrtimer *timer)
{
    // 定时器逻辑
    hrtimer_forward_now(timer, interval);
    return HRTIMER_RESTART;
}

static int __init hrtimer_init(void)
{
    interval = ktime_set(0, 100 * NSEC_PER_MSEC); // 100ms间隔
    hrtimer_init(&simple_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    simple_hrtimer.function = simple_hrtimer_callback;
    hrtimer_start(&simple_hrtimer, interval, HRTIMER_MODE_REL);
    return 0;
}

验证效果

修改方法1的代码后,定时器触发间隔会稳定在100ms左右,即使某次出现调度延迟,后续的到期时间依然会按固定间隔排列,不会累积漂移。

内容的提问来源于stack exchange,提问作者Vijay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 03:22:02