Linux系统“时间”简介

简介: Linux系统提供了很多关于time的处理API,这些API各自的功能和使用场景都有所不同。对于初学者有时会混淆它们,对于API的具体含义理解不到位。本文总结各类time相关的API的使用方式。 分类的标准按照函数的具体功能进行划分。

时间获取、设置


time


函数原型:


time_t time(time_t *t);


基本功能描述


  1. time()的基本功能为返回相对于UTC(1970-01-01 00:00:00 +0000)所经过的秒数。注意,如果t不为NULL的话,t必须有效的内存地址。一般情况下,t设置NULL。


get/set time


函数原型


int gettimeofday(struct timeval *tv, struct timezone *tz); int settimeofday(const struct timeval *tv, const struct timezone *tz);


truct timeval {     time_t      tv_sec;     /* seconds */     suseconds_t tv_usec;    /* microseconds */ };


基本功能描述


gettimeofday()和settimeofday()的基本功能为获取-设置系统时间,设置或者获取时间都保存在timeval结构中。其中,gettimeofday返回时间与time(2)的类似,都是相对于UTC所经过的


秒数(tv_sec),不过它更为精确,还包括一个毫秒(tv_usec)。


注意事项


使用gettimeofday时,需要有几点注意的地方:


  1. 当tv或者tz为NULL, 相应的结构不会被设置或返回。


  1. timezone结构已废弃,所以tz应该被设置为NULL(PS:经测试Linux系统不处理settimeofday中的tz参数)。


  1. 对于tv的算书运算宏可以参考(man 2 timeradd)


格式化时间、日期


ctime函数族


函数原型


char *asctime(const struct tm *tm);
    char *asctime_r(const struct tm *tm, char *buf);
    char *ctime(const time_t *timep);
    char *ctime_r(const time_t *timep, char *buf);
    struct tm *gmtime(const time_t *timep);
    struct tm *gmtime_r(const time_t *timep, struct tm *result);
    struct tm *localtime(const time_t *timep);
    struct tm *localtime_r(const time_t *timep, struct tm *result);
    time_t mktime(struct tm *tm);


基本功能描述


  1. ctime函数族的主要功能是将时间和日期转换为struct tm结构或者ASCII字符串。


  1. ctime、gmtime、localtime的参数为time_t,该值一般表示日历时间,一般通过time(2)获得该值。


  1. asctime、mktime的参数为struct tm结构,该结构的定义如下:


tm_sec    The number of seconds after the minute, normally in the range 0 to 59, but can be up to 60 to allow for leap seconds.
tm_min    The number of minutes after the hour, in the range 0 to 59.
tm_hour   The number of hours past midnight, in the range 0 to 23.
tm_mday   The day of the month, in the range 1 to 31.
tm_mon    The number of months since January, in the range 0 to 11.
tm_year   The number of years since 1900.
tm_wday   The number of days since Sunday, in the range 0 to 6.
tm_yday   The number of days since January 1, in the range 0 to 365.
tm_isdst  A flag that indicates whether daylight saving time is in effect at the time described.  The value is positive if daylight saving time is in effect, zero if it is not, and negative if
the information is not available.


注意,week的0-6代表"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", and "Sat";


  1. ctime(t)与asctime(localtime(t))的效果一样,都是将日历时间转换为类似于下面的C String:

'Wed Jan  9 15:08:05 2019\n'


注意,ctime返回的时间字符串为当前时区的时间。


ctime_r为ctime的线程安全版本,参数buf的大小至少为26字节。


  1. gmtime将日历时间转换为tm结构,注意该值为UTC时间(PS:所以gmtime以GMT开头)。gmtime的返回值指向一块静态内存,所以该值会被其他时间设置函数设置的值所覆盖。


