0


Linux C编程基础:获取时间

1.前言

对于linux下的编程,无论是用户态还是内核态,时间获取都是经常需要使用到的。以下分别从用户态和内核态整理了几个常用的时间获取接口,供编写代码时快速查阅。

linux时间子系统的发展历史及详细介绍,可以参考:深入理解Linux时间子系统。

2.用户态获取时间

  • 2.1 clock_gettime()
#include<time.h>intclock_gettime(clockid_t __clock_id,structtimespec*__tp);
  • 作用:根据系统时钟的类型,获取当前时间
  • __clock_id:系统时钟的类型。常用取值: - CLOCK_REALTIME: 系统相对时间,从UTC 1970-1-1 0:0:0开始计时,更改系统时间会更改获取的值;- CLOCK_MONOTONIC: 系统绝对时间/单调时间,为系统重启到现在的时间,更改系统时间对它没有影响;- CLOCK_PROCESS_CPUTIME_ID: 本进程到当前代码系统CPU花费的时间;- CLOCK_THREAD_CPUTIME_ID: 本线程到当前代码系统CPU花费的时间;
  • __tp: 存放当前的时间。
  • 返回值:成功则返回0,失败则返回-1
timespec

结构体:

structtimespec{
  __time_t tv_sec;/* Seconds. 秒 */
  __syscall_slong_t tv_nsec;/* Nanoseconds.  纳秒*/};

示例:

#include<stdio.h>#include<string.h>#include<time.h>longlongget_clock_sys_time_ns(void){structtimespec tp;longlong time_ns =0;clock_gettime(CLOCK_MONOTONIC,&tp);
    time_ns =(longlong)tp.tv_sec *1000000000+ tp.tv_nsec;return time_ns;}intmain(void){structtimespec tp;///< 获取从1970年1月1日到目前的时间memset(&tp,0,sizeof(structtimespec));clock_gettime(CLOCK_REALTIME,&tp);printf("clock_id = CLOCK_REALTIME, sec = %ld, nsec = %ld\n", tp.tv_sec, tp.tv_nsec);///< 获取系统启动时间memset(&tp,0,sizeof(structtimespec));clock_gettime(CLOCK_MONOTONIC,&tp);printf("clock_id = CLOCK_MONOTONIC, sec = %ld, nsec = %ld, sys_time = %lld ns\n", tp.tv_sec, tp.tv_nsec,get_clock_sys_time_ns());///< 获取本进程运行时间memset(&tp,0,sizeof(structtimespec));clock_gettime(CLOCK_PROCESS_CPUTIME_ID,&tp);printf("clock_id = CLOCK_PROCESS_CPUTIME_ID, sec = %ld, nsec = %ld\n", tp.tv_sec, tp.tv_nsec);///< 获取本线程运行时间memset(&tp,0,sizeof(structtimespec));clock_gettime(CLOCK_THREAD_CPUTIME_ID,&tp);printf("clock_id = CLOCK_THREAD_CPUTIME_ID, sec = %ld, nsec = %ld\n", tp.tv_sec, tp.tv_nsec);return0;}
  • 2.2.gettimeofday()
#include<sys/time.h>intgettimeofday(structtimeval*tv,structtimezone*tz);
  • 作用:获取当前时间(从1970年1月1日到目前的时间)
  • tv:当前UTC时间
  • tz:当前时区信息
  • 返回值:成功则返回0,失败则返回-1

timeval结构体:

structtimeval{
  __time_t tv_sec;/* Seconds.  秒*/
  __suseconds_t tv_usec;/* Microseconds.  微秒*/};

timezone结构体:

structtimezone{int tz_minuteswest;/* Minutes west of GMT. 和Greenwich时间差了多少分钟 */int tz_dsttime;/* Nonzero if DST is ever in effect. 日光节约时间的状态  */};

示例:

#include<stdio.h>#include<string.h>#include<sys/time.h>longlongget_sys_time_ms(void){longlong time_ms =0;structtimeval tv;gettimeofday(&tv,NULL);
    time_ms =((longlong)tv.tv_sec*1000000+ tv.tv_usec)/1000;return time_ms;}intmain(void){///< 获取系统时间printf("sys_time = %lld ms\n",get_sys_time_ms());return0;}
  • 2.3.time()
#include<time.h>time_ttime(time_t*tloc);
  • 作用:获取1970-01-01 00:00:00 +0000至今的秒数(UTC)
  • tloc:返回的秒存储指针
  • 返回值:成功则返回秒数,失败则返回-1,错误原因存在errno中。

time_t的类型:

typedeflongtime_t;

示例:

#include<stdio.h>#include<time.h>time_tget_utc_time(void){returntime(NULL);}intmain(int argc,char**argv){time_t utc_time =get_utc_time();printf("utc_time = %ld s\n", utc_time);return0;}
  • 2.4.localtime()
#include<time.h>structtm*localtime(consttime_t*timep);
  • 作用:将time_t类型的时间转换为struct tm类型的时间
  • timep:当前UTC秒数
  • 返回值:返回当地时间

tm结构体:

structtm{int tm_sec;/* Seconds. [0-60] (1 leap second) */int tm_min;/* Minutes. [0-59] */int tm_hour;/* Hours. [0-23] */int tm_mday;/* Day.  [1-31] */int tm_mon;/* Month. [0-11] 注意:0代表1月,以此类推*/int tm_year;/* Year - 1900.  该值为实际年份减去1900*/int tm_wday;/* Day of week. [0-6] 注意:0代表星期一,以此类推*/int tm_yday;/* Days in year.[0-365] 从每年的1月1日开始的天数,其中0代表1月1日,以此类推*/int tm_isdst;/* DST.  [-1/0/1] 夏玲时标识符*/};

示例:

#include<stdio.h>#include<time.h>time_tget_utc_time(void){returntime(NULL);}intmain(int argc,char**argv){time_t utc_time =get_utc_time();printf("utc_time = %ld s\n", utc_time);structtm*local_tm =localtime(&utc_time);printf("local time = %.4d-%.2d-%.2d %.2d:%.2d:%.2d\n", local_tm->tm_year +1900,
                                                           local_tm->tm_mon +1,
                                                           local_tm->tm_mday,
                                                           local_tm->tm_hour,
                                                           local_tm->tm_min,
                                                           local_tm->tm_sec);return0;}
  • 2.5.localtime_r()
