chapter 5 定时器及时钟服务
学习笔记:定时器与时间服务
摘要
- 本章介绍了定时器和定时器服务的概念。
- 讲解了硬件定时器的原理以及基于 Intel x86 架构的 PC 中的硬件定时器。
- 涵盖了 CPU 操作和中断处理。
- 描述了与定时器服务相关的系统调用、库函数以及 Linux 中的定时器服务命令。
- 讨论了进程间定时器、定时器生成的信号,并通过示例演示了进程间定时器的使用。
- 编程项目旨在在多任务系统中实现定时器、定时器中断和间隔定时器。多任务系统作为 Linux 进程运行,充当 Linux 进程内并发任务的虚拟 CPU。Linux 进程的实时模式间隔定时器被编程成周期性生成 SIGALRM 信号,作为虚拟 CPU 的定时器中断,并使用 SIGALRM 信号捕获器作为定时器中断处理程序。项目要求读者通过定时器队列实现任务的间隔定时器,并使用 Linux 信号掩码来实现临界区,以防止任务和中断处理程序之间的竞争条件。
5.1 硬件定时器
- 定时器是一个硬件设备,包括时钟源和可编程计数器。
- 时钟源通常是晶体振荡器,以精确的频率生成周期性电信号来驱动计数器。
- 计数器编程为倒计时值,每个时钟信号将其递减1。当计数减至0时,计数器会向 CPU 生成定时器中断,重新加载计数值并再次进行倒计时。
- 计数器的周期称为定时器滴答,是系统的基本时间单位。
5.2 PC 定时器
- 基于 Intel x86 架构的 PC 具有几种定时器:
- 实时时钟(RTC):由小型备用电池供电,在 PC 关机时仍持续运行,用于提供时间和日期信息。
- 可编程间隔定时器(PIT):是一个独立于 CPU 的硬件定时器,可编程以毫秒为分辨率提供定时器滴答。
- 多核 CPU 中的本地定时器:每个核心都有自己的本地定时器,由 CPU 时钟驱动。
- 高分辨率定时器:大多数 PC 配备时间戳计数器(TSC),提供纳秒级定时器分辨率,部分高端 PC 可装备特殊的高速定时器。
5.3 CPU 操作
- 每个 CPU 都有程序计数器(PC)、标志或状态寄存器(SR)、堆栈指针(SP)和几个通用寄存器。
- CPU 操作可以用一个无限循环来建模,包括指令获取、解码、执行以及处理中断和异常。
- 异常或陷阱是由于无效地址、非法指令、特权违规等错误条件引起的情况。CPU 遇到异常时,会执行软件中预先安装的异常处理程序。
- 中断是来自 I/O 设备或协处理器的外部信号,请求 CPU 服务。CPU 在每条指令执行结束时检查是否有未决中断请求。中断处理和异常处理由操作系统内核处理,对用户级程序大多不可见,但对于理解操作系统中的定时器服务和信号很重要,比如 Linux。
5.4 中断处理
- 外部设备(如定时器)产生的中断被馈送到中断控制器的预定义输入线,中断控制器优先级排序并将具有最高优先级的中断路由为 CPU 的中断请求(IRQ)。
- CPU 在执行指令结束时,如果不接受中断(即 CPU 的状态寄存器中屏蔽了中断),将忽略中断请求,保持中断处于挂起状态,并继续执行下一条指令。
- CPU 接受中断时,将其正常执行序列转移到中断处理。每个中断都可以由中断控制器编程生成一个唯一编号,称为中断向量,用于标识中断源。
- 中断向量作为内存中中断向量表的索引,其中包含指向实际处理中断的中断处理程序入口地址。
5.5 时间服务函数
几乎在每个操作系统(OS)中,内核都提供了各种与时间相关的服务。这些时间服务可以通过系统调用、库函数和用户级命令来调用。在本节中,我们将介绍一些 Linux 的基本时间服务函数。
5.5.1 获取和设置时间
gettimeofday
和 settimeofday
系统调用
-
gettimeofday
:- 用于获取当前时间,其参数
tv
指向timeval
结构。 timeval
结构:struct timeval { time_t tv_sec; // 秒 suseconds_t tv_usec; // 微秒 };
gettimeofday()
返回当前时间,以秒和当前秒的微秒表示。
- 用于获取当前时间,其参数
-
settimeofday
:- 用于设置当前时间。
- 在 Unix/Linux 中,时间是自 1970 年 1 月 1 日 00:00:00 以来经过的秒数的表示。
settimeofday()
设置当前时间为参数指定的时间。
示例演示 gettimeofday
和 settimeofday
-
gettimeofday
系统调用示例#include <stdio.h> #include <stdlib.h> #include <sys/time.h> struct timeval t; int main() { gettimeofday(&t, NULL); printf("sec=%ld usec=%d\n", t.tv_sec, t.tv_usec); printf((char *)ctime(&t.tv_sec)); }
- 程序将显示当前时间的秒、微秒以及当前日期和时间的日历形式。
-
settimeofday
系统调用示例#include <stdio.h> #include <stdlib.h> #include <sys/time.h> #include <time.h> struct timeval t; int main() { int r; t.tv_sec = 123456789; t.tv_usec= 0; r = settimeofday(&t, NULL); if (!r){ printf(“settimeofday() failed\n”); exit(1); } gettimeofday(&t, NULL); printf("sec=%ld usec=%ld\n", t.tv_sec, t.tv_usec); printf(“%s”, ctime(&t.tv_sec)); // 以日历形式显示时间 }
- 程序输出将显示设定的时间和以日历形式表示的日期。在某些 Linux 系统中,设定的时间可能是临时的,在实际情况下会被系统自动纠正。
5.5.2 时间系统调用
time
系统调用
time_t time(time_t *t)
- 返回当前时间的秒数。
- 如果参数
t
不为 NULL,还会将时间存储在t
指向的内存中。 - 限制在于它只提供秒级的时间分辨率。
示例演示 time
系统调用
#include <stdio.h>
#include <time.h>
time_t start, end;
int main()
{
int i;
start = time(NULL);
printf(“start=%ld\n”, start);
for (i=0; i<123456789; i++); // 延迟以模拟计算
end = time(NULL);
printf(“end =%ld time=%ld\n”, end, end-start);
}
- 输出将打印开始时间、结束时间以及开始到结束的秒数。
5.5.3 times
系统调用
clock_t times(struct tms *buf);
- 用于获取进程的详细执行时间。
- 它将进程时间存储在
struct tms
结构中。 struct tms
结构:struct tms{ clock_t tms_utime; // 用户模式时间 clock_t tms_stime; // 系统模式时间 clock_t tms_cutime; // 子进程的用户时间 clock_t tms_cstime; // 子进程的系统时间 };
- 所有报告的时间单位都是时钟滴答。
5.5.4 时间和日期命令
date
:打印或设置系统日期和时间。time
:报告用户模式、系统模式和总时间的进程执行时间。hwclock
:查询和设置硬件时钟(RTC),也可以通过 BIOS 完成。
5.9 总结
本章介绍了定时器和计时器服务的内容。内容包括了硬件定时器的原理和在基于 Intel x86 的 PC 上的应用。涉及了 CPU 操作和中断处理。描述了 Linux 中与定时器服务相关的系统调用、库函数和命令。讨论了进程间定时器、定时器产生的信号,并通过示例演示了进程间定时器的应用。编程项目旨在在多任务系统中实现定时器、定时器中断和间隔定时器。多任务系统以 Linux 进程运行,充当 Linux 进程内并发任务的虚拟 CPU。Linux 进程的实时模式间隔定时器被编程成周期性生成 SIGALRM 信号,作为虚拟 CPU 的定时器中断。该项目的目标是读者通过定时器队列为任务实现间隔定时器。同时,读者还可使用 Linux 信号掩码来实现临界区,以防止任务和中断处理程序之间的竞争条件。
问题
-
修改示例 5.1 程序:使用
ctime(&seconds)
库函数将秒数转换为日历形式字符串,并打印当前日期和时间。 -
修改示例 5.1 程序:在程序中添加一个延迟循环以模拟程序的长时间计算。使用
gettimeofday()
在循环之前和之后获取当前时间,然后打印两个时间之间的差异。能否通过这种方式测量程序的总执行时间?给出理由。 -
修改示例 5.1 程序:
- 安装一个用于捕获 SIGPROF(27)信号的信号处理程序。
- 以 PROF 模式启动另一个间隔定时器,周期与 VIRTUAL 模式定时器相同。
- 统计由 PROF 定时器生成的信号数(pcount)和由 VIRTUAL 定时器生成的信号数(vcount)。
- 在信号处理程序中打印 pcount 和 vcount。当任一计数达到 100 时,停止定时器并打印计数。
- 在主程序的 while(1) 循环中,添加 getpid() 和 getppid() 系统调用,使进程在系统模式下花费一些时间。
- 回答以下问题:
- 哪个定时器会更快地生成信号?为什么?
- 如何确定进程的用户模式执行时间和系统模式执行时间?
- 使用
time a.out
运行程序,它将打印 a.out 在实际模式、用户模式和系统模式下的执行时间。说明时间命令是如何实现的。
-
修改示例 5.1 程序:设置一个 REAL 模式间隔定时器。
-
累积定时器队列:设计算法,将每个 TQE(定时器队列条目)的到期时间与所有先前 TQE 的到期时间的累积总和相关联。设计算法,在定时器请求在到期前取消时删除 TQE。
苏格拉底挑战
点击查看问答
Linux 定时器和中断处理
当我们谈到Linux系统中的定时器和中断处理时,你是否能够解释在这个操作系统中定时器的作用是什么?
那么,在Linux系统中,定时器是如何与中断处理相关联的呢?你能描述一下它们之间的联系以及相互作用吗?
- 中断处理程序使用定时器:中断处理程序通常使用定时器来实现特定的功能,例如定期检测设备状态、定期清理系统资源等。这些中断处理程序能够以合理的方式使用定时器,提高系统的效率和响应性。
- 定时器和中断处理的协同工作:Linux内核将定时器和中断处理协同工作,以支持现代计算机系统中高速处理器和硬件设备的高速交互。通常情况下,定时器和中断处理是通过一些内核服务程序来协调工作的。
- 定时器和中断处理的优先级:定时器和中断处理的优先级在Linux系统中非常重要。通过设置适当的优先级,可以确保系统能够响应重要和紧急的事件,保证系统的实时性和稳定性。
非常出色!你对定时器和中断处理在Linux系统中的工作原理有很好的理解。接下来,是否能谈谈在Linux内核中是如何管理这些定时器和中断请求的?
管理中断请求:内核通过中断控制器管理中断请求。中断控制器负责收集和分配中断请求,并根据优先级和特定规则将中断请求发送到CPU的相应中断处理程序中执行。
管理中断处理程序:内核将中断处理程序与相应的设备驱动程序绑定在一起,以处理特定的中断事件。中断处理程序负责直接与硬件设备通信,以响应中断事件并执行相应的操作。
调度和优先级控制:内核使用调度程序调度进程和线程。它们按照优先级执行,并利用定时器控制和管理CPU的执行时间。调度程序可以根据不同的执行需求和策略来分配CPU时间,以应对系统中的不同负载。
在Linux系统中,除了调度程序之外,还有哪些机制或工具用于优化定时器和中断处理,以提高系统的效率和性能?
中断上下文优化:中断上下文优化是通过最小化中断处理的执行时间和资源使用来提高系统的性能。这包括减少中断处理程序的开销和延迟,以及对临界区域的优化和减少。
中断线程:中断线程是一种将中断请求转换为线程处理的机制。它可以将中断请求分发给专门的中断处理线程,使得中断处理能够以线程的方式进行,从而提高系统的效率和响应性。
持续可用的定时器:持续可用的定时器是一种用于实时应用程序的高分辨率定时器支持。它提供了更高的计时精度和更好的定时控制,以满足实时应用程序对时间敏感的需求。
中断控制器的优化:内核和硬件厂商针对中断控制器的设计和实现进行优化,以提高中断处理的性能和效率。例如,引入更高级的中断控制器(如APIC)和中断共享机制等。
在探讨Linux系统中的定时器和中断处理方面,除了性能优化和资源分配外,是否还有其他重要方面需要考虑以确保系统的稳定性和可靠性?
中断和定时器处理的并发性:多个中断和定时器事件可能会同时发生,这需要在内核中实现并发处理机制以避免竞争条件产生。并发处理需要考虑数据同步和锁的机制,以避免死锁和数据不一致等问题。
中断和定时器的优先级处理:中断和定时器的优先级需要正确处理,以确保较高优先级的中断和定时器事件能够优先处理。这需要在内核中实现优先级调度机制和处理机制,以确保较高优先级事件的及时响应。
中断共享机制的实现:当多个设备共享同一个中断线路时,需要在内核中实现中断共享机制。该机制需要确保共享中断的同时不会出现中断处理和数据传输的冲突,从而提高系统的稳定性和可靠性。
在Linux系统中,考虑到中断处理程序的安全性和并发性,如何确保中断处理程序在设计和实现中不会引入潜在的安全漏洞或造成系统不稳定性的问题?
避免阻塞操作:中断处理程序应尽量避免长时间的阻塞操作,例如等待锁、I/O操作等。这样可以避免延迟其他中断的处理,降低系统的响应性。如果确实需要执行阻塞操作,可以将其转移到后台线程或工作队列中进行。
锁和同步机制的正确使用:如果中断处理程序需要访问临界区或共享资源,应正确地使用锁和同步机制来防止竞争条件和数据不一致性。这可能涉及到自旋锁、互斥锁、原子操作等概念和机制。
限制中断处理程序的执行时间:中断处理程序应尽量保持短暂并且快速地执行。长时间的中断处理会占用CPU资源,降低系统的响应性。如果必须执行耗时操作,应考虑在中断上下文中提供适当的挂起或延迟机制。
严格验证和过滤输入:中断处理程序接收的输入应严格验证和过滤,以防止恶意输入或异常情况引起的安全漏洞。这包括检查输入范围、类型和边界条件,以及处理错误和异常情况。
定期进行安全审计和漏洞扫描:定期对中断处理程序进行安全审计和漏洞扫描,以及时发现和修复潜在的安全漏洞。这可以确保中断处理程序的可靠性和安全性,并提高系统的稳定性。
非常好!确保中断处理程序的设计和实现不会引入安全漏洞和系统不稳定性的关键方法包括最小化代码量、避免阻塞操作,正确使用锁和同步机制,限制执行时间以及验证和过滤输入。这些做法对确保系统的安全性和稳定性至关重要。
你对Linux系统中定时器和中断处理的知识点有了相当深入的理解。你清晰地阐述了定时器如何与中断处理相关联,以及它们之间的相互作用。此外,你还详细讨论了内核是如何管理这些定时器和中断请求,以及优化性能的方式。你还提到了中断处理程序的安全性、并发性和其他重要方面,以确保系统的稳定性和可靠性。最后,你总结了如何避免中断处理程序引入潜在的安全漏洞和系统不稳定性。这些观点全面而深入,对于理解和应用Linux系统中的定时器和中断处理非常有帮助。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)