Linux 的实时时钟 (RTC) 驱动程序¶
当 Linux 开发人员谈论“实时时钟”时,他们通常指的是跟踪挂钟时间并有电池支持的设备,即使系统断电也能工作。这种时钟通常不会跟踪本地时区或夏令时 -- 除非它们与 MS-Windows 双启动 -- 而是会被设置为协调世界时 (UTC,以前称为“格林威治标准时间”)。
最新的非 PC 硬件倾向于只计算秒数,就像 time(2) 系统调用报告的那样,但 RTC 也非常普遍地使用公历和 24 小时制来表示时间,如 gmtime(3) 报告的那样。
Linux 有两个基本兼容的用户空间 RTC API 系列,您可能需要了解
/dev/rtc ... 是 PC 兼容系统提供的 RTC,因此它不适合移植到非 x86 系统。
/dev/rtc0, /dev/rtc1 ... 是一个框架的一部分,该框架受到所有系统上的各种 RTC 芯片的支持。
程序员需要理解 PC/AT 功能并非总是可用,并且某些系统可以做更多的事情。也就是说,RTC 在两个 RTC 框架中都使用相同的 API 来发出请求(当然,使用不同的文件名),但硬件可能不提供相同的功能。例如,并非每个 RTC 都连接到 IRQ,因此它们无法发出警报;并且标准 PC RTC 只能发出最多 24 小时后的警报,而其他硬件可能能够在未来一个世纪内的任何时间安排一个警报。
旧的 PC/AT 兼容驱动程序:/dev/rtc¶
所有 PC(甚至 Alpha 机器)都内置了实时时钟。通常它们内置在计算机的芯片组中,但有些可能实际上在电路板上有一个 Motorola MC146818(或克隆)。这是在您的计算机关闭时保持日期和时间的时钟。
ACPI 已经标准化了 MC146818 功能,并在某些方面对其进行了扩展(启用更长的警报周期,以及从休眠状态唤醒)。该功能未在旧驱动程序中公开。
但是,它也可以用于生成从慢速 2Hz 到相对快速的 8192Hz 的信号,以 2 的幂递增。这些信号由中断号 8 报告。(哦!所以这就是 IRQ 8 的用途...)它也可以用作 24 小时警报,在警报响起时发出 IRQ 8。警报也可以被编程为仅检查三个可编程值中的任何子集,这意味着可以将其设置为在每个小时的第 30 分钟的第 30 秒响起,例如。时钟也可以设置为在每次时钟更新时生成中断,从而生成 1Hz 信号。
中断通过 /dev/rtc(主设备号 10,次设备号 135,只读字符设备)以 unsigned long 的形式报告。低字节包含引发的中断类型(更新完成、警报已响或周期性),其余字节包含自上次读取以来的中断数。如果启用了 /proc 文件系统,则通过伪文件 /proc/driver/rtc 报告状态信息。该驱动程序内置锁定,因此一次只允许一个进程打开 /dev/rtc 接口。
用户进程可以通过对 /dev/rtc 执行 read(2) 或 select(2) 来监视这些中断 -- 它们都会阻塞/停止用户进程,直到收到下一个中断。这对于诸如相当高频率的数据采集之类的任务非常有用,在这种情况下,人们不想通过轮询 gettimeofday 等等来消耗 100% 的 CPU。
在高频率或高负载下,用户进程应检查自上次读取以来收到的中断数,以确定是否存在任何中断“堆积”。仅供参考,在 /dev/rtc 上运行紧密读取循环的典型 486-33 将开始在高于 1024Hz 的频率下偶尔出现中断堆积(即,自上次读取以来 > 1 个 IRQ 事件)。因此,您确实应该检查您读取的值的高字节,尤其是在高于正常定时器中断的频率(即 100Hz)时。
只有 root 才能编程和/或启用大于 64Hz 的中断频率。这可能有点保守,但我们不希望邪恶的用户在慢速 386sx-16 上生成大量 IRQ,这可能会对性能产生负面影响。可以通过将不同的值写入 /proc/sys/dev/rtc/max-user-freq 来更改此 64Hz 限制。请注意,中断处理程序只有几行代码,以尽量减少这种影响的可能性。
此外,如果内核时间与外部源同步,内核将每 11 分钟将时间写回 CMOS 时钟。在执行此操作的过程中,内核会短暂关闭 RTC 周期性中断,因此如果您正在进行认真的工作,请注意这一点。如果您不将内核时间与外部源(通过 ntp 或其他方式)同步,那么内核将不会干预 RTC,允许您独占访问该设备以用于您的应用程序。
警报和/或中断频率通过 ./include/linux/rtc.h 中列出的各种 ioctl(2) 调用编程到 RTC 中。与其写 50 页描述 ioctl() 等等,不如包含一个小的测试程序,演示如何使用它们,并演示驱动程序的功能,这可能更有用。对于那些有兴趣编写将使用此驱动程序的应用程序的人来说,这可能更有用。请参阅本文档末尾的代码。
(最初的 /dev/rtc 驱动程序由 Paul Gortmaker 编写。)
新的可移植 “RTC 类” 驱动程序:/dev/rtcN¶
由于 Linux 支持许多非 ACPI 和非 PC 平台,其中一些平台具有多个 RTC 样式的时钟,因此它需要一个比期望每个系统上都有一个电池支持的 MC146818 克隆更具可移植性的解决方案。因此,已经定义了一个新的“RTC 类”框架。它提供了三个不同的用户空间接口
/dev/rtcN ... 与旧的 /dev/rtc 接口非常相似
/sys/class/rtc/rtcN ... sysfs 属性支持对某些 RTC 属性的只读访问。
/proc/driver/rtc ... 系统时钟 RTC 可能会使用 procfs 接口公开自身。如果没有系统时钟的 RTC,则默认使用 rtc0。此处显示的信息(目前)比通过 sysfs 显示的信息更多。
RTC 类框架支持各种 RTC,从集成到可嵌入片上系统 (SOC) 处理器中的 RTC 到使用 I2C、SPI 或其他总线与主机 CPU 通信的离散芯片。甚至还支持 PC 样式的 RTC ... 包括通过 ACPI 在较新的 PC 上公开的功能。
新的框架还消除了“每个系统一个 RTC”的限制。例如,低功耗电池支持的 RTC 可能是一个离散的 I2C 芯片,但高功能 RTC 集成到 SOC 中。该系统可能会从离散 RTC 读取系统时钟,但由于其更高的功能,将其用于所有其他任务。
查看 tools/testing/selftests/rtc/rtctest.c 以获取 ioctl 接口的使用示例。