#include<time.h>structtm*localtime_r(consttime_t*timep,structtm*result);
  • 作用:将time_t类型的时间转换为struct tm类型的时间
  • timep:当前UTC秒数
  • result:当地时间
  • 返回值:返回当地时间

注:
localtime不是一个线程安全的函数。对于实时性要求较高的系统,多个线程同时调用localtime,可能会造成数据被覆盖。使用localtime_r来替代。

示例:

#include<stdio.h>#include<time.h>time_tget_utc_time(void){returntime(NULL);}intmain(int argc,char**argv){time_t utc_time =get_utc_time();printf("utc_time = %ld s\n", utc_time);structtm result;structtm*local_tm =localtime_r(&utc_time,&result);printf("local time = %.4d-%.2d-%.2d %.2d:%.2d:%.2d\n", local_tm->tm_year +1900,
                                                           local_tm->tm_mon +1,
                                                           local_tm->tm_mday,
                                                           local_tm->tm_hour,
                                                           local_tm->tm_min,
                                                           local_tm->tm_sec);printf("result time = %.4d-%.2d-%.2d %.2d:%.2d:%.2d\n", result.tm_year +1900,
                                                            result.tm_mon +1,
                                                            result.tm_mday,
                                                            result.tm_hour,
                                                            result.tm_min,
                                                            result.tm_sec);return0;}
  • 2.6.gmtime()
#include<time.h>structtm*gmtime(consttime_t*timep);
  • 作用:返回tm结构的GMT时间(UTC时间)
  • timep:当前UTC秒数
  • 返回值:返回当地时间

例子:

#include<stdio.h>#include<time.h>time_tget_utc_time(void){returntime(NULL);}intmain(int argc,char**argv){time_t utc_time =get_utc_time();printf("utc_time = %ld s\n", utc_time);structtm*gmt_tm =gmtime(&utc_time);printf("gmt time = %.4d-%.2d-%.2d %.2d:%.2d:%.2d\n", gmt_tm->tm_year +1900,
                                                         gmt_tm->tm_mon +1,
                                                         gmt_tm->tm_mday,
                                                         gmt_tm->tm_hour,
                                                         gmt_tm->tm_min,
                                                         gmt_tm->tm_sec);return0;}

localtime和gmtime的区别:
localtime和gmtime都是C语言中的函数,用于将time_t类型的时间转换为struct tm类型的时间。它们的区别在于,gmtime将time_t转换为UTC时间,即世界标准时间,而localtime将time_t转换为本地时间。
例子:使用gmtime与localtime接口返回的小时数来计算当地时区