为了避免该问题,我们可以使用线程安全的gmtime_r,该函数会将转换结果保存到result缓存中。


  1. localtime同样将日历时间转换为tm结构,该值为相对于用户本地时区的值。localtime线程不安全,多线程环境下,建议使用localtime_r。


  1. asctime将tm中保存的信息转换为C String, 其会简单的将tm中的信息拼接成字符串返回。asctime县城不安全,多线程环境下,建议使用asctime_r。


  1. mktime将保存这本地时间的tm转换为日历时间(UTC时间)。


注意事项


  1. POSIX.1-2001.  C89 and C99 定义了 asctime(), ctime(), gmtime(), localtime(), and mktime().


POSIX.1-2008 标注 asctime(), asctime_r(), ctime(), and ctime_r() 已废弃, 推荐使用strftime.


  1. 按照POSIX.1-2004的说明,localtime()使用之前需要调用一下tzset(),localtime_r()没有这个要求,为了可移植性,建议再使用localtime()之前调用tzset().


strftime函数


函数原型


size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);


strftime主要用于格式化日期和时间,其将保存在tm中的时间信息,按照format的参数进行格式化,并将格式化后的数据保存到大小为max的缓冲区中。format的具体参数比较多(参考man strftime(2)).


#include <time.h>
#include <stdio.h>
#include <stdlib.h>
    int
main(int argc, char *argv[])
{
    char outstr[200];
    time_t t;
    struct tm *tmp;
    t = time(NULL);
    tmp = localtime(&t);
    if (tmp == NULL) {
        perror("localtime");
        exit(EXIT_FAILURE);
    }
    if (strftime(outstr, sizeof(outstr), argv[1], tmp) == 0) {
        fprintf(stderr, "strftime returned 0");
        exit(EXIT_FAILURE);
    }
    printf("Result string is \"%s\"\n", outstr);
    exit(EXIT_SUCCESS);
}
$./a.out "%F %T"
Result string is "2019-01-09 16:51:25"


clock获取、设置


函数原型


int clock_getres(clockid_t clk_id, struct timespec *res);
int clock_gettime(clockid_t clk_id, struct timespec *tp);
int clock_settime(clockid_t clk_id, const struct timespec *tp);


基本功能描述


  1. clock_getres返回clk_id的时间精度,如果res不为NULL,那查询到的结果将保存到res中。


  1. clock_gettime和clock_settime获取、设置特定clk_id的时间。res、tp的数据结构如下:


struct timespec {
    time_t   tv_sec;        /* seconds */
    long     tv_nsec;       /* nanoseconds */
};


  1. clk_id代表系统中特定的时钟。时钟既可以是系统全局并且所有进程都可见的,或者,其测量的时间只针对于某一个进程。CLOCK_REALTIME在所有的实现中,都是系统可见的,它表示相对于UTC所经过的秒数和纳秒。


当系统时间改变时,对于基于相对时间的定时器,其时间不会受影响,但是对于基于绝对时间的定时器,其时间将受到影响。


  1. 下面为glic和Linux kernel所支持的clocks:



  • CLOCK_REALTIME: 系统范围内的实时时钟。配置该clock下需要root权限。该clock会受到系统时间不连续跳跃的影响(例如,系统管理员手动修改系统时间),同时也会因为adjtime、NTP校准时间而受到影响。


