《Unix&Linux系统编程》第五章学习笔记
第5章 定时器及时钟服务
5.1 硬件定时器
定时器是由时钟源和可编程计算器组成的硬件设备。
- 时钟源:晶体振荡器,会产生周期性电信号。使用倒计时值对计数器进行编程,每个时钟信号间1,当计数减为0时,计数器向CPU生成一个定时器中断。
- 计数器周期称为定时器刻度,是系统的基本计时单元。
5.2 个人计算机定时器
- 实时时钟(RTC):个人计算机关机时,也能连续运行,用于试试提供时间和日期信息。
- 可编程间隔定时器(PIT):是与CPU分离的硬件定时器,可编程,提供以毫秒位单位的定时器刻度。
- 多核CPU中的本地定时器:多核CPU的每个核都有自己的本地计时器,由CPU时钟驱动。
- 高分辨率定时器:时间戳定时器(TSC)由系统时钟驱动,可提供纳秒级的定时器分辨率。
5.3 CPU操作
每个CPU都有一个程序计数器(PC),也称为指令指针(IP),以及一个标志或状态寄存器(SR)、一个堆栈指针(SP)和几个通用寄存器。
PC-->下一条要执行的命令
SR-->CPU当前状态(操作模式、中断掩码、条件码)
SP-->堆栈栈顶
- 异常(陷阱):无效地址、非法指令、越权等原因。
- 中断:I/O设备或协处理器方的状态。
5.4 中断处理
- 外部设备(如定时器)的中断被馈送到中断控制器的预定义输入行,按优先级对中断输入排序,并将具有最高优先级的中的作为中断请求(IRQ)路由到CPU。
- 对于每个中断,可以编程中断控制器以生成一个唯一编号——中断向量。
5.5 时钟服务函数
时钟服务可通过系统调用、库函数和用户级命令调用。
(1)gettimeofday-settimeofday
gettimeofday系统调用:
在Unix/Linux系统中,时间表示自1970年1月1日00:00:00起经过的秒数,可通过库函数ctime(&time)转换为日历形式。
代码:
#include <stdio.h>
#include <stdlib. h>
#include <sys/time. h>
#include <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)); //show time in calendar form
}
实践截图:
(2)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++) ; // delay to simulate computation
end = time (NULL) ;
printf ("end=%ld time=%ld\n", end, end-start) ;
}
实践截图:
输出打印开始时间、结束时间以及从开始到结束的秒数。
(3)times系统调用
可用于获取某进程的具体执行时间。
(4)time和date命令
- date:打印或设置其他日期和时间
- time:报告进程在用户模式和系统模式下的执行时间和总时间
- hwclock:查询并设置硬件时钟(RTC),也可通过BIOS完成
5.6 间隔定时器
- 代码:
#include <signal.h>
#include <stdio.h>
#include <sys/time.h>
int count = 0;
struct itimerval t;
void timer_handler(int sig)
{
printf("timer_handler: signal=%d count=%d\n", sig, ++count);
if (count>=8) {
printf("cancel timer\n") ;
t.it_value.tv_sec = 0;
t.it_value.tv_usec = 0;
setitimer (ITIMER_VIRTUAL, &t, NULL) ;
}
}
int main()
{
struct itimerval timer ;
//Install timer_ handler as SIGVTALRM signal handler
signal (SIGVTALRM, timer_handler) ;
//Configure the timer to expire after 100 msec
timer.it_value.tv_sec = 0;
timer.it_value.tv_usec = 100000; //100000 nsec
//and every 1 sec afterward
timer.it_interval.tv_sec = 1;
timer.it_interval.tv_usec = 0;
//Start a VIRTUAL i timer
setitimer(ITIMER_VIRTUAL, &timer, NULL) ;
printf("looping: enter Control-C to terminate\n");
while(1);
}
- 实践截图:
5.7 REAL模式间隔定时器
VITRUAL和PROF模式下的间隔计时器仅在执行进程时才有效。REAL模式间隔定时器各不相同,无论进程是否在执行,它们都必须由定时器中断处理程序来更新,因此操作系统内核必须使用额外的数据结构来处理进程的REAL模式定时器,并在定时器到期或被取消时采取措施。