#include<stdio.h>#include<time.h>time_tget_utc_time(void){returntime(NULL);}intmain(int argc,char**argv){time_t utc_time =get_utc_time();printf("utc_time = %ld s\n", utc_time);structtm*gmt_tm =gmtime(&utc_time);printf("gmt time = %.4d-%.2d-%.2d %.2d:%.2d:%.2d\n", gmt_tm->tm_year +1900,
                                                         gmt_tm->tm_mon +1,
                                                         gmt_tm->tm_mday,
                                                         gmt_tm->tm_hour,
                                                         gmt_tm->tm_min,
                                                         gmt_tm->tm_sec);int gmt_hour = gmt_tm->tm_hour;structtm*local_tm =localtime(&utc_time);printf("local time = %.4d-%.2d-%.2d %.2d:%.2d:%.2d\n", local_tm->tm_year +1900,
                                                           local_tm->tm_mon +1,
                                                           local_tm->tm_mday,
                                                           local_tm->tm_hour,
                                                           local_tm->tm_min,
                                                           local_tm->tm_sec);int local_hour = local_tm->tm_hour;int local_time_zone = local_hour - gmt_hour;if(local_time_zone <-12){
        local_time_zone +=24;}elseif(local_time_zone >12){
        local_time_zone -=24;}else{}printf("local_time_zone = %d\n", local_time_zone);return0;}

3.内核态获取时间

  • 3.1.**do_gettimeofday()**(比较老的函数,新内核可能不存在了)
#include<linux/time.h>voiddo_gettimeofday(structtimeval*tv);
  • 作用:与C标准库中gettimeofday()用法相同
  • tv:当前UTC时间

timeval结构体:

structtimeval{
  __time_t tv_sec;/* Seconds.  秒*/
  __suseconds_t tv_usec;/* Microseconds.  微秒*/};

示例:

