[TOC]
kernel/time/alarmtimer.c
闹钟定时器(Alarm Timer)初始化:构建可挂起的定时器基础框架
本代码片段的核心功能是初始化Linux内核中的闹钟定时器(Alarm Timer)子系统。闹钟定时器的主要特点是它们能够在系统进入挂起(suspend)等低功耗状态后,依然能够到期并唤醒系统。此初始化函数负责建立管理这些定时器的核心数据结构,将它们与具体的时钟源(如CLOCK_REALTIME
和CLOCK_BOOTTIME
)关联起来,并注册相应的驱动以等待与硬件设备绑定。
实现原理分析
此初始化过程是闹钟定时器框架能够工作的基础,它通过配置一个预定义的alarm_bases
全局数组来为不同类型的闹钟定时器提供统一的管理接口。
- 配置时钟源: 函数首先为
REALTIME
和BOOTTIME
两个闹钟“基地”(alarm_bases
数组的元素)分别配置其clockid
和获取时间的函数指针。这使得上层代码可以通过ALARM_REALTIME
类型来设置一个基于“墙上时间”(wall-clock time)的定时器,或通过ALARM_BOOTTIME
类型设置一个基于系统启动时间的单调递增定时器。 - 通用初始化: 通过一个循环,为每一个闹钟基地初始化其定时器队列头(
timerqueue_init_head
)和自旋锁(spin_lock_init
)。定时器队列是一种高效的数据结构(通常是红黑树),用于按唤醒时间的先后顺序来组织所有的定时器事件。 - 硬件接口建立: 调用
alarmtimer_rtc_interface_setup
。这是一个关键步骤,它负责在闹钟定时器框架与底层的RTC(Real-Time Clock,实时时钟)设备之间建立联系。RTC是能够在CPU休眠时依然保持计时的硬件,是实现唤醒功能的物理基础。 - 驱动注册: 调用
platform_driver_register
将alarmtimer_driver
注册为一个平台驱动。这意味着闹钟定时器功能被实现为一个等待与平台设备(platform device)进行绑定的驱动程序。这个平台设备通常由底层的RTC驱动或者专门的定时器硬件驱动来注册,代表了能够提供闹钟功能的物理硬件。 - 错误处理: 函数包含了
goto
跳转的错误处理机制。如果在注册平台驱动时失败,程序会跳转到out_if
标签,执行alarmtimer_rtc_interface_remove
来撤销之前建立的RTC接口,保证系统状态的一致性。
代码分析
1 | // alarmtimer_init: 闹钟定时器代码的初始化函数。 |
闹钟定时器硬件后端发现与绑定
本代码片段展示了闹钟定时器(Alarm Timer)框架如何动态地发现并绑定一个合适的实时时钟(RTC)设备作为其硬件后端。其核心功能是创建一个“类接口”(class interface),监听系统中所有RTC设备的注册事件。一旦一个具备唤醒能力的RTC设备出现,此代码就会尝试获取该设备,并注册一个名为”alarmtimer”的平台设备,从而将通用的闹钟定时器服务与具体的物理硬件连接起来。
实现原理分析
该机制是Linux设备模型中“胶水层”代码的典型范例,它利用class_interface
机制来解耦通用子系统与具体设备驱动。
类接口注册 (
alarmtimer_rtc_interface_setup
):- 此函数是整个机制的入口。它初始化一个
class_interface
结构体,将其与rtc_class
(所有RTC设备都属于这个类)进行关联。 - 通过调用
class_interface_register
,它向内核设备模型注册一个回调。其效果是:每当系统中有一个属于rtc_class
的设备被添加时,内核就会自动调用该接口中指定的.add_dev
函数,即alarmtimer_rtc_add_device
。
- 此函数是整个机制的入口。它初始化一个
设备添加处理 (
alarmtimer_rtc_add_device
):- 这是回调的核心逻辑,当一个新的RTC设备被注册时,此函数被调用。
- 单例检查: 首先检查全局指针
rtcdev
是否已经被赋值。闹钟定时器框架设计为只使用一个RTC设备作为后端,因此如果已经绑定了一个,则直接返回-EBUSY
。 - 能力检查: 对传入的RTC设备进行严格的能力验证。
a.test_bit(RTC_FEATURE_ALARM, rtc->features)
: 检查该RTC设备是否声称支持硬件闹钟功能。
b.device_may_wakeup(rtc->dev.parent)
: 检查该RTC设备是否有能力唤醒系统。这是实现可挂起定时器的物理前提。
任何一项检查失败,都表示此RTC不适合作为闹钟定时器的后端。 - 平台设备注册: 如果能力检查通过,它会调用
platform_device_register_data
注册一个新的平台设备,名称为”alarmtimer”。这一步是关键的抽象转换:它将一个具体的RTC设备(如rtc0
)的存在,转化为了一个更通用的”alarmtimer”设备的存在。这个新创建的”alarmtimer”平台设备随后就可以与上一段代码分析中注册的alarmtimer_driver
平台驱动进行匹配和绑定。 - 独占式获取: 在一个自旋锁的保护下,它再次检查
rtcdev
,并尝试获取RTC驱动模块和设备的引用计数(try_module_get
,get_device
)。这确保了在闹钟定时器使用期间,底层的RTC驱动不会被卸载。成功后,将全局rtcdev
指向该RTC设备。
接口注销 (
alarmtimer_rtc_interface_remove
):- 这是一个清理函数,它调用
class_interface_unregister
来移除之前注册的回调,停止监听新的RTC设备事件。
- 这是一个清理函数,它调用
代码分析
1 | // alarmtimer_rtc_add_device: 当一个RTC设备被添加到系统时,此回调函数被调用。 |
系统挂起处理与唤醒闹钟设置
本代码片段是 Linux 内核闹钟定时器(alarmtimer)子系统的电源管理核心。其主要功能是在系统进入挂起(suspend)状态前,通过注册的 alarmtimer_suspend
回调函数,精确计算出下一次需要唤醒系统的时间点,并利用绑定的硬件实时时钟(RTC)设置一个物理唤醒闹钟。在系统恢复(resume)时,通过 alarmtimer_resume
回调函数清理该闹钟。这套机制确保了基于闹钟定时器的定时任务(如 CLOCK_REALTIME_ALARM
)能够在系统深度睡眠时依然被准确唤醒和执行。
实现原理分析
该功能通过标准的平台驱动(platform_driver)模型,将闹钟定时器的逻辑与内核的电源管理(Power Management, PM)框架进行挂钩。
驱动与设备绑定:
alarmtimer_driver
是一个平台驱动,其名称为"alarmtimer"
。在系统初始化阶段,当alarmtimer_rtc_add_device
函数成功发现并绑定一个合适的 RTC 设备后,会创建一个同名为"alarmtimer"
的平台设备。内核的设备模型会自动将此驱动与该设备进行匹配。- 匹配成功后,
alarmtimer_driver
中定义的dev_pm_ops
(设备电源管理操作集)即被激活。
系统挂起处理 (
alarmtimer_suspend
):- 当系统准备进入挂起状态时,内核的 PM 核心会调用所有已注册驱动的
.suspend
回调,alarmtimer_suspend
随之被执行。 - 获取最早到期时间: 函数的核心任务是找出所有已注册的闹钟中,最先到期的那一个。它会遍历
alarm_bases
数组中所有的闹钟类型(ALARM_REALTIME
和ALARM_BOOTTIME
)。 - 查询定时器队列: 对于每种闹钟类型,它会持有该基座的自旋锁,并调用
timerqueue_getnext
从其红黑树实现的定时器队列中,获取到期时间最早的节点。 - 计算最小时间差: 它计算出每个队列中最早到期时间与当前时间的差值(delta),并与一个全局最小值
min
进行比较,持续更新min
以保存全局最小的时间差。 - 安全边界检查: 如果计算出的最小时间差
min
小于2秒,这意味着有一个闹钟即将到期。为了确保该闹钟能够被精确处理,函数会调用pm_wakeup_event
来短暂地阻止系统挂起,并返回-EBUSY
。这使得系统有时间在活动状态下处理这个即将到期的软件定时器。 - 硬件闹钟编程: 如果
min
大于安全边界,函数将执行以下步骤来设置硬件唤醒:
a.rtc_timer_cancel
: 首先取消任何可能存在的旧的 RTC 闹钟。
b.rtc_read_time
: 读取当前 RTC 硬件的精确时间。
c.rtc_bound_alarmtime
: 考虑到某些 RTC 硬件可能不支持设置过长时间的闹钟,此函数会将min
调整到硬件支持的最大范围内。
d.rtc_timer_start
: 将当前 RTC 时间与调整后的min
相加,得到未来的绝对唤醒时间,并调用此函数将该时间编程到 RTC 硬件的闹钟寄存器中。
- 当系统准备进入挂起状态时,内核的 PM 核心会调用所有已注册驱动的
系统恢复处理 (
alarmtimer_resume
):- 当系统从挂起状态被唤醒后(无论是由 RTC 闹钟还是其他唤醒源),PM 核心会调用
.resume
回调。 alarmtimer_resume
的逻辑非常简单:它会调用rtc_timer_cancel
来无条件地取消之前设置的 RTC 硬件闹钟。这是一个必要的清理步骤,因为系统已经被唤醒,后续的定时将重新由高精度的软件定时器(hrtimer)接管。
- 当系统从挂起状态被唤醒后(无论是由 RTC 闹钟还是其他唤醒源),PM 核心会调用
代码分析
1 | // alarmtimer_get_rtcdev: 获取并返回当前被选定用于唤醒闹钟的rtc_device结构体指针。 |