所以对于定时任务,不应该通过该clock获取基础时间。前面所说的gettimeofday、time等函数所依据的clock就是CLOCK_REALTIME。


  • CLOCK_REALTIME_COARSE(Linux特有属性,Linux 2.6.32开始加入): 相对于CLOCK_REALTIME,该clock更快,但精度较低。常用于能够快速获取时间但对精度要求不高的timestamp。


  • CLOCK_MONOTONIC: 无法设置的clock,表示从某个未指定的起始点开始的单调时间.该clock不受系统时间不连续跳转的影响(例如,系统管理员手动修改clock),但会受到adjtime(3)和NTP增量校准时间的影响。 对于定时任务,如果精度要求不高可以选用该clock获取基准时间,对时间精度要求较高的定时任务会受到adjtime和NTP校准时间的影响。


  • CLOCK_MONOTONIC_COARSE(Linux特有属性,Linux 2.6.32开始加入): 与CLOCK_REALTIME_COARSE类似,其性能较好,但精度较差。


  • CLOCK_MONOTONIC_RAW(Linux特有属性,Linux 2.6.32开始加入): 与CLOCK_MONOTONIC类似,但是其时间基于硬件时钟,所以,其不会受到adjtime(3)和NTP增量校准时间的影响。 定时任务基准时间可以通过该clock过获取。


  • CLOCK_BOOTTIME(Linux特有属性,Linux 2.6.39开始加入): 与CLOCK_MONOTONIC类似,而且该clock记录的时间包括系统挂起过程中所经过的时间。该clock为那些想要获取包括系统挂起时间而且不用为CLOCK_REALTIME(会受到settimeofday影响)所带来的并发症 所困扰的应用,提供了解决方案。定时任务可以通过该clock获取基础时间。


  • CLOCK_PROCESS_CPUTIME_ID(Linux 2.6.12开始加入): 来自CPU的定时器,高分辨率、进程独有。用于统计单个进程所使用的CPU时间。


  • CLOCK_THREAD_CPUTIME_ID(Linux 2.6.12开始加入): 线程相关的CPU-time clock。 用于统计单个线程所使用的CPU时间。


CLOCK_PROCESS_CPUTIME_ID和CLOCK_THREAD_CPUTIME_ID用于当我们进行系统中各个应用程序的性能分析和统计的时候。下面的程序用于获取制定进程的CPU-TIME:


#define _XOPEN_SOURCE 600 
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <time.h>
    int 
    main(int argc, char *argv[])
    {
        clockid_t clockid;
        struct timespec ts; 
        if (argc != 2) {
            fprintf(stderr, "%s <process-ID>\n", argv[0]);
            exit(EXIT_FAILURE);
        }   
        if (clock_getcpuclockid(atoi(argv[1]), &clockid) != 0) {
            perror("clock_getcpuclockid");
            exit(EXIT_FAILURE);
        }   
        if (clock_gettime(clockid, &ts) == -1) {
            perror("clock_gettime");
            exit(EXIT_FAILURE);
        }   
        printf("CPU-time clock for PID %s is %ld.%09ld seconds\n", argv[1], (long) ts.tv_sec, (long) ts.tv_nsec);
            exit(EXIT_SUCCESS);
    }


下面为Linux kernel和glic所支持的clock的比较表:


clock_id clock_scope can_set settimeofday-unaffected adjtime/NTP-unaffected performance precise
CLOCK_REALTIME system-wide YES NO NO lower higher
CLOCK_REALTIME_COARSE system-wide YES NO NO higher lower
CLOCK_MONOTONIC system-wide NO YES NO lower higher
CLOCK_MONOTONIC_COARSE system-wide NO YES NO higher lower
CLOCK_MONOTONIC_RAW system-wide NO YES YES lower higher
CLOCK_BOOTTIME system-wide NO YES NO lower higher


时间调整


adjtime


函数原型


int adjtime(const struct timeval *delta, struct timeval *olddelta);


基本功能描述


adjtime用于校准系统时间,需要调整的时间保存在delta中,如果delta > 0,内核会通过一定的算法将系统时间缓慢的向上调整(例如,每一秒增加一些),直到时间调整结束;如果delta < 0,那么系统时间会缓慢的向下调整。


adjtimex


函数原型


int adjtimex(struct timex *buf);


基本功能描述


该函数用于调整内核时钟,其基于RFC1305所描述的算法实现。该系统调用实现较为复杂,而且功能强大,但一般应用不会使用。


睡眠


sleep


函数原型


unsigned int sleep(unsigned int seconds);


基本功能描述


