欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux下有关时间的函数:time,times,clock,gettimeofday等

程序员文章站 2024-01-27 18:21:40
...

因为项目需要,所以把Linux下时间相关的内容刷了一遍。终于抽出时间总结一下。

目录

1. 时间的概念

2. 获得时钟时间的函数

2.1 时钟时间的存储类型

2.2 time函数

2.3 gettimeofday函数

3. 获得程序运行的时间(即进程时间)

3.1 进程时间的存储类型

3.2 times函数

3.3 clock函数

4. 补充说明


1. 时间的概念

首先理解时钟时间,也叫做墙上时钟时间,可以简单理解为宏观上经过的时间。细究起来就是进程运行的时钟总量,其值与系统中同时运行的进程数有关。既然有多个进程,那么进程就会出现等待,阻塞,运行几个状态,且都需要消耗时间。这些都包含在时钟时间内。上面这个概念最为重要。下面还有几个概念:

用户CPU时间:是执行用户指令所用的时间。

系统CPU时间:是为该进程执行内核程序所经历的时间。

用户CPU时间+系统CPU时间=运行时间,也称作CPU时间。CPU时间通常表现为滴答计数,保存在数据结构clock_t中,sysconf可以得到每秒的滴答数值设定值。

归纳来说,就是时钟时间包括进程的等待,阻塞和运行三大部分的时间,而运行时间又分为用户CPU时间和系统CPU时间。

一般来说,我们通常就关注两类时间,一类是真实时间及时钟时间,另一类是进程时间通常用CPU时间来表示。

2. 获得时钟时间的函数

获得时间的方法有几个函数可以实现,大体来说分为两类。

一类是获得时钟时间,通常会得到从过去某个时间点到当前时刻的时间差,这个过去的时间点通常为1970年。当然,在一些封装好了的函数中,并不需要你知道过去的时间点是什么具体的时刻,内部会帮你换算好,返回当前的实际时间。

2.1 时钟时间的存储类型

linux下存储时间常见的有两种存储方式,一个是从1970年到现在经过了多少秒,一个是用一个结构来分别存储年月日时分秒的。

第一种存储方式涉及到一种数据类型time_t,关于time_t,是在头文件time.h中定义的。这里可以明确的看到,time_t的数据类型其实就是长整型,而其表示的含义即从1970年到现在经过的秒数。

#ifndef __TIME_T
#define __TIME_T    
typedef long time_t;    
#endif

第二种存储方式更加精确,可以精确到微秒级。用一个结构体表示

struct timeval
{
    long tv_sec; /*秒*/
    long tv_usec; /*微秒*/
};

2.2 time函数

首先看函数声明,该函数包含在time.h头文件中,其返回值类型为time_t。是自Epoch以来经过的秒数。如果其传入参数不为NULL,那么该秒数还会被赋给传入的参数中。

#include <time.h>
time_t time(time_t *timep);
// returns number of seconds since the Epoch, or -1 on error

使用实例如下:

void testTime()
{
  time_t t;
  t = time(NULL);
  cout << t << endl;
}

2.3 gettimeofday函数

同样先看看书声明,该函数包含在sys/time.h头文件中,其返回值为int类型,返回0表示调用函数成功,返回-1表示调用失败。其函数参数有两个,第一个保存获得的具体时间,第二个参数是历史产物,现在已经不用,赋成NULL即可。

#include <sys/time.h>
int gettimeofday(struct timeval *tv, stuct timezone *tz);
// return 0 on success, or -1 on error

使用实例如下:

void testGettimeofday()
{
  timeval t1;
  gettimeofday(&t1, NULL);
  cout << t1.tv_sec << endl;
  cout << t1.tv_usec << endl;
}

3. 获得程序运行的时间(即进程时间)

另外一类是cpu时间,即运行时间。这类时间其实也是返回的时间差,时间差的计算起点通常是进程开始的时间,其实和1970年这种过去时间节点的概念是一致的。但是返回的cpu时间单位往往不是时间的单位,而是cpu的时钟计时单元。想要获得直观的时间,还需要进行滴答数和时间的换算,即通常的一秒钟会有多少个时钟计时单元。

3.1 进程时间的存储类型

Cpu时间可以通过times()函数或者clock()函数获得,介绍这两种方法之前,先介绍一个数据类型clock_t和一个结构体tms。

