3. 内核定时器核心数据结构:struct timer_list 成员详解与回调函数原型

在 Linux 内核中,struct timer_list 是驱动开发者使用标准内核定时器(低精度定时器,基于 jiffies)的核心数据结构。理解其每个成员的含义、生命周期以及回调函数的编写规范,是正确使用内核定时器的基础。本章将深入剖析该结构体,并给出标准的回调函数原型。

3.1 struct timer_list 结构体全景

该结构体定义在 <linux/timer.h> 中。不同内核版本(如 4.x 与 5.x/6.x)在成员命名上略有差异,但核心逻辑一致。以下以较新的内核版本(5.x/6.x)为例进行说明。

成员名称 类型 描述 驱动开发者职责
entry struct list_head 用于将定时器挂接到内核定时器链表(bucket)的链表节点。 由内核定时器框架管理,驱动通常不直接操作。
expires unsigned long 定时器的到期时间,以 jiffies 为单位。表示定时器期望在哪个 jiffies 时刻触发。 必须设置:通常使用 jiffies + delay 或 msecs_to_jiffies(delay_ms) 赋值。
function void (*function)(struct timer_list *) 定时器到期时被内核调用的回调函数指针。 必须设置:指向驱动中定义的定时器处理函数。
flags unsigned int 控制定时器行为的标志位。例如 TIMER_IRQSAFE、TIMER_DEFERRABLE 等。 通常设置为 0 或按需设置特定标志。
slack unsigned int 定时器允许的松弛量(以 jiffies 为单位)。用于内核合并定时器以减少唤醒次数,提升系统性能。 通常由内核自动计算,驱动可调用 timer_setup() 时指定。
debug struct timer_debug_entry 仅在启用了 CONFIG_TIMER_STATS 或调试选项时存在,用于记录定时器注册信息。 驱动开发者无需关心,主要用于内核调试。
版本兼容提示: 在较老的内核版本(如 3.x/4.x)中,struct timer_list 包含 data(unsigned long)成员,用于向回调函数传递参数。新版本中该成员已被移除,回调函数直接通过 struct timer_list * 指针获取上下文,推荐使用 container_of 宏或 from_timer 宏来访问包含该定时器的自定义结构体。

3.2 回调函数原型与编写规范

定时器回调函数是驱动开发者实现的核心逻辑,其函数原型固定如下:

void my_timer_callback(struct timer_list *t);

该函数由内核在软中断(SoftIRQ)上下文中调用,因此必须遵守以下严格的约束条件:

  • 不可睡眠:回调函数运行在原子上下文中,禁止调用任何可能导致睡眠的函数(如 msleep()、wait_event()、kmalloc(GFP_KERNEL)、copy_from_user() 等)。
  • 不可持有互斥锁:不能获取 mutex 或 semaphore,但可以使用自旋锁(spin_lock)。
  • 执行时间应尽可能短:长时间占用 CPU 会阻塞其他软中断和定时器处理,导致系统延迟增加。
  • 可重入性:如果定时器被设置为周期性触发,回调函数可能在上一轮执行未完时再次被调用,需确保逻辑正确。
最佳实践: 回调函数通常只做快速的状态检查、标志位设置、唤醒等待队列或提交工作队列(workqueue)。将耗时的业务逻辑(如数据拷贝、硬件操作)推迟到工作队列或内核线程中执行。

3.3 从回调函数获取驱动上下文:from_timer 宏

由于新版本内核移除了 data 成员,驱动开发者需要一种机制从 struct timer_list * 指针反推出包含它的自定义结构体。内核提供了 from_timer 宏,其本质是对 container_of 的封装。

// 驱动自定义结构体
struct my_device {
    int id;
    struct timer_list my_timer;
    // ... 其他成员
};

// 定时器回调函数
void my_timer_callback(struct timer_list *t) {
    // 从 timer_list 指针获取 my_device 结构体指针
    struct my_device *dev = from_timer(dev, t, my_timer);

    // 现在可以安全地访问 dev->id 等成员
    pr_info("Timer expired for device %d\n", dev->id);

    // 如果需要重新启动定时器,在此处调用 mod_timer()
}

from_timer 宏的原型为:

#define from_timer(var, callback_timer, timer_field_name) \
    container_of(callback_timer, typeof(*(var)), timer_field_name)

它接受三个参数:

  • var:用于接收结果的自定义结构体指针变量名。
  • callback_timer:回调函数中传入的 struct timer_list * 指针。
  • timer_field_name:struct timer_list 在自定义结构体中的成员名称。

3.4 内核定时器生命周期与状态转换

下图展示了内核定时器从初始化到触发的完整生命周期,以及关键 API 的作用位置。

内核定时器生命周期与状态转换 未初始化 已初始化 已激活(挂入链表) 到期触发(回调执行) 已删除 timer_setup() add_timer() / mod_timer() jiffies 达到 expires del_timer() mod_timer() 重新激活 说明:回调执行后定时器默认变为“已删除”状态。若需周期性触发,必须在回调函数中调用 mod_timer() 重新设置 expires。

3.5 关键 API 与成员操作速查

API / 操作 作用 影响的主要成员
timer_setup(timer, callback, flags) 初始化 struct timer_list,设置回调函数和标志。 function, flags, entry
add_timer(timer) 将已初始化的定时器激活并加入内核定时器链表。 expires(需提前设置), entry
mod_timer(timer, expires) 修改定时器的到期时间。如果定时器未激活,则激活它。 expires, entry
del_timer(timer) 从内核定时器链表中删除一个已激活的定时器。 entry
del_timer_sync(timer) 删除定时器,并确保在 SMP 系统上回调函数不会正在其他 CPU 上执行。 entry
timer_pending(timer) 检查定时器是否正在等待执行(即是否已激活)。 读取 entry 状态
关键注意事项:
  • 在调用 add_timer() 或 mod_timer() 之前,必须确保 expires 成员已被正确赋值。
  • 在驱动卸载(remove)或模块退出时,必须调用 del_timer_sync() 确保定时器被完全删除且回调函数不再运行,否则可能导致访问已释放的内存。
  • 回调函数中不能直接调用 add_timer() 来重新启动定时器,应使用 mod_timer()。

3.6 完整初始化与使用示例片段

#include <linux/timer.h>
#include <linux/jiffies.h>

struct my_driver_data {
    int sample_value;
    struct timer_list poll_timer;
};

static void poll_timer_callback(struct timer_list *t)
{
    struct my_driver_data *data = from_timer(data, t, poll_timer);

    // 快速处理:读取硬件状态,更新 data->sample_value
    // 注意:此处不可睡眠

    // 重新启动定时器,实现周期性轮询(例如每 100ms)
    mod_timer(&data->poll_timer, jiffies + msecs_to_jiffies(100));
}

static int my_driver_probe(struct platform_device *pdev)
{
    struct my_driver_data *data;

    data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
    if (!data)
        return -ENOMEM;

    // 1. 初始化定时器
    timer_setup(&data->poll_timer, poll_timer_callback, 0);

    // 2. 设置首次到期时间(例如 100ms 后首次触发)
    data->poll_timer.expires = jiffies + msecs_to_jiffies(100);

    // 3. 激活定时器
    add_timer(&data->poll_timer);

    // ... 其他初始化
    return 0;
}

static int my_driver_remove(struct platform_device *pdev)
{
    struct my_driver_data *data = platform_get_drvdata(pdev);

    // 4. 删除定时器,确保安全
    del_timer_sync(&data->poll_timer);

    // ... 其他清理
    return 0;
}