#include<linux/module.h>#include<linux/time.h>MODULE_LICENSE("GPL");int __init do_gettimeofday_init(void){printk("do_gettimeofday test begin.\n");structtimeval now={.tv_sec=0,.tv_usec=0};//声明一个变量do_gettimeofday(&now);//调用函数获取时间,此时间是距离1970-01-01 00:00:00的时间/*显示当前时间差*/printk("the seconds of the day is: %ld\n", now.tv_sec);//秒数printk("the microseconds of the day is: %ld\n", now.tv_usec);//微秒数printk("do_gettimeofday test over.\n");return0;}void __exit do_gettimeofday_exit(void){printk("Goodbye do_gettimeofday test\n");}module_init(do_gettimeofday_init);module_exit(do_gettimeofday_exit);
  • 3.2.基于ktime_t格式的时间 参考:linux kernel时钟获取
    1. ktime_get()#include"linux/ktime.h"ktime_tktime_get(void);``````作用:获取的是CLOCK_MONOTONIC时间。通过ktime_get获取的时间是不统计设备休眠时间的,并且这个时间统计的起始点则是设备启动后。返回值:返回ktime_t格式的数据类型,单位为纳秒。ktime_t的定义:typedef s64 ktime_t;示例:time_test_drv_initktime_t curTime =0;curTime =ktime_get();TIME_TEST_INFO("ktime_get:%lld ns", curTime);
    1. ktime_get_ts64()#include"linux/time64.h"voidktime_get_ts64(structtimespec64*ts);``````作用:和ktime_get的功能是完全一样的,区别在于对时间的表示数据类型由ktime_t变成了timespec64。timespec64的定义如下:structtimespec64{time64_t tv_sec;/* seconds */long tv_nsec;/* nanoseconds */};timespec64中包含了秒和纳秒,相对ktime_t纳秒这个时间表示更加适合人类查看.示例:staticvoidshow_time_ts64(constchar* caller,constint line,conststructtimespec64*curTimeTs){pr_info("%s,%d:%lld s %ld ns\n", caller,__LINE__, curTimeTs->tv_sec, curTimeTs->tv_nsec);}time_test_drv_initstructtimespec64 curTimeTs;ktime_get_boottime_ts64(&curTimeTs);show_time_ts64(__func__,__LINE__,&curTimeTs);
  • 3.ktime_get_boottime()staticinlinektime_tktime_get_boottime(void);``````作用:ktime_get_boottime获取的时间和ktime_get最大的不同是其包含了设备进入休眠的时间,其这个时间统计的起始点也是设备启动后。返回值:返回值类型为ktime_t,单位为纳秒。示例:time_test_drv_initktime_t curTime =0;curTime =ktime_get_boottime();TIME_TEST_INFO("ktime_get_boottime:%lld ns", curTime);
  • 4.ktime_get_boottime_ts()voidktime_get_boottime_ts64(structtimespec64*);``````作用:ktime_get_boottime_ts相对于ktime_get_boottime的功能是完全一样的,区别在于对时间的表示数据类型由ktime_t变成了timespec64。
  • 5.ktime_get_real()ktime_tktime_get_real(void);``````作用:ktime_get_real获取的时间的起点不是设备的启动时间点了,而是相对UTC的,即从1970开始。 示例:time_test_drv_initktime_t curTime =0;curTime =ktime_get_real();TIME_TEST_INFO("ktime_get_real:%lld ns", curTime);
  • 6.ktime_get_real_ts()voidktime_get_real_ts(structtimespec64*);``````作用:ktime_get_real_ts相对于ktime_get_real的功能是完全一样的,区别在于对时间的表示数据类型由ktime_t变成了timespec64。 示例:time_test_drv_initstructtimespec64 curTimeTs;ktime_get_real_ts64(&curTimeTs);
  • 7.汇总- 7.1.返回ktime_t的时间接口:接口时间体系说明ktime_t ktime_get( void );CLOCK_MONOTONIC以系统启动的时间点为时间原点的时间体系ktime_t ktime_get_boottime( void );CLOCK_BOOTTIME以系统启动的时间点为时间原点的时间体系ktime_t ktime_get_real( void );CLOCK_REALTIME自然时间体系ktime_t ktime_get_clocktai( void );CLOCK_TAI自然时间体系ktime_t ktime_get_raw( void );CLOCK_MONOTONIC_RAW以系统启动的时间点为时间原点的时间体系- CLOCK_REALTIMECLOCK_TAI,这两个都是自然时间体系,不同的是后者不受闰秒的影响。不过由于用户空间可能会设置时间,NTP也会调节自然时间,所以这两个时间体系都有可能会产生回跳。- CLOCK_BOOTTIMECLOCK_MONOTONICCLOCK_MONOTONIC_RAW,这三个都是以系统启动的时间点为时间原点的时间体系。CLOCK_BOOTTIME、CLOCK_MONOTONIC,不同点是前者不会在系统休眠时暂停走时而后者会,相同点是两者都受NTP的影响。CLOCK_MONOTONIC_RAW和CLOCK_MONOTONIC基本相同,但是前者不受NTP的影响。- 7.2.返回u64(纳秒)的时间接口:接口时间体系说明u64 ktime_get_ns( void );CLOCK_MONOTONIC以系统启动的时间点为时间原点的时间体系u64 ktime_get_boottime_ns( void );CLOCK_BOOTTIME以系统启动的时间点为时间原点的时间体系u64 ktime_get_real_ns( void );CLOCK_REALTIME自然时间体系u64 ktime_get_clocktai_ns( void );CLOCK_TAI自然时间体系u64 ktime_get_raw_ns( void );CLOCK_MONOTONIC_RAW以系统启动的时间点为时间原点的时间体系- 7.3.返回timespec64的时间接口:接口时间体系说明void ktime_get_ts64( struct timespec64 * );CLOCK_MONOTONIC以系统启动的时间点为时间原点的时间体系void ktime_get_boottime_ts64( struct timespec64 * );CLOCK_BOOTTIME以系统启动的时间点为时间原点的时间体系void ktime_get_real_ts64( struct timespec64 * );CLOCK_REALTIME自然时间体系void ktime_get_clocktai_ts64( struct timespec64 * );CLOCK_TAI自然时间体系void ktime_get_raw_ts64( struct timespec64 * );CLOCK_MONOTONIC_RAW以系统启动的时间点为时间原点的时间体系#include"linux/time64.h"structtimespec64{time64_t tv_sec;/* seconds */long tv_nsec;/* nanoseconds */};- 7.4.返回time64_t(秒数)的时间接口:接口时间体系说明time64_t ktime_get_seconds( void );CLOCK_MONOTONIC以系统启动的时间点为时间原点的时间体系time64_t ktime_get_boottime_seconds( void );CLOCK_BOOTTIME以系统启动的时间点为时间原点的时间体系time64_t ktime_get_real_seconds( void );CLOCK_REALTIME自然时间体系time64_t ktime_get_clocktai_seconds( void );CLOCK_TAI自然时间体系time64_t ktime_get_raw_seconds( void );CLOCK_MONOTONIC_RAW以系统启动的时间点为时间原点的时间体系- 7.5.返回粗略ktime_t的时间接口:接口时间体系说明ktime_t ktime_get_coarse( void );CLOCK_MONOTONIC以系统启动的时间点为时间原点的时间体系ktime_t ktime_get_coarse_boottime( void );CLOCK_BOOTTIME以系统启动的时间点为时间原点的时间体系ktime_t ktime_get_coarse_real( void );CLOCK_REALTIME自然时间体系ktime_t ktime_get_coarse_clocktai( void );CLOCK_TAI自然时间体系- 7.6.返回粗略u64(纳秒数)的时间接口:接口时间体系说明u64 ktime_get_coarse_ns( void );CLOCK_MONOTONIC以系统启动的时间点为时间原点的时间体系u64 ktime_get_coarse_boottime_ns( void );CLOCK_BOOTTIME以系统启动的时间点为时间原点的时间体系u64 ktime_get_coarse_real_ns( void );CLOCK_REALTIME自然时间体系u64 ktime_get_coarse_clocktai_ns( void );CLOCK_TAI自然时间体系- 7.7.返回粗略timespec64的时间接口:接口时间体系说明void ktime_get_coarse_ts64( struct timespec64 * );CLOCK_MONOTONIC以系统启动的时间点为时间原点的时间体系void ktime_get_coarse_boottime_ts64( struct timespec64 * );CLOCK_BOOTTIME以系统启动的时间点为时间原点的时间体系void ktime_get_coarse_real_ts64( struct timespec64 * );CLOCK_REALTIME自然时间体系void ktime_get_coarse_clocktai_ts64( struct timespec64 * );CLOCK_TAI自然时间体系- 7.8.返回fast u64(纳秒数)的时间接口:接口时间体系说明u64 ktime_get_mono_fast_ns( void );CLOCK_MONOTONIC以系统启动的时间点为时间原点的时间体系u64 ktime_get_boot_fast_ns( void);CLOCK_BOOTTIME以系统启动的时间点为时间原点的时间体系u64 ktime_get_real_fast_ns( void );CLOCK_REALTIME自然时间体系u64 ktime_get_raw_fast_ns( void );CLOCK_MONOTONIC_RAW以系统启动的时间点为时间原点的时间体系

