首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Jiffies回绕问题详解

Jiffies回绕问题详解

作者头像
一个平凡而乐于分享的小比特
发布2026-02-02 17:20:29
发布2026-02-02 17:20:29
840
举报

🔥作者简介: 一个平凡而乐于分享的小比特,中南民族大学通信工程专业研究生,研究方向无线联邦学习 🎬擅长领域:驱动开发,嵌入式软件开发,BSP开发 ❄️作者主页:一个平凡而乐于分享的小比特的个人主页 ✨收录专栏:Linux,本专栏目的在于,记录学习Linux操作系统的总结 欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖💖

在这里插入图片描述
在这里插入图片描述

Jiffies回绕问题详解

📖 什么是Jiffies?

Jiffies是Linux内核中的一个全局变量,用来记录系统启动以来的时钟滴答数(时钟中断次数)。每次时钟中断发生,jiffies就增加1。

基本信息速览

项目

说明

定义

extern unsigned long volatile jiffies;

更新频率

由HZ决定,通常为100、250或1000Hz

数据类型

32位或64位无符号整数

32位最大值

约49.7天(HZ=1000时)

主要用途

记录时间戳、计算时间间隔、实现超时机制

为什么会出现回绕?

核心原因:计数器溢出

就像汽车的里程表一样,当数值达到最大值后,会从0重新开始

代码语言:javascript
复制
普通32位jiffies(HZ=1000):
开始:0x00000000 → ... → 0xFFFFFFFF → 0x00000000(回绕!)
时间:0天 → 49.7天 → 0天(重新计数)
时间线图解
代码语言:javascript
复制
系统启动
    │
    ├─── jiffies = 0
    │      (时间: 0天)
    │
    ├─── jiffies = 2,147,483,647
    │      (时间: 24.85天)
    │
    ├─── jiffies = 4,294,967,295 ← **最大值**
    │      (时间: 49.7天)   ↗
    │                      │
    └─── jiffies = 0       │ **回绕发生!**
          (重新开始)       │
                           │
                   计数器溢出,从0重新开始

⚠️ 回绕引发的问题

场景对比:正常的超时检查 vs 回绕时的错误
正常情况(无回绕)
代码语言:javascript
复制
unsigned long timeout = jiffies + HZ*5;  // 5秒后超时

// 循环检查是否超时
while (time_before(jiffies, timeout)) {
    // 正常工作...
}
// jiffies达到timeout,正常退出循环

时间轴图示

代码语言:javascript
复制
jiffies: 1000 → 1001 → ... → 5999 → 6000
timeout: 6000 (固定值)
关系:jiffies < timeout ✅ 始终成立,直到超时
回绕发生时
代码语言:javascript
复制
// 假设jiffies即将回绕
unsigned long jiffies = 0xFFFFFFFE;  // 即将溢出
unsigned long timeout = jiffies + HZ*5;  // 计算得到 0x00000003

// 使用简单比较(错误的做法)
while (jiffies < timeout) {  // 这里会出问题!
    // ...
}

时间轴图示

代码语言:javascript
复制
阶段1(回绕前):
jiffies: 0xFFFFFFFE → 0xFFFFFFFF → 0x00000000
timeout: 0x00000003
关系:回绕前 jiffies > timeout ❌

阶段2(回绕后):
jiffies: 0x00000000 → 0x00000001 → 0x00000002 → 0x00000003
timeout: 0x00000003
关系:jiffies < timeout ✅

问题:在回绕发生前,由于jiffies > timeout,循环会提前退出

🔧 解决方案

Linux内核提供的安全宏

功能

等价的数学表达式

是否防回绕

time_after(a,b)

判断a是否在b之后

(long)(b) - (long)(a) < 0

time_before(a,b)

判断a是否在b之前

(long)(a) - (long)(b) < 0

time_after_eq(a,b)

a是否在b之后或相等

-

time_before_eq(a,b)

a是否在b之前或相等

-

原理揭秘:有符号整数比较
代码语言:javascript
复制
// 安全比较的原理
#define time_after(a,b) \
    ((long)((b) - (a)) < 0)

// 假设:
// a = 0x00000003 (回绕后的小值)
// b = 0xFFFFFFFE (回绕前的大值)

// 计算:(long)(b - a) = (long)(0xFFFFFFFE - 0x00000003)
//                   = (long)(0xFFFFFFFB)
// 转换为有符号:-5 < 0 ✅

图解原理

代码语言:javascript
复制
无符号视角(错误):
小值(3) < 大值(0xFFFFFFFE)?  ✓ 但这是回绕后的错误判断

