使用一个倒计时值对计数器进行编程,每个时钟信号减1。当计数减为0时,计数器向CPU生成一个定时器中断,将计数值重新加载到计数器中,并重复倒计时。计数器周期称为定时器刻度,是系统的基本计时单元。
基于Intel x86的个人计算机有数个定时器:
每个CPU都有:
当PC指向内存中要执行的下一条指令时,SR包含CPU的当前状态,SP指向当前堆栈栈顶。堆栈是CPU用于特殊操作(如push、pop调用和返回等)的一个内存区域。
由于无效地址、非法指令、越权等原因,可能会出现一个错误状态,称为异常或陷阱。当CPU遇到异常时,它会根据内存中预先安装的指针来执行软件中的异常处理程序。在每条指令执行结束时,CPU会检查挂起的中断。中断是I/O设备或协处理器发送 给CPU的外部信号,请求CPU服务。如果有挂起的中断请求,但是CPU未处于接受中断 的状态,即它的状态寄存器已经屏蔽了中断,CPU会忽略中断请求,继续执行下一条指令。 否则,它将直接执行中断处理。在中断处理结束时,它将恢复指令的正常执行。
中断处理和异常处理都在操作系统内核中进行。在大多数情况下,用户级程序无法访问它们。
外部设备(如定时器)的中断被馈送到中断控制器的预定义输入行,按优先级对中断输入排序,并将具有最高优先级的中断作为中断请求(IRQ)路由到CPU。在每条指令执行结束时,如果CPU未处于接受中断的状态,即在CPU的状态寄存器中屏蔽了中断.它将忽略中断请求,使其处于挂起状态,并继续执行下一条指令。如果CPU处于接受中断状态,即中断未被屏蔽,那么CPU将会转移它正常的执行顺序来进行中断处理。对于每个中断,可以编程中断控制器以生成一个唯一编号,叫作中断向量,标识中断源。在获取中断向量号后,CPU用它作为内存中中断向量表中的条目索引,条目包含一个指向中断处理程序入口地址的指针来实际处理中断。当中断处理结束时,CPU恢复指令的正常执行。
gettimeofday()函数用于返回当前时间(当前秒的秒和微秒)。settimeofday()函数用于设置当前时间。在Unix/Linux中,时间表示自1970年1月1日00:00:00起经过的秒数。它可以通过库函数ctime(&time)转换为日历形式。
以秒为单位返回当前时间。如果参数t不是NULL,还会将时间存储在t指向的内存中。 time系统调用具有一定的局限性,只提供以秒为单位的分辨率,而不是以微秒为单位。
可用于获取某进程的具体执行时间。它将进程时间存储在struct tms buf中
Linux为每个进程提供了三种不同类型的间隔计时器,可用作进程计时的虚拟时钟。间隔定时器由setitimer()系统调用创建。getitimer()系统调用返回间隔定时器的状态。
各间隔定时器在参数which指定的不同时间域中工作。当间隔定时器定时到期时,会向进程发送一个信号,并将定时器重置为指定的间隔值(如果是非零数)。一个信号就是发送给某个进程进行处理的一个数字(1到31)。有3类间隔定时器,分别是:
整个基本系统在一个虚拟CPU上运行,它是一个Linux进程。定时器向Linux进程发 出的信号可看作是对基本系统虚拟CPU的中断。
在基本代码系统中,只有一种执行实体,即任务,一次只执行一个任务。某任务在收 到切换命令、进入休眠或退出之前,会一直执行下去。此外.任务切换只会发生在操作结束时,而不会发生在任何操作过程中。因此,任务之间没有竞争,因此在基本代码系统中没有临界区.
sleep,usleep和nanosleep
sleep()和nanosleep()都是使进程睡眠一段时间后被唤醒,但是二者的实现完全不同。Linux中并没有提供系统调用sleep(),sleep()是在库函数中实现的,它是通过调用alarm()来设定报警时间,调用sigsuspend()将进程挂起在信号SIGALARM上,sleep()只能精确到秒级上。nanosleep()则是Linux中的系统调用,它是使用定时器来实现的,该调用使调用进程睡眠,并往定时器队列上加入一个timer_list型定时器,time_list结构里包括唤醒时间以及唤醒后执行的函数,通过nanosleep()加入的定时器的执行函数仅仅完成唤醒当前进程的功能。系统通过一定的机制定时检查这些队列(比如通过系统调用陷入核心后,从核心返回用户态前,要检查当前进程的时间片是否已经耗尽,如果是则调用schedule()函数重新调度,该函数中就会检查定时器队列,另外慢中断返回前也会做此检查),如果定时时间已超过,则执行定时器指定的函数唤醒调用进程。当然,由于系统时间片可能丢失,所以nanosleep()精度也不是很高。
信号量SIGALRM + alarm()
alarm方式虽然很好,但这种方式的精度能达到1秒,是无法低于1秒的精度。其中利用了*nix系统的信号量机制,首先注册信号量SIGALRM处理函数,调用alarm(),设置定时长度
RTC机制
RTC机制利用系统硬件提供的Real Time Clock机制,通过读取RTC硬件/dev/rtc,通过ioctl()设置RTC频率,这种方式比较方便,利用了系统硬件提供的RTC,精度可调,而且非常高.该种方式要求系统有RTC设备
select()
能精确到1us,目前精确定时的最流行方案。通过使用select(),来设置定时器;原理利用select()方法的第5个参数,第一个参数设置为0,三个文件描述符集都设置为NULL,第5个参数为时间结构体
#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; signal(SIGVTALRM, timer_handler); timer.it_value.tv_sec = 0; timer.it_value.tv_usec = 100000; timer.it_interval.tv_sec = 1; timer.it_interval.tv_usec = 0; setitimer(ITIMER_VIRTUAL,&timer, NULL); printf("looping:enter Control-C to Terminate\n"); while(1); }
运行结果
#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)); }
运行结果出错了
上网查了原因 https://blog.csdn.net/weixin_40877924/article/details/108762118
然后我换了一个代码
#include <sys/time.h> #include <stdio.h> #include <unistd.h> void foo() { int i = 0; for (; i <= 10000; i++); } int main(int argc, char *argv[]) { struct timeval start; struct timeval end; float timeuse; gettimeofday(&start, NULL); printf("start time is %d:%d\n", start.tv_sec, start.tv_usec); foo(); gettimeofday(&end, NULL); printf("end time is %d:%d\n", end.tv_sec, end.tv_usec); timeuse = 1000000 * (end.tv_sec - start.tv_sec) + ( end.tv_usec - start.tv_usec ); printf("timeuse1 is %f \n", timeuse); timeuse /= 1000000; printf("timeuse2 is %f \n", timeuse); printf("use time is %f\n", timeuse); return 0; }
运动结果
在自学的过程中,经常会遇到很多问题,甚至是运行和书上一模一样的代码,都会出现问题,这时就要及时查资料,寻找解决办法。但是运行代码不是出结果了就结束了,还要尽量看懂它,这样才会有所收获。