4.自己实现的延时函数示例

voiddelay_us(uint32_t nus){volatileuint32_t startts, endts, ts;

    ts = nus;

    startts =get_time_us();
    endts = startts + ts;if(endts > startts){while(get_time_us()< endts);}else{while(get_time_us()> endts);while(get_time_us()< endts);}}

5.有关__clock_id的详细说明

clockid

的取值常用的有以下:

  • CLOCK_REALTIME,就是我们所说的自然时间,由于计算机上的时间有可能不准,所以是可以设置的,所以有可能会产生跳跃。后面所有的时间体系都是不可设置的,下面不再一一说明了。CLOCK_REALTIME_ALARM、CLOCK_REALTIME_COARSE、CLOCK_TAI虽然本身是不可设置的,但是都会受到CLOCK_REALTIME设置的影响,有可能会产生跳跃。
  • CLOCK_REALTIME_ALARM,和CLOCK_REALTIME相同,在定时器设置时才有用,ALARM代表的是定时设置,如果目标定时时间到了的时候系统在休眠,会唤醒系统。
  • CLOCK_REALTIME_COARSE,和CLOCK_REALTIME相同,精度不高但是获取比较快。
  • CLOCK_TAI,和CLOCK_REALTIME相同,但是不考虑闰秒问题,TAI是International Atomic Time的反向简称。
  • CLOCK_MONOTONIC,由于前面几个时间体系都有可能会产生回跳,计算机中需要有一个单调递增的时间体系。此时间体系的时间原点并不重要,在Linux中是以系统启动的时间点作为时间原点,在计算机休眠时会暂停走时,受adjtime和NTP的影响可能会向前跳跃。
  • CLOCK_MONOTONIC_COARSE,同上,但是精度降低,访问更快。
  • CLOCK_MONOTONIC_RAW,同CLOCK_MONOTONIC,但是不受adjtime和NTP的影响。
  • CLOCK_BOOTTIME,以系统启动时间为时间原点的时间体系,不受其它因素的影响,计算机休眠时依然走时。
  • CLOCK_BOOTTIME_ALARM,同上,在定时器设置时才有用,ALARM代表的是定时设置,如果目标定时时间到了的时候系统在休眠,会唤醒系统。
  • CLOCK_PROCESS_CPUTIME_ID,以进程创建时间为时间原点,进程运行时走时,进程休眠时暂停走时。
  • CLOCK_THREAD_CPUTIME_ID,以线程创建时间为时间原点,线程运行时走时,线程休眠时暂停走时。
标签: linux c语言

本文转载自: https://blog.csdn.net/weixin_40837318/article/details/131150265
版权归原作者 zhang-ge 所有, 如有侵权,请联系我们删除。

“Linux C编程基础:获取时间”的评论:

还没有评论