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

Linux内核Timer使用触发Oops问题及正确用法咨询

Linux内核中timer_list的正确使用与问题排查

你遇到的Oops和CPU阻塞问题,核心原因几乎都是timer回调触发时,关联的socket上下文已经被释放,或者timer没有被正确终止导致无限循环。下面是内核timer_list的标准用法和针对你的场景的修复要点:


一、初始化timer:绑定安全的上下文

timer必须和持久化的内核态内存绑定,绝对不能用用户态内存或会被提前释放的内核内存作为私有数据。

新内核(5.10+)推荐用法:

用timer_setup()初始化,配合from_timer()在回调中获取上下文:

struct rudp_socket {
    struct timer_list retrans_timer;
    struct sk_buff *pending_pkt;
    int state; // 比如RUDP_OPEN/RUDP_CLOSED
    int retrans_count;
};

// 初始化timer,绑定回调和结构体
timer_setup(&rudp_sock->retrans_timer, retransmit_callback, 0);

旧内核兼容用法:

用setup_timer(),通过unsigned long传递上下文指针:

setup_timer(&rudp_sock->retrans_timer, retransmit_callback, (unsigned long)rudp_sock);

二、启停timer:用mod_timer统一管理

不要混用add_timer()和mod_timer(),mod_timer()可以同时处理timer的启动和超时时间修改,自动处理timer是否已激活的状态:

// 设置100ms后触发重传(启动或更新timer)
mod_timer(&rudp_sock->retrans_timer, jiffies + msecs_to_jiffies(100));

三、资源释放前:必须同步删除timer

这是你遇到Oops的核心原因——测试程序关闭后socket资源被释放,但timer还在排队触发,回调访问已释放内存导致页错误。

必须用del_timer_sync()替代del_timer(),它会等待当前正在执行的回调完成(如果有的话),彻底避免竞态:

void rudp_release_socket(struct rudp_socket *sock) {
    // 第一步:先删除timer,确保回调不会再触发,且当前回调执行完毕
    del_timer_sync(&sock->retrans_timer);
    
    // 第二步:再释放所有关联资源
    if (sock->pending_pkt) {
        kfree_skb(sock->pending_pkt);
    }
    kfree(sock);
}

⚠️ 注意:不能在timer回调内部调用del_timer_sync(),会导致死锁,回调里如果要停止timer,用del_timer()即可。


四、回调函数:先做有效性检查

在回调里第一时间检查上下文是否有效,避免访问无效内存:

// 新内核回调写法
void retransmit_callback(struct timer_list *t) {
    struct rudp_socket *sock = from_timer(sock, t, retrans_timer);
    
    // 先检查socket状态,防止访问已释放内存
    if (!sock || sock->state == RUDP_CLOSED) {
        return;
    }

    // 重传逻辑
    if (sock->pending_pkt) {
        udp_send(sock->pending_pkt);
        
        // 检查重传次数,避免无限循环
        if (sock->retrans_count >= MAX_RETRANS) {
            rudp_release_socket(sock);
            return;
        }
        
        sock->retrans_count++;
        // 重新设置timer,继续下一次重传
        mod_timer(&sock->retrans_timer, jiffies + msecs_to_jiffies(100));
    }
}

// 旧内核回调写法
void retransmit_callback(unsigned long data) {
    struct rudp_socket *sock = (struct rudp_socket *)data;
    
    if (!sock || sock->state == RUDP_CLOSED) {
        return;
    }
    
    // 同上重传逻辑...
}

五、避免CPU阻塞:设置重传终止条件

如果你的重传逻辑没有终止条件(比如没检查重传次数上限),timer会无限触发,占满CPU核心。必须在回调里判断终止条件,比如达到最大重传次数后关闭连接、停止timer。


快速排查步骤

  1. 检查socket释放路径,确认del_timer_sync()在所有资源释放之前调用。
  2. 检查timer回调里是否有上下文有效性校验,比如socket状态、指针非空判断。
  3. 替换所有add_timer()为mod_timer(),避免重复注册timer。
  4. 确认重传逻辑有明确的终止条件,防止无限循环触发timer。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 19:03:17