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 使用相同的 API 在两个 RTC 框架中发出请求(当然使用不同的文件名),但硬件可能不提供相同的功能。例如,并非每个 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,只读字符设备)报告。低字节包含引发的中断类型(更新完成、警报响起或定期),其余字节包含自上次读取以来的中断数。如果启用了 /proc 文件系统,则状态信息通过伪文件 /proc/driver/rtc 报告。该驱动程序内置锁定,因此一次只允许一个进程打开 /dev/rtc 接口。
用户进程可以通过在 /dev/rtc 上执行 read(2) 或 select(2) 来监视这些中断 - 这两个操作都会阻塞/停止用户进程,直到收到下一个中断。这对于诸如需要合理高频率数据采集,而又不希望通过轮询 gettimeofday 等来占用 100% CPU 的情况很有用。
在高频率或高负载下,用户进程应检查自上次读取以来收到的中断数,以确定是否存在任何中断“堆积”。仅供参考,运行紧凑的读取循环的典型 486-33 在 /dev/rtc 上开始遭受偶尔的中断堆积(即自上次读取以来 > 1 个 IRQ 事件),频率高于 1024Hz。因此,您确实应该检查读取值的高字节,尤其是在高于正常定时器中断频率(即 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 读取系统时钟,但会因其更高的功能而将集成的 RTC 用于所有其他任务。
有关 ioctl 接口的示例用法,请查看 tools/testing/selftests/rtc/rtctest.c。