一、前言
本文将简单介绍unix系统中时间相关的概念,包括日历时间和进程时间两种不同的时间值。
二、日历时间
日历时间是指从1970年1月1日 00:00:00这个时间点开始,到当前所经过的秒数累计值。将这个秒数值经过换算就能得到当前时间。日历时间常用来记录文件最近一次的修改时间。
系统基本数据类型time_t用于保存这种时间值。
2.1 日历时间相关的库函数和系统调用
在Unix系统中,与日历时间相关的库函数和系统调用主要集中在标准C库(libc)和POSIX标准中。以下是一些常用的函数和调用:
1.time()
功能:获取当前时间,返回自纪元以来的秒数
头文件:<time.h>
原型:time_t time(time_t *tloc);
参考代码:
#include <stdio.h>
#include <time.h>
int main() {
time_t now;
time(&now);
printf("The current time is: %ld\n", now);
printf("The current time is: %s", ctime(&now));
return 0;
}
运行结果如下所示:
(2.1-1)
2.localtime()
功能:将time_t类型的时间转换为本地时间的tm结构体。 头文件:<time.h>
原型:struct tm *localtime(const time_t *timep);
先来看下tm结构体的定义:
#include <time.h>
struct tm {
int tm_sec; // 秒,范围从 0 到 60(偶尔会出现闰秒)
int tm_min; // 分,范围从 0 到 59
int tm_hour; // 时,范围从 0 到 23
int tm_mday; // 一月中的第几天,范围从 1 到 31
int tm_mon; // 月份,范围从 0 到 11(0 表示一月)
int tm_year; // 年份,从 1900 年开始的年数
int tm_wday; // 一周中的第几天,范围从 0 到 6(0 表示星期天)
int tm_yday; // 一年中的第几天,范围从 0 到 365
int tm_isdst; // 夏令时标志,正数表示夏令时有效,0 表示无效,负数表示信息不可用
};
参考代码:
#include <stdio.h>
#include <time.h>
int main() {
time_t now;
struct tm *ltm;
time(&now);
ltm = localtime(&now);
printf("The current time is: %ld\n", now);
printf("The current time is: %s", ctime(&now));
printf("The current time is: %d-%d-%d %d:%d:%d %d %d %d\n", ltm->tm_year + 1900, ltm->tm_mon + 1, ltm->tm_mday, ltm->tm_hour, ltm->tm_min, ltm->tm_sec,ltm->tm_wday, ltm->tm_yday, ltm->tm_isdst);
return 0;
}
运行结果如下:
(2.1-2)
3.gmtime()
功能:将time_t类型的时间转换为UTC时间的tm结构体。 头文件:<time.h>
原型:struct tm *gmtime(const time_t *timep);
参考代码:
#include <stdio.h>
#include <time.h>
int main() {
time_t now;
struct tm *ltm;
now = time(NULL);
ltm = gmtime(&now);
printf("The current time is: %ld\n", now);
printf("The current time is: %s", ctime(&now));
printf("The current time is: %d-%d-%d %d:%d:%d %d %d %d\n", ltm->tm_year + 1900, ltm->tm_mon + 1, ltm->tm_mday, ltm->tm_hour, ltm->tm_min, ltm->tm_sec,ltm->tm_wday, ltm->tm_yday, ltm->tm_isdst);
return 0;
}
运行结果如下所示:
(2.1-3)
需要注意的是,只有当系统的时间设置为UTC时间,通过该库函数返回的结果才是UTC时间。
4.mktime()
功能:将tm结构体表示的时间转换为time_t类型的时间。 头文件:<time.h>
原型:time_t mktime(struct tm *tm);
参考代码:
#include <stdio.h>
#include <time.h>
int main() {
struct tm ltm;
time_t current_time;
ltm.tm_year = 2024 - 1900;
ltm.tm_mon = 8 - 1;
ltm.tm_mday = 27;
ltm.tm_hour = 10;
ltm.tm_min = 40;
ltm.tm_sec = 0;
ltm.tm_isdst = -1;
current_time = mktime(<m);
printf("The current time is: %ld\n", current_time);
printf("The current time is: %s", ctime(¤t_time));
return 0;
}
运行结果如下所示:
(2.1-4)
5.strftime()
功能:将tm结构体表示的时间格式化为字符串。
头文件:<time.h>
原型:size_t strftime(char *s, size_t
max, const char *format, const struct tm *tm);
参考代码:
#include <stdio.h>
#include <time.h>
int main()
{
time_t current_time;
struct tm *ltm;
char time_str[50];
time(¤t_time);
ltm = localtime(¤t_time);
strftime(time_str, sizeof(time_str), "%Y-%m-%d_%H:%M:%S", ltm);
printf("The current time is: %ld\n", current_time);
printf("The current time is: %d-%d-%d %d:%d:%d %d %d %d\n", ltm->tm_year + 1900, ltm->tm_mon + 1, ltm->tm_mday, ltm->tm_hour, ltm->tm_min, ltm->tm_sec,ltm->tm_wday, ltm->tm_yday, ltm->tm_isdst);
printf("The current time is: %s\n", time_str);
return 0;
}
运行结果如下:
(2.1-5)
6.ctime()
功能:将time_t类型的时间转换为可读的字符串格式。
头文件:<time.h>
原型:char *ctime(const time_t *timep);
上面的参考代码中已经包含了此用例。
上面的是库函数,接下来将介绍的是系统调用:
7.gettimeofday()
功能:获取当前时间,包括秒和微秒
头文件:<sys/time.h>
原型:int gettimeofday(struct timeval *tv, struct timezone *tz);
这里的timeval结构定义如下:
#include <sys/time.h>
struct timeval {
time_t tv_sec; // 秒
suseconds_t tv_usec; // 微秒
};
参考代码:
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
int main()
{
struct timeval tv;
struct timezone tz;
gettimeofday(&tv, &tz);
printf("The current time is: %ld.%ld %d\n", tv.tv_sec, tv.tv_usec, tz.tz_dsttime);
printf("%s", ctime(&tv.tv_sec));
return 0;
}
运行结果如下所示:
(2.1-6)
8.settimeofday()
功能:设置系统时间和时区。
头文件:<sys/time.h>
原型:int settimeofday(const struct timeval *tv, const struct timezone *tz);
参考代码:
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
int main()
{
struct timeval tv;
struct timezone tz;
gettimeofday(&tv, &tz);
printf("The current time is: %ld.%ld %d\n", tv.tv_sec, tv.tv_usec, tz.tz_dsttime);
printf("%s", ctime(&tv.tv_sec));
tv.tv_sec += 180;
if(settimeofday(&tv, NULL) == -1) {
perror("settimeofday");
return 1;
}
gettimeofday(&tv, &tz);
printf("The new time is: %ld.%ld %d\n", tv.tv_sec, tv.tv_usec, tz.tz_dsttime);
printf("%s", ctime(&tv.tv_sec));
return 0;
}
运行结果如下所示:
(2.1-7)
提示我们没有权限去修改系统的时间。可以尝试使用root用户来执行程序,即加上sudo。
9.clock_gettime()
功能:获取高精度时间。
头文件:<time.h>
原型:int clock_gettime(clockid_t clk_id, struct
timespec *tp);
- clk_id:表示要获取的时钟类型,常用的时钟类型包括:
- CLOCK_REALTIME:系统实时时间,从Epoch(1970年1月1日00:00:00 UTC)开始计算。
- CLOCK_MONOTONIC:单调递增的时间,不受系统时间调整的影响。
- CLOCK_PROCESS_CPUTIME_ID:进程的CPU时间。
- CLOCK_THREAD_CPUTIME_ID:线程的CPU时间。
- tp:指向 struct timespec 结构体的指针,用于存储返回的时间值。
返回值
- 成功时返回0。
- 失败时返回-1,并设置 errno 以指示错误类型。
struct timespec 结构体
struct timespec 结构体用于表示时间,包括秒和纳秒:
struct timespec {
time_t tv_sec; // 秒
long tv_nsec; // 纳秒
};
10.clock_settime()
功能:设置高精度时间。
头文件:<time.h>
原型:int clock_settime(clockid_t clk_id, const
struct timespec *tp);
三、进程时间
进程时间也被称为cpu时间,是用来度量进程使用的中央处理器资源的时间。简单来说,就是进程从开始到结束这段时间里,处理器用于处理该进程的时间片的总和。进程时间以时钟滴答计算,也就是说进程时间是通过时钟来计算的。
系统基本类型clock_t用于保存这种时间值。
当度量一个进程的执行时间时,unix系统用3个进程时间值来维护:时钟时间、用户cpu时间,系统cpu时间。
时钟时间:是进程运行的时间总量,其值与系统中同时运行的进程数有关。
用户cpu时间:用户cpu时间指执行用户指令的时间,也就是执行内核操作以外的时间。
系统cpu时间:指为该进程执行内核程序所用的时间。
用户cpu时间和系统cpu时间被称为cpu时间。一般cpu时间是小于时钟时间的,因为同一时间系统一般都不仅有一个进程在运行。
3.1 如何获取进程时间
要取得任意进程的进程时间,可以直接通过time命令,如下所示:
(3.1-1)
从上图所示,时钟时间大于cpu时间。
参考资料:
《UNIX环境高级编程(第3版) (史蒂文斯 (W.Richard Stevens) 拉戈 (Stephen A.Rago))
(Z-Library)》