180 likes | 367 Views
Linux 系统下程序定时及测量. 杨 飞. 2009 - 12 - 25. 主要内容. 内核时钟与定时器原理 软件定时方法 程序运行时间测量. 内核时钟与定时器原理. 1. 时钟硬件. 主要与三种硬件相关 实时时钟 RTC ( Real time clock ) 可编程间隔定时器 PIT(Programmable Interval Timer) 时间戳计数器 TSC(Time Stamp Counter). 内核时钟与定时器原理. 通过主板小电池供电给 RTC 中的振荡器 整个系统的计时标准. 实时时钟 RTC.
E N D
Linux 系统下程序定时及测量 杨 飞 2009-12-25
主要内容 • 内核时钟与定时器原理 • 软件定时方法 • 程序运行时间测量
内核时钟与定时器原理 1.时钟硬件 • 主要与三种硬件相关 • 实时时钟 RTC(Real time clock) • 可编程间隔定时器PIT(Programmable Interval Timer) • 时间戳计数器TSC(Time Stamp Counter)
内核时钟与定时器原理 • 通过主板小电池供电给RTC中的振荡器 • 整个系统的计时标准 实时时钟 RTC 可编程间隔定时器PIT • 采用的最典型的芯片是Intel 8253/8254可编程定时/计数芯片 • 每隔一定周期通过IRQ0发出时钟中断 • 时钟频率HZ是用来定义每一秒有几次timer interrupts,可以用Asm/Param.h 查看 • 100HZ 250HZ 1000HZ 可选择 • 滴答Tick是HZ的倒数,意即timer interrupt每发生一次中断的时间。 • Linux中用全局变量jiffies表示系统自启动以来的时钟滴答数目。
内核时钟与定时器原理 时间戳计数器TSC • 64位寄存器、用作时间戳计数器 • Linux在系统初始化的时候确定时钟信号CLK(CPU的实际频率)的频率 • 它在每个时钟信号CLK 到来时+1 • 利用汇编指令rdtsc读取TSC的值。通过CPU的TSC,操作系统通常可以得到更为精准的时间度量。
内核时钟与定时器原理 2 定时器 定时器通过软件实现,包含一个时间范围,表明需要多长时间到期。这个时间范围的计算是把jeffies的当前值加上持续的时钟滴答数得到。 linux系统提供3种类型的定时器: (1)静态定时器 (2)动态定时器 (3)间隔定时器 其中(1)和(2)由系统内核使用,(3)由进程在用户态使用
内核时钟与定时器原理 • Linux系统有用户模式和核心模式两种。进程执行可以在两种模式上运行。 • 核心模式的进程:优先级别较高;运行效率高;较强的底层控制能力。 • 用户模式的进程:优先级别较低;不能直接存取内核,可以通过授权的系统调用访问内核资源 • 进程执行时间:用户模式下执行时间和核心模式执行时间。
内核时钟与定时器原理 进程采用的三种间隔定时器: • ITIMER_REAL:不管进程是否运行以及处于什么模式,定时器启动后每个时钟滴答都将其间隔计数器减1,当减到0值时,内核向进程发送SIGALRM信号 • ITIMER_VIRTUAL:当进程在用户模式执行时,定时器启动后每个时钟滴答都将其间隔计数器减1,当减到0值时,内核向进程发送SIGVTALRM信号。 • ITIMER_PROF:进程在用户模式和核心模式运行时,定时器启动后每个时钟滴答都将其间隔计数器减1,当减到0值时,内核向进程发送SIGPROF信号
内核时钟与定时器原理 相关数据结构及变量 struct itimerval { struct timeval it_interval; /* 定时器重启动的间隔值 */ struct timeval it_value; /* 定时器启动的初始值 */ }; struct timeval { long tv_sec; /* 秒 */ long tv_usec; /* 微秒(1/1000000) */ }; 由于间隔定时器的间隔计数器的内部表示方式与外部表现方式互不相同,因此有必要实现以微秒为单位的timeval结构和为时钟滴答次数单位的jiffies之间的相互转换。Linux在kernel/itimer.c中实现了其互相转换的两个函数——tvtojiffies()函数和jiffiestotv()函数
内核时钟与定时器原理 • 定时器的精度 取决于系统的时钟中断频率HZ • Jiffies:系统启动以来的时钟滴答数,32位的无符号整数全局变量jiffies, 每次时钟中断时加1(时间间隔1ms、4ms、10ms)。 • Xtime:是timeval结构的全局变量。用来表示当前时间距UNIX时间基准1970-01-01 00:00:00的相对秒数值,时间精度是纳秒。
软件定时方法 常用的定时: setitimer ()精度微秒级,最多只能开设3个定时器 函数原型: int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue); which 指的是上述三种定时器类型 value 指的是时间结构体 setitimer()将value指向的结构体设为计时器的当前值,如果ovalue不是NULL,将返回计时器原有值。
软件定时方法 Select()精度微秒级,主要用于socket通信及定时,可以开设多个定时器。 函数原型 int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout) struct fd_set类型 存放的是文件描述符(file descriptor),即文件句柄。 struct timeval 时间结构体
软件定时方法 例子: void time_v(int signame){ …. return; } int main() { struct itimerval t_v,t_v1; signal(SIGALRM, time_v); //触发信号 t_v.it_value.tv_sec = 1;//设置起始时间(秒级) t_v.it_value.tv_usec = 0;(微秒级) t_v.it_interval.tv_sec = 1;//设置间隔时间(秒级) t_v.it_interval.tv_usec = 0;//(微秒级) setitimer(ITIMER_REAL, &t_v, &t_v1); //启用定时程序 for(;;); } Setitimer()采用信号机制,当定时到期后,触发SIGALRM信号,系统去调用相关函数运行。
程序运行时间测量 合理的计算程序运行时间,有利于对算法的分析。 计算程序运行时间一般有如下方法: 1.通过Time函数 函数原型: time_t time( time_t * timer ) time_t表示的时间(日历时间)是从一个时间点(1970年1月1日0时0分0秒)到当前的秒数 该方法精度是秒级,若对结果的要求不高,可以选择它。 time_t t_start, t_end; double usetime; t_start= time( NULL ); …用户程序 … //待分析程序 t_end = time( NULL ); usetime = difftime( t_start, t_end);//计算时间差
程序运行时间测量 2 通过Clock函数 函数原型: clock_t clock( void ); clock_t 表示时钟滴答数 该方法精度毫秒,返回自程序开始运行的处理器时间 clock_t t_start, t_end; double usetime; t_start = clock() //表示当前时钟滴答数 …用户程序 … //待分析程序 t_end = clock(); usetime = (double)( (t_end – t_start)/(double)CLOCKS_PER_SEC ); CLOCKS_PER_SEC linux系统下默认值1000000
程序运行时间测量 3 通过gettimeofday函数 函数原型: int gettimeofday(struct timeval *tv, struct timezone *tz); 该方法精度是微秒,基于TSC实现。用当前TSC值减去上次时钟中断发生时的TSC,用这两个值更新xtime,并将这个值返回到用户空间。 struct timeval start,end; float timeuse; gettimeofday(&start,NULL); …用户程序 … //待分析程序 gettimeofday(&end,NULL); timeuse=CLOCKS_PER_SEC*(end.tv_sec- start.tv_sec)+end.tv_usec-start.tv_usec; timeuse/=CLOCKS_PER_SEC;
程序运行时间测量 合理的定时以及程序时间测量,可以根据测量精度选择相关函数。 纳秒级的时间精度,由于xtime的精度为ns,可以参考sys_gettimeofday()实现或者可以采用汇编指令实现 sys_gettimeofday(struct timeval __user *tv){ offset = time_interpolator_get_offset(); sec = xtime.tv_sec; nsec = xtime.tv_nsec; usec = (nsec + offset) / 1000; ktv->tv_sec = sec; ktv->tv_usec = usec; copy_to_user(tv, &ktv, sizeof(ktv)); } getnstimeofday (struct timespec *tv) { sec = xtime.tv_sec; nsec = xtime.tv_nsec+time_interpolator_get_offset(); tv->tv_sec = sec; tv->tv_nsec = nsec; }