sleep会导致调用线程进入睡眠态,直至seconds消逝完,或者被信号打断。sleep返回零表示所要求的时间已经等待完成;如果其被信息打断而返回,那么返回值表示所剩的秒数。


注意事项


  1. sleep有可能基于SIGALRM信号实现,所以混合使用alarm(2)和sleep会导致问题。


  1. sleep过程中,如果在信号处理函数调用longjmp或者修改SIGALRM的信号处理过程会导致未知的结果。


usleep


函数原型


int usleep(useconds_t usec);


基本功能描述


usleep阻塞当前调用线程至少usec。usleep执行成功返回0,失败时返回-1,如果其被信号打断,那错误码为EINTR。


注意事项


  1. useconds_t的取值范围为[0,1000000].


  1. 该函数会和SIGALARM存在交互,所以和其他定时器函数例如:alarm(2), sleep(3), nanosleep(2), setitimer(2), timer_create(2), timer_delete(2), timer_getoverrun(2), timer_gettime(2), timer_settime(2), ualarm(3)一起使用,其行为都是为定义的。


  1. POSIX.1-2001已经表明usleep函数已经废弃,代之使用nanosleep。


nanosleep


函数原型


int nanosleep(const struct timespec *req, struct timespec *rem);


基本功能描述


  1. nanosleep阻塞当前线程,知道rem消逝完,或者有信号递送给当前线程或进程处理。如果nanosleep被信号打断而返回,其会返回-1,剩余的时间会保存到rem(rem非空)中。可以继续将rem作为nanosleep的参数,使其继续 完成睡眠过程。


timesepc定义如下:
struct timespec {
    time_t tv_sec;        /* seconds */
    long   tv_nsec;       /* nanoseconds */
};其中,nanoseconds的范围为:0 to 999999999;


  1. 相对于sleep(3)、usleep(3),nanosleep存在如下优势:


(1)提供更高的精度用来定义睡眠间隔; (2)POSIX.1明确的定义了其不于信号进行交互; (3)其可以更为方便的恢复一个被中断的任务继续睡眠;


注意事项


  1. 如果调用在被信号中断后多次重新启动,nanosleep()睡眠的相对间隔可能会有问题,因为在调用的中断和重新启动之间的时间会导致在睡眠最终完成时出现漂移。使用带绝对时间值的clock_nanosleep(2)可以避免这个问题。


  1. 如果req中指定的间隔不是底层时钟粒度的精确倍数(参见time(7),通过sysconf(_SC_CLK_TCK)获得该值),然后将这个区间四舍五入到下一个倍数;此外,在休眠完成之后,在CPU空闲下来再次执行调用线程之前,可能仍然会有一个延迟


clock_nanosleep


函数原型


int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *request, struct timespec *remain);


基本功能描述


  1. clock_nanosleep提供高分辨率、可定制clock的sleep。clock_nanosleep与nanosleep类似,区别在于clock_nanosleep允许选择测量sleep间隔的clock,同时,它还允许sleep间隔配置成相对值或者绝对值。可供选择的clock_id包括:CLOCK_REALTIME、CLOCK_MONOTONIC、CLOCK_PROCESS_CPUTIME_ID,具体可参见上一章的介绍。


2. flag为0,表示request为相对于当前clock的间隔;flag为TIMER_ABSTIME, request表示为与clock_id之间的绝对值。如果request <= 当前的clock,那么clock_nanosleep会立即返回。


3. 与nanosleep相似,clock_nanosleep同样会被信号中断,所不同的是,如果clock_nanosleep被中断,其返回值表示错误码。其若被中断,那么其返回EINTR,如果remain不为NULL,并且flag不为TIMER_ABSTIME, 那么剩 余的时间被保存在remain中。然后可以使用这个值再次调用clock_nanosleep()并完成(相对)睡眠。


