Linux C编程基础:获取时间
时间:2024-03-31 13:45:35 来源:网络cs 作者:欧阳逸 栏目:其他工具 阅读:
1.前言
对于linux下的编程,无论是用户态还是内核态,时间获取都是经常需要使用到的。以下分别从用户态和内核态整理了几个常用的时间获取接口,供编写代码时快速查阅。
linux时间子系统的发展历史及详细介绍,可以参考:深入理解Linux时间子系统。
2.用户态获取时间
2.1 clock_gettime()#include <time.h>int clock_gettime (clockid_t __clock_id, struct timespec *__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
结构体:
struct timespec{ __time_t tv_sec; /* Seconds. 秒 */ __syscall_slong_t tv_nsec; /* Nanoseconds. 纳秒*/};
示例:
#include <stdio.h>#include <string.h>#include <time.h>long long get_clock_sys_time_ns(void){ struct timespec tp; long long time_ns = 0; clock_gettime(CLOCK_MONOTONIC, &tp); time_ns = (long long)tp.tv_sec * 1000000000 + tp.tv_nsec; return time_ns;}int main(void){ struct timespec tp; ///< 获取从1970年1月1日到目前的时间 memset(&tp, 0, sizeof(struct timespec)); 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(struct timespec)); 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(struct timespec)); 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(struct timespec)); 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); return 0;}
2.2.gettimeofday() #include <sys/time.h>int gettimeofday(struct timeval *tv, struct timezone *tz);
作用
:获取当前时间(从1970年1月1日到目前的时间)tv
:当前UTC时间tz
:当前时区信息返回值
:成功则返回0,失败则返回-1 timeval结构体:
struct timeval{ __time_t tv_sec; /* Seconds. 秒*/ __suseconds_t tv_usec; /* Microseconds. 微秒*/};
timezone结构体:
struct timezone { 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>long long get_sys_time_ms(void){ long long time_ms = 0; struct timeval tv; gettimeofday(&tv, NULL); time_ms = ((long long)tv.tv_sec*1000000 + tv.tv_usec) / 1000; return time_ms;}int main(void){ ///< 获取系统时间 printf("sys_time = %lld ms\n", get_sys_time_ms()); return 0;}
2.3.time() #include <time.h>time_t time(time_t *tloc);
作用
:获取1970-01-01 00:00:00 +0000至今的秒数(UTC)tloc
:返回的秒存储指针返回值
:成功则返回秒数,失败则返回-1,错误原因存在errno中。 time_t的类型:
typedef long time_t;
示例:
#include <stdio.h>#include <time.h>time_t get_utc_time(void){ return time(NULL);}int main(int argc, char **argv){ time_t utc_time = get_utc_time(); printf("utc_time = %ld s\n", utc_time); return 0;}
2.4.localtime() #include <time.h>struct tm *localtime(const time_t *timep);
作用
:将time_t类型的时间转换为struct tm类型的时间timep
:当前UTC秒数返回值
:返回当地时间 tm结构体:
struct tm{ 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_t get_utc_time(void){ return time(NULL);}int main(int argc, char **argv){ time_t utc_time = get_utc_time(); printf("utc_time = %ld s\n", utc_time); struct tm *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); return 0;}
2.5.localtime_r() #include <time.h>struct tm *localtime_r(const time_t *timep, struct tm *result);
作用
:将time_t类型的时间转换为struct tm类型的时间timep
:当前UTC秒数result
:当地时间返回值
:返回当地时间 注:
localtime不是一个线程安全的函数。对于实时性要求较高的系统,多个线程同时调用localtime,可能会造成数据被覆盖。使用localtime_r来替代。
示例:
#include <stdio.h>#include <time.h>time_t get_utc_time(void){ return time(NULL);}int main(int argc, char **argv){ time_t utc_time = get_utc_time(); printf("utc_time = %ld s\n", utc_time); struct tm result; struct tm *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); return 0;}
2.6.gmtime() #include <time.h>struct tm *gmtime(const time_t *timep);
作用
:返回tm结构的GMT时间(UTC时间)timep
:当前UTC秒数返回值
:返回当地时间 例子:
#include <stdio.h>#include <time.h>time_t get_utc_time(void){ return time(NULL);}int main(int argc, char **argv){ time_t utc_time = get_utc_time(); printf("utc_time = %ld s\n", utc_time); struct tm *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); return 0;}
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_t get_utc_time(void){ return time(NULL);}int main(int argc, char **argv){ time_t utc_time = get_utc_time(); printf("utc_time = %ld s\n", utc_time); struct tm *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; struct tm *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; } else if (local_time_zone > 12) { local_time_zone -= 24; }else{} printf("local_time_zone = %d\n", local_time_zone); return 0;}
3.内核态获取时间
3.1.do_gettimeofday()(比较老的函数,新内核可能不存在了)#include <linux/time.h> void do_gettimeofday(struct timeval *tv);
作用
:与C标准库中gettimeofday()用法相同tv
:当前UTC时间 timeval结构体:
struct timeval{ __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"); struct timeval 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"); return 0;}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时钟获取
#include "linux/ktime.h"ktime_t ktime_get(void);
作用
:获取的是CLOCK_MONOTONIC时间。通过ktime_get获取的时间是不统计设备休眠时间的,并且这个时间统计的起始点则是设备启动后。
返回值
:返回ktime_t格式的数据类型,单位为纳秒。
ktime_t的定义:
typedef s64ktime_t;
示例:
time_test_drv_initktime_t curTime = 0;curTime = ktime_get();TIME_TEST_INFO("ktime_get:%lld ns", curTime);
ktime_get_ts64() #include "linux/time64.h"void ktime_get_ts64(struct timespec64 *ts);
作用
:和ktime_get的功能是完全一样的,区别在于对时间的表示数据类型由ktime_t变成了timespec64。
timespec64的定义如下:
struct timespec64 {time64_ttv_sec;/* seconds */longtv_nsec;/* nanoseconds */};
timespec64中包含了秒和纳秒,相对ktime_t纳秒这个时间表示更加适合人类查看.
示例:
static void show_time_ts64(const char* caller, const int line, const struct timespec64 *curTimeTs){pr_info("%s,%d:%lld s %ld ns\n", caller, __LINE__, curTimeTs->tv_sec, curTimeTs->tv_nsec);}time_test_drv_initstruct timespec64 curTimeTs;ktime_get_boottime_ts64(&curTimeTs);show_time_ts64(__func__, __LINE__, &curTimeTs);
3.ktime_get_boottime()
static inline ktime_t ktime_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()
void ktime_get_boottime_ts64(struct timespec64 *);
作用
:ktime_get_boottime_ts相对于ktime_get_boottime的功能是完全一样的,区别在于对时间的表示数据类型由ktime_t变成了timespec64。
5.ktime_get_real()
ktime_t ktime_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()
void ktime_get_real_ts(struct timespec64 *);
作用
:ktime_get_real_ts相对于ktime_get_real的功能是完全一样的,区别在于对时间的表示数据类型由ktime_t变成了timespec64。
示例:
time_test_drv_initstruct timespec64 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_REALTIME和CLOCK_TAI,这两个都是自然时间体系,不同的是后者不受闰秒的影响。不过由于用户空间可能会设置时间,NTP也会调节自然时间,所以这两个时间体系都有可能会产生回跳。
CLOCK_BOOTTIME、CLOCK_MONOTONIC和CLOCK_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 | 以系统启动的时间点为时间原点的时间体系 |
接口 | 时间体系 | 说明 |
---|---|---|
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"struct timespec64 {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 | 以系统启动的时间点为时间原点的时间体系 |
接口 | 时间体系 | 说明 |
---|---|---|
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 | 自然时间体系 |
接口 | 时间体系 | 说明 |
---|---|---|
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 | 自然时间体系 |
接口 | 时间体系 | 说明 |
---|---|---|
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 | 自然时间体系 |
接口 | 时间体系 | 说明 |
---|---|---|
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.自己实现的延时函数示例
void delay_us(uint32_t nus){ volatile uint32_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,以线程创建时间为时间原点,线程运行时走时,线程休眠时暂停走时。
本文链接:https://www.kjpai.cn/news/2024-03-31/151533.html,文章来源:网络cs,作者:欧阳逸,版权归作者所有,如需转载请注明来源和作者,否则将追究法律责任!