我们在程序中会频繁地取当前时间,例如处理一个http请求时,两次调用gettimeofday取差值计算出处理该请求消耗了多少秒。这样的调用无处不在,所以我们有必要详细了解下,gettimeofday这个函数做了些什么?它可以真的精确到微秒吗?它的调用成本有多大?如果在系统繁忙时,频繁的调用它有问题吗?
gettimeofday是C库提供的函数(C库做了些自己的优化),然而也封装了sys_gettimeofday系统调用,就是说,执行这个函数,除却C库的优化,必然执行一次系统调用。接下来,我来试着回答以上4个问题。
一、gettimeofday做了些什么?
它把内核保存的墙上时间和jiffies综合处理后返回给用户。解释下墙上时间和jiffies是什么:1、墙上时间就是实际时间(1970/1/1号以来的时间),它是由我们主板电池供电的(装过PC机的同学都了解)RTC单元存储的,这样即使机器断电了时间也不用重设。当操作系统启动时,会用这个RTC来初始化墙上时间,接着,内核会在一定精度内根据jiffies维护这个墙上时间。2、jiffies就是操作系统启动后经过的时间,它的单位是节拍数。有些体系架构,1个节拍数是10ms,但我们常用的x86体系下,1个节拍数是1ms。也就是说,jiffies这个全局变量存储了操作系统启动以来共经历了多少毫秒。我们来看看gettimeofday是如何做的。首先它调用了sys_gettimeofday系统调用。
大家看到,它调用do_gettimeofday函数取到当前时间存储到局部变量ktv上,然后调用copy_to_user把结果复制到用户空间。每个体系都有自己的实现,我这里就简单列下x86_64体系下do_gettimeofday的实现:
二、gettimeofday可以精确到微秒吗?
不可以,上面说了,它最终的精确是由jiffies全局变量决定的,而jiffies的单位是节拍数,节拍数又是跟体系结构对应的。目前,x86-64的节拍数是1ms,所以,我们取到的当前时间精度只到毫秒,而且会有正负0.5毫秒的误差。
那么,为什么不能精确到微秒或者纳秒呢?难道cpu现在号称nGHZ都是假的吗?
呵呵,是真的,但是时钟的维护不可能精确到指令级的。时钟就是靠jiffies变量维护的,jiffies是通过时钟中断来维护的。所以,当我们需要更精确的时间精度时,就意味着在一秒钟会有更多的时钟中断需要内核处理,这会加大操作系统的负担。以前,是10ms一个节拍数,意味着时间精度只到10ms。现在随着每秒钟CPU可以处理更多的指令,大部分体系架构下,都是1ms一个节拍数,这意味着目前大家觉得,1秒钟发送1000个时钟中断最平衡。每个时钟中断来临时,中断处理程序会更新jiffies这个变量。
三、它的成本有多大?如果在系统繁忙时,几毫秒调用一次有问题吗?
最上面已经说了,这是个系统调用!最简单的系统调用都有无法避免的成本:陷入内核态。当我们调用gettimeofday时,将会向内核发送软中断,然后将陷入内核态,这时内核至少要做下列事:处理软中断、保存所有寄存器值、从用户态复制函数参数到内核态、执行、将结果复制到用户态。这些都是成本!
而且,它本身的精度是1ms处理一次CPU的时钟中断,精度也只到毫秒,如果我们只是几毫秒就调用一次,就有点得不偿失了。所以,当我们的代码中在运行时非常频繁的调用gettimeofday时,请思考下,是否每次都有必要?是否需要缓存下这个值在用户空间呢?学学nginx或者JVM吧。
四、关于jiffies值得一提的两点
先看看它的定义:
只谈两点。
1、它用了一个C语言里比较罕见的关键字volatile,这个关键字用于解决并发问题。C语言编译器很喜欢做优化的,它不清楚某个变量可能会被并发的修改,例如上面的jiffies变量首先是0,如果首先一个CPU修改了它的值为1,紧接着另一个CPU在读它的值,例如 __jiffies = 0; while (__jiffies == 1),那么在内核的C代码中,如果不加volatile字段,那么第二个CPU里的循环体可能不会被执行到,因为C编译器在对代码做优化时,生成的汇编代码不一定每次都会去读内存!它会根据代码把变量__jiffies设为0,并一直使用下去!而加了volatile字段后,就会要求编译器,每次使用到__jiffies时,都要到内存里真实的读取这个值。
2、它的类型是unsigned long,在32位系统中,最大值也只有43亿不到,从系统启动后49天就到达最大值了,之后就会清0重新开始。那么jiffies达到最大值时的回转问题是怎么解决的呢?或者换句话说,我们需要保证当jiffies回转为一个小的正数时,例如1,要比几十秒毫秒前的大正数大,例如4294967290,要达到jiffies(1)>jiffies(4294967290)这种效果。
内核是通过定义了两个宏来解决的:
很巧妙的设计!仅仅把unsigned long转为long类型后相减比较,就达到了jiffies(1)>jiffies(4294967290)效果,简单的解决了jiffies的回转问题,赞一个。
分享到:
相关推荐
在C语言中可以使用函数gettimeofday()函数来得到时间。它的精度可以达到微妙,下面一起来看看
gettimeofday 取得目前的时间例子
函数说明:settimeofday()会把目前时间设成由tv 所指的结构信息,当地时区信息则设成tz 所指的结构。详细的说明请参考gettimeofday()。 注意,在Linux下,只有root 权限才能使用此函数修改时间。 返回值:成功则返回...
在Linux下计算某个程序段执行的时间一般使用gettimeofday函数,此函数的声明在sys/time.h文件中。此函数接收两个结构体参数,分别为timeval、timezone. 两个结构体的声明如下: struct timeval { time_t tv_sec;...
该函数用通过调用gettimeofday分类统计了机器的指令周期反应时间。精度到微秒。
计算系统运行一个thread需要多长时间
Linux下对时间进行运算,如果是到秒级的,一般是用time之类的函数实现。文中介绍了Linux下精确到微秒级的时间操作函数。主要是用到了gettimeofday函数,并且介绍了这个函数的结构。
系统自带的ping命令当它接送完所有ICMP报文后,会对所有发送和所有接收的ICMP报文进行统计,从而计算ICMP报文丢失的比率。为达此目的,定义两个全局变量:接收计数器和发送计数器,用于记录ICMP报文接受和发送数目。...
主要介绍了php使用gettimeofday函数返回当前时间并存放在关联数组里的方法,涉及php中gettimeofday函数的使用技巧,需要的朋友可以参考下
下面小编就为大家带来一篇inux下gettimeofday函数windows替换方法(详解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
函数段的测试有4中方法,分别是clock,times,gettimeofday,getrusage来实现的。四种函数测量同一程序所需时间
1,gettimeofday xtime使用总结 2,含有自测代码程序
一、linux平台下的计时函数gettimeofdayint gettimeofday ( struct timeval * tv , struct timez
如果仅仅测试时间还行,但是如果程序中用到时间控制类的函数,如time, gettimeofday自身还会消耗不少时间,且增加程序执行的成本,这样得到的时间不精确。针对这种情况,使用CPU心跳的函数来处理时间,经封装后的...
应用select多路复用, linux, 使用C语言进行计时,在用户空间中可以使用C语言函数gettimeofday 得到时间,它的调用格式是:
SQL语句的性能监控从监控工具来说大致可分为由高级语言提供和由Oracle本身提供,高级语言以典型的应用C语言和WEB开发语言PHP为例, C语言中可以用 gettimeofday 函数来在某一数据库操作之前和之后分别获取一个时间值,...