注意事项


  1. 使用绝对计时器有助于防止nanosleep(2)中描述的那种计时器漂移问题(这些问题在试图重启被信号反复打断的相对睡眠的程序中更加严重)。要避免这些问题,可以为所需的时钟调用clock_gettime(2),将所需的时间间隔添加到返回的时间值,然后使用TIMER_ABSTIME标记调用clock_nanosleep().


  1. clock_nanosleep()在被信号处理程序中断后永远不会重新启动,尽管使用sigaction(2) SA_RESTART标志.


  1. flag为TIMER_ABSTIME时,remain参数没有任何用处.


  1. POSIX.1指定在通过clock_settime(2)更改CLOCK_REALTIME时钟的值之后,新的时钟值应该作为是否唤醒clock_nanosleep新的标准.如果新的时钟值超过睡眠间隔,那么clock_nanosleep()调用将立即返回。


  1. POSIX.1指定通过clock_settime(2)更改CLOCK_REALTIME时钟的值不会对相对clock_nanosleep()上阻塞的线程产生影响.


总结


Linux系统提供了各种各样的time函数,不同的精度、不同clock_id、不同睡眠机制等,我们在实现具体功能时,需按照实际的需求来选择合适的函数。后续,还会总结Linux系统下timer的种类和使用方式。


相关文章
|
1月前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
80 3
|
1月前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
74 2
|
5天前
|
存储 缓存 监控
Linux缓存管理:如何安全地清理系统缓存
在Linux系统中,内存管理至关重要。本文详细介绍了如何安全地清理系统缓存,特别是通过使用`/proc/sys/vm/drop_caches`接口。内容包括清理缓存的原因、步骤、注意事项和最佳实践,帮助你在必要时优化系统性能。
117 78
|
9天前
|
Linux Shell 网络安全
Kali Linux系统Metasploit框架利用 HTA 文件进行渗透测试实验
本指南介绍如何利用 HTA 文件和 Metasploit 框架进行渗透测试。通过创建反向 shell、生成 HTA 文件、设置 HTTP 服务器和发送文件,最终实现对目标系统的控制。适用于教育目的,需合法授权。
41 9
Kali Linux系统Metasploit框架利用 HTA 文件进行渗透测试实验
|
1月前
|
缓存 Java Linux
如何解决 Linux 系统中内存使用量耗尽的问题?
如何解决 Linux 系统中内存使用量耗尽的问题?
125 48
|
5天前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
47 13
|
29天前
|
Ubuntu Linux 网络安全
linux系统ubuntu中在命令行中打开图形界面的文件夹
在Ubuntu系统中,通过命令行打开图形界面的文件夹是一个高效且实用的操作。无论是使用Nautilus、Dolphin还是Thunar,都可以根据具体桌面环境选择合适的文件管理器。通过上述命令和方法,可以简化日常工作,提高效率。同时,解决权限问题和图形界面问题也能确保操作的顺利进行。掌握这些技巧,可以使Linux操作更加便捷和灵活。
20 3
|
6天前
|
Ubuntu Linux C++
Win10系统上直接使用linux子系统教程(仅需五步!超简单,快速上手)
本文介绍了如何在Windows 10上安装并使用Linux子系统。首先,通过应用商店安装Windows Terminal和Linux系统(如Ubuntu)。接着,在控制面板中启用“适用于Linux的Windows子系统”并重启电脑。最后,在Windows Terminal中选择安装的Linux系统即可开始使用。文中还提供了注意事项和进一步配置的链接。
22 0
|
1月前
|
存储 运维 Linux
如何在 Linux 系统中使用 envsubst 命令替换环境变量?
`envsubst` 是 Linux 系统中用于替换文本中环境变量值的实用工具。本文分三部分介绍其工作原理、使用方法及实际应用,包括配置文件替换、脚本执行中环境变量替换和动态生成文件等场景,帮助用户高效利用 `envsubst` 进行开发和运维工作。
52 4
|
1月前
|
Linux
在 Linux 系统中,`find` 命令
在 Linux 系统中,`find` 命令
34 1