20191324读书笔记8
第五章 定时器及时钟服务
硬件定时器
- 定时器是由时钟源和可编程计数器组成的硬件设备。时钟源通常是一个晶体振荡器,会产生周期性电信号,以精确的频率驱动计数器。使用一个倒计时值对计数器进行编程,每个时钟信号减1。当计数减为0时,计数器向CPU生成一个定时器中断,将计数值重新加载到计数器中,并重复倒计时。计数器周期称为定时器刻度,是系统的基本计时单元。
个人计算机定时器
- 实时时钟(RTC) : RTC由一个小型备用电池供电。即使在个人计算机关机时,它 也能连续运行。它用于实时提供时间和日期信息。当Linux启动时,它使用RTC更新系统 时间变量.以与当前时间保持一致。在所有类Unix系统中,时间变量是一个长整数,包含 从1970年1月1日起经过的秒数。
- 可编程间隔定时器(PIT): PIT是与CPU分离的一个硬件定时器。可 对它进行编程,以提供以毫秒为单位的定时器刻度。在所有I/O设备中,PIT可以最高优先 级IRQ0中断。PIT定时器中断由Linux内核的定时器中断处理程序来处理,为系统操作提 供基本的定时单元,例如进程调度、进程间隔定时器和其他许多定时事件。
- 多核CPU中的本地定时器:在多核CPU中,每个核都 是一个独立的处理器,它有自己的本地定时器,由CPU时钟驱动。
- 高分辨率定时器:大多数电脑都有一个时间戳定时器(TSC),由系统时钟驱动。它 的内容可通过64位TSC寄存器读取。由于不同系统主板的时钟频率可能不同,TSC不适合 作为实时设备,但它"提供纳秒级的定时器分辨率。一些高端个人计算机可能还配备有专用 高速定时器,以提供纳秒级定时器分辨率.
CPU操作
- 每个CPU都有一个程序计数器(PC),也称为指令指针(IP),以及一个标志或状态寄 存器(SR)、一个堆栈指针(SP)和几个通用寄存器,当PC指向内存中要执行的下一条指 令时,SR包含CPU的当前状态,如操作模式、中断掩码和条件码,SP指向当前堆栈栈顶。
- CPU操作可通过无限循环进行建模。
中断处理
- 在每条指令执行结束时,如果CPU未处于接受中断的状态,即在CPU的状态寄存器中屏蔽了中断。它将忽略中断请求.使其处于挂起状态并继续执行下一条指令。
时钟服务函数
- clock_t times(struct tms *buf);可用于获取某进程的具体执行时间。它将进程时间存储在struct tms buf中:
clock_t // user mode time
clock_t tms_stime; // system mode time
clock_t tms_cutime; // user time of children
clock_t tms_cstime; // system time of children
- date:打印或设置系统日期和时间。
- time:报告进程在用户模式和系统模式下的执行时间和总时间。
- hwclock:查询并设置硬件时钟(RTC),也可以通过BIOS来完成。
- 间隔定时器的值用以下结构体(在<sys/time.h>中)定义:
struct itimerval {
struct timeval it_inteirval; /* interval for periodic timer / struct timeval it_value; / time until next expiration /
};
struct timeval (
time_t tv_sec; / seconds /
suseconds_t tv_usec; / microseconds */
);
临界区
在基本代码系统中,只有一种执行实体,即任务,一次只执行一个任务。某任务在收到切换命令、进入休眠或退出之前,会一直执行下去。此外,任务
换只会发生在操作结束时,而不会发生在任何操作过程中。因此,任务之间没有竞争,因此在基本代码系统中没有临界区。但是,一旦我们将中断引人
系统,情况就会改变。有两种类型的实体来执行中断,分别是任务和中断处理程序,它们可能会争夺系统中的同一(共享)数据对象。例如,当某任务请
求间隔定时器时,必须将请求作为定时器队列元素输入timerQueue中。当某任务修改timerQueue 时,如果出现定时器中断,它将转移任务以执行中
断处理程序,可能会改动同一 timerQueue,造成竞态条件。因此,timerQueue是临界区,必须对它进行保护,以确保它一次只能由一个执行实体访
问。同样,当某进程在sleep()函数过程中执行时,可能被转移到执行中断处理程序,即可执行wakeup(),以试图在进程完成休眠操作之前唤醒它,
从而导致另一个竟态条件。所以,问题是如何防止任务和中断处理程序相互干扰。
实践
示例5.3:time.c
#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++); // delay to simulate computation
end = time (NULL);
printf ("end =%ld time=%ld\n" , end, end-start);
}
运行截图
示例5.1 gettimeofday.c
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
int main()
{
struct timeval tv;
gettimeofday(&tv, NULL);
printf("tv_sec: %d\n", tv.tv_sec);
printf("tv_usec: %d\n", tv.tv_usec);
return 0;
}