有符号视角(正确):
将差值看作有符号数:
0xFFFFFFFB = -5(负数)
负数表示 a 在 b 之后 ✓
正确使用示例
代码语言:javascript
复制
// 正确的超时检查
unsigned long timeout = jiffies + HZ*5;

do {
    // 执行任务...
} while (time_before(jiffies, timeout));  // 使用安全宏

// 或者检查是否超时
if (time_after(jiffies, timeout)) {
    printk("已超时!\n");
}

📊 实战场景对比表

场景

错误代码示例

问题

正确代码示例

超时检查

if (jiffies > timeout)

回绕时提前触发

if (time_after(jiffies, timeout))

等待时间

while (current < end)

回绕时无限循环

while (time_before(current, end))

时间间隔

interval = now - last

回绕时得到巨大差值

使用time_after宏保护

定时器调度

直接比较时间戳

定时器调度错误

使用内核定时器API

🛡️ 最佳实践指南

1. 总是使用内核提供的宏
代码语言:javascript
复制
// ✅ 推荐
#include <linux/jiffies.h>
unsigned long start = jiffies;
unsigned long timeout = start + HZ*2;

// 安全等待
while (time_before(jiffies, timeout)) {
    // ...
}

// 检查是否超时
if (time_after(jiffies, timeout)) {
    handle_timeout();
}
2. 计算时间间隔的正确方法
代码语言:javascript
复制
// ✅ 安全的时间差计算
unsigned long interval = jiffies - last_time;
// 即使回绕,差值也会正确(无符号减法自动处理回绕)

// 但判断是否超过某个间隔要小心:
if (time_after(jiffies, last_time + HZ*5)) {
    // 超过5秒
}
3. 64位jiffies避免回绕
代码语言:javascript
复制
// 现代内核提供jiffies_64
extern u64 jiffies_64;

// 32位架构上也能安全访问
u64 current_jiffies = get_jiffies_64();

// 64位的回绕时间:
// 如果HZ=1000,回绕时间约5.8亿年!

📈 回绕影响总结表

系统配置

32位jiffies回绕周期

是否常见

建议

HZ=100

约497天

较常见

必须处理

HZ=250

约198天

常见

必须处理

HZ=1000

约49.7天

很常见

必须处理

64位jiffies

数亿年

无需考虑

基本安全

🎯 关键要点总结

  1. jiffies回绕是必然发生的:32位jiffies在系统运行约50天后必然回绕
  2. 问题本质:无符号整数的回绕特性与时间方向的矛盾
  3. 解决方案:使用内核提供的time_after/before等宏
  4. 安全原理:通过有符号数比较来正确判断时间先后
  5. 现代方案:使用64位jiffies(jiffies_64)可以避免此问题

🔍 实际调试技巧

如何检测回绕相关bug?
代码语言:javascript
复制
// 添加调试代码
#define DEBUG_JIFFIES_WRAPAROUND

#ifdef DEBUG_JIFFIES_WRAPAROUND
    if (jiffies < last_check) {
        printk("检测到jiffies回绕!\n");
    }
    last_check = jiffies;
#endif
模拟测试回绕
代码语言:javascript
复制
# 强制设置jiffies接近回绕点(仅用于测试)
echo $((0xFFFFFFF0)) > /sys/kernel/debug/jiffies_reset

💡 一句话记住

“处理jiffies,永远不要用<>直接比较,要使用time_beforetime_after宏!”

通过理解jiffies回绕的机制并正确使用内核提供的工具,可以写出健壮的、不会因系统长时间运行而崩溃的驱动程序或内核模块。

参考博客:jiffies回绕问题

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-01-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Jiffies回绕问题详解
    • 📖 什么是Jiffies?
      • 基本信息速览
    • ⏰ 为什么会出现回绕?
      • 核心原因:计数器溢出
      • 时间线图解
    • ⚠️ 回绕引发的问题
      • 场景对比:正常的超时检查 vs 回绕时的错误
    • 🔧 解决方案
      • Linux内核提供的安全宏
      • 原理揭秘:有符号整数比较
      • 正确使用示例
    • 📊 实战场景对比表
    • 🛡️ 最佳实践指南
      • 1. 总是使用内核提供的宏
      • 2. 计算时间间隔的正确方法
      • 3. 64位jiffies避免回绕
    • 📈 回绕影响总结表
    • 🎯 关键要点总结
    • 🔍 实际调试技巧
      • 如何检测回绕相关bug?
      • 模拟测试回绕
    • 💡 一句话记住
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档