Clock_t是时钟计数单元的计数,而其通常是以结构体tms的形式呈现的,结构体tms的定义如下:

struct tms{
  clock_t tms_utime; // 调用进程到目前为止使用的用户CPU时间
  clock_t tms_stime; // 调用进程到目前为止使用的系统cpu时间
  clock_t tms_cutime; // 包含等待所有子进程的用户cpu时间
  clock_t tms_cstime; // 包含等待所有子进程的系统cpu时间
};

有了这两个概念,下面可以讨论上述两种函数的区别和用法了。

3.2 times函数

首先是times函数,声明如下。可以看到times函数的返回类型为clock_t,且其定义包含在头文件sys/time.h中。可以调用sysconf(_SC_CLK_TCK)来获得实际的时间。这里要注意times函数和上面提到的time函数的功能是不同的,times函数是获得进程时间的方法,time函数是获得时钟时间的方法。

#include <sys/times.h>
clock_t times(struct tms *buf);
// returns number of clock ticks(sysconf(_SC_CLK_TCK))since "arbitrary" time in past on success, or (clock_t)-1 on error

上面提到clock_t通常是以结构体tms的形式呈现的,在实际写程序中,一般先定义一个clock_t类型的变量,通过传参的方式传入times函数,得到对应的时间。

void testTimes()
{
  tms t1;
  tms t2;
  long clockticks = sysconf(_SC_CLK_TCK);
  cout << clockticks << endl;

  if(times(&t1) == -1)
    cout << "call times() error" << endl;
  cout << "user cpu time is: " << (double)t1.tms_utime / clockticks << endl;
  cout << "system cpu time is: " << (double)t1.tms_stime / clockticks << endl;
  //cout << (double)t1.tms_utime << endl;
  //sleep(10);
  for(int i=0; i<100000000; i++)
    getppid();

  if(times(&t2) == -1)
    cout << "call times() error" << endl;
  cout << "user cpu time is: " << (double)t2.tms_utime / clockticks << endl;
  cout << "system cpu time is: " << (double)t2.tms_stime / clockticks << endl;
  //cout << "user cpu time containing waiting is: " << (double)t2.tms_cutime / clockticks << endl;
  //cout << "system cpu time containing waiting is: " << (double)t2.tms_cstime / clockticks << endl;

}

这里需要注意的是,由于times得到的是cpu的运行时间,所以需要cpu真实的运行才能看出t2得到的变化的时间,这里使用getppid()函数。不能用sleep函数,因为sleep函数代表将进程进行阻塞,这部分时间是不算在times函数的时间里的。

3.3 clock函数

首先看clock函数的声明,与times函数相同,同样返回clock_t类型的返回值。但是这两者返回的值是不同的,其对应的每秒的次数是CLOCKS_PER_SEC,这个值有可能会根据系统内的定义而又所不同,可以将其打印出来看一下。不过其总是和clock这个函数是对应的,相除之后得到的就是对应的秒数。

#include <time.h>
clock_t clock(void);
// returns total CPU time used by calling process measured in CLOCKS_PER_SEC, or -1 on error

同样,由于clock函数得到的也是cpu的运行时间,所以程序中不能用sleep来进行实验,sleep的时间不算在clock计算的时间内。

void testClock()
{
  clock_t clocktime;
  cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << endl;
  for(int i=0; i<100000000; i++)
    getppid();

  //sleep(5);
  clocktime = clock();
  cout << "cpu running time is: " << clocktime / CLOCKS_PER_SEC << endl;
}

4. 补充说明

sys/times.h ,sys/time.h 和 time.h的区别sys/time.h这个头文件是Linux系统的头文件,只能用在linux的平台上,time.h的头文件是c语言标准的头文件,可以跨平台使用。time_t这类的数据结构是linux的,所以只调用time.h,就需要自己定义time_t(如果使用到了的话)。sys/times.h是包含times函数的头文件,其定义的是获得进程时间的方法。sys/time.h是包含gettimeofday函数的头文件,其定义的是获得时钟时间的方法。

_SC_CLK_TCK 在头文件unistd.h中

Times函数和clock函数的功能基本上是一致的,只是处理的方式不同,这是历史原因造成的,一个是POSIX.1标准,一个是C语言编程标准。在转换为实际时间的过程当中,sysconf(_SC_CLK_TCK)和CLOCKS_PER_SEC不要使用混淆。

相关标签: Linux linux