第八周学习笔记
定时器及时钟服务
一、主要内容
1.硬件定时器
使用一个倒计时值对计数器进行编程,每个时钟信号减1。当计数减为0时,计数器向CPU生成一个定时器中断,将计数值重新加载到计数器中,并重复倒计时。计数器周期称为定时器刻度,是系统的基本计时单元。
2.个人计算机定时器
基于Intel x86的个人计算机有数个定时器:
- 实时时钟(RTC)
- 可编程间隔定时器(PIT)
- 多核CPU中的本地定时器
- 高分辨率定时器
3.CPU操作
每个CPU都有:
- 一个程序计数器(PC),也称为指令指针(IP)
- 一个标志或状态寄存器(SR)
- 一个堆栈指针(SP)
- 几个通用寄存器
当PC指向内存中要执行的下一条指令时,SR包含CPU的当前状态,SP指向当前堆栈栈顶。堆栈是CPU用于特殊操作(如push、pop调用和返回等)的一个内存区域。
由于无效地址、非法指令、越权等原因,可能会出现一个错误状态,称为异常或陷阱。当CPU遇到异常时,它会根据内存中预先安装的指针来执行软件中的异常处理程序。在每条指令执行结束时,CPU会检查挂起的中断。中断是I/O设备或协处理器发送 给CPU的外部信号,请求CPU服务。如果有挂起的中断请求,但是CPU未处于接受中断 的状态,即它的状态寄存器已经屏蔽了中断,CPU会忽略中断请求,继续执行下一条指令。 否则,它将直接执行中断处理。在中断处理结束时,它将恢复指令的正常执行。
中断处理和异常处理都在操作系统内核中进行。在大多数情况下,用户级程序无法访问它们。
4.中断处理
外部设备(如定时器)的中断被馈送到中断控制器的预定义输入行,按优先级对中断输入排序,并将具有最高优先级的中断作为中断请求(IRQ)路由到CPU。在每条指令执行结束时,如果CPU未处于接受中断的状态,即在CPU的状态寄存器中屏蔽了中断.它将忽略中断请求,使其处于挂起状态,并继续执行下一条指令。如果CPU处于接受中断状态,即中断未被屏蔽,那么CPU将会转移它正常的执行顺序来进行中断处理。对于每个中断,可以编程中断控制器以生成一个唯一编号,叫作中断向量,标识中断源。在获取中断向量号后,CPU用它作为内存中中断向量表中的条目索引,条目包含一个指向中断处理程序入口地址的指针来实际处理中断。当中断处理结束时,CPU恢复指令的正常执行。
5.时钟服务函数
(1)gettimeofday-settimeofday
gettimeofday()函数用于返回当前时间(当前秒的秒和微秒)。settimeofday()函数用于设置当前时间。在Unix/Linux中,时间表示自1970年1月1日00:00:00起经过的秒数。它可以通过库函数ctime(&time)转换为日历形式。
(2)time系统调用
以秒为单位返回当前时间。如果参数t不是NULL,还会将时间存储在t指向的内存中。 time系统调用具有一定的局限性,只提供以秒为单位的分辨率,而不是以微秒为单位。
(3)times系统调用
可用于获取某进程的具体执行时间。它将进程时间存储在struct tms buf中
(4)time和data命令
- date:打印或设置系统日期和时间。
- time:报告进程在用户模式和系统模式下的执行时间和总时间。
- hwclock:查询并设置硬件时钟(RTC),也可以通过BIOS来完成。
6.间隔定时器
Linux为每个进程提供了三种不同类型的间隔计时器,可用作进程计时的虚拟时钟。间隔定时器由setitimer()系统调用创建。getitimer()系统调用返回间隔定时器的状态。
各间隔定时器在参数which指定的不同时间域中工作。当间隔定时器定时到期时,会向进程发送一个信号,并将定时器重置为指定的间隔值(如果是非零数)。一个信号就是发送给某个进程进行处理的一个数字(1到31)。有3类间隔定时器,分别是:
- ITIMER_REAL:实时减少,在到期时生成一个SIGALRM(14)信号。
- IT1MER_V1RTUAL;仅当进程在用户模式下执行时减少,在到期时生成一个 SIGVTALRM(26)信号。
- ITIMER_PROF:当进程正在用户模式和系统(内核)模式下执行时减少。这类间隔 定时器与IT1MER_VIRTUAL结合使用,通常用于分析应用程序在用户模式和内核模式下花费的时间。它在到期时生成一个SIGPROF(27)信号。
7.定时器中断
整个基本系统在一个虚拟CPU上运行,它是一个Linux进程。定时器向Linux进程发 出的信号可看作是对基本系统虚拟CPU的中断。
8.临界区
在基本代码系统中,只有一种执行实体,即任务,一次只执行一个任务。某任务在收 到切换命令、进入休眠或退出之前,会一直执行下去。此外.任务切换只会发生在操作结束时,而不会发生在任何操作过程中。因此,任务之间没有竞争,因此在基本代码系统中没有临界区.
二、实践及问题
time()获取当前时间
#include <stdio.h> #include <string.h> #include <time.h> int main() { time_t seconds; seconds = time((time_t *)NULL); printf("%d\n", seconds); return 0; }
gettimeofday()
#include <signal.h> #include <stdio.h> #include <sys/time.h> #include <time.h> int count = 0; struct itimerval t; time_t start,end ; void timer_handler(int sig){ end =time(NULL); printf("timer_handler : signal %d count=%d , diff: %ld \n",sig, ++count,end -start); start = end; 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 ; signal (SIGVTALRM ,timer_handler); timer.it_value.tv_sec = 0; timer.it_value.tv_usec = 100000; //every 1s afterward timer.it_interval.tv_sec = 1; timer.it_interval.tv_usec = 0; // start a virtual itimer start = time(NULL); setitimer( ITIMER_VIRTUAL , &timer ,NULL ); printf("press Ctrl + C to terminate \n"); while(1); }
![](https://img2022.cnblogs.com/blog/2166419/202210/2166419-20221009191952744-653345579.png)
gettimetoday
#include <stdio.h> #include <stdlib.h> #include <sys/time.h> #include <time.h> int main(){ struct timeval t; gettimeofday(&t,NULL); printf("sec = %ld , usec = %d\n",t.tv_sec,t.tv_usec); printf((char*)ctime(&t.tv_sec)); return 0; }
![](https://common.cnblogs.com/images/loading.gif)