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。
快速排查步骤
- 检查socket释放路径,确认
del_timer_sync()在所有资源释放之前调用。 - 检查timer回调里是否有上下文有效性校验,比如socket状态、指针非空判断。
- 替换所有
add_timer()为mod_timer(),避免重复注册timer。 - 确认重传逻辑有明确的终止条件,防止无限循环触发timer。
内容的提问来源于stack exchange,提问作者zhf999
相关产品推荐
相关产品推荐

