日历时间(Calendar Time)

系统调用gettimeofday(),可于tv指向的缓冲区中返回日历时间。

#include <sys/time.h>

int gettimeofday(struct timeval *tv, struct timezone *tz);
                        Returns 0 on success, or -1 on error
  • 参数

    • tv:自 Epoch 以来的秒数(时间戳)信息
    • tz:时区信息
  • 返回值

    • 成功:返回0
    • 失败:返回-1

tv是指向如下数据结构的一个指针:

struct timeval {
    time_t     tv_sec;    /* Seconds since 00:00:00, 1 Jan 1970 UTC */
    suseconds  tv_usec;   /* Additional microseconds (long int) */
}

time()系统调用返回自 Epoch 以来的秒数(和函数gettimeofday()所返回的tv参数中tv_sec字段的数值相同)

#include <time.h>

int time(time_t *timep);
                     Returns number of seconds since the Epoch, or (time_t)-1 on error
  • 参数

    • timep:如果不指定为NULL,函数会将时间戳置于该参数所指向的位置
  • 返回值

    • 成功:返回时间戳
    • 失败:返回-1

时间转换函数

下图所示为用于在time_t值和其他时间格式之间相互转换的函数,其中包括打印输出。
time_conversion_function.png

这些函数屏蔽了因时区、夏令时制和本地化等问题给转换所带来的种种复杂性。

time_t转换为可打印格式

为了将time_t转换为可打印格式,ctime()函数提供了一个简单方法。

#include <time.h>

int *ctime(const time_t *timep);
                     Returns pointer to statically allocated string terminated by newline and \0 on success, or NULL on error
  • 参数

    • timep:指定时间戳
  • 返回值

    • 成功:返回一个长达26字节的字符串,内含标准格式的日期和时间,即可打印格式(存储在静态分配的内存)
    • 失败:返回NULL

time_t和分解时间之间的转换

函数gmtime()localtime()可将一time_t值转换为一个所谓的分解时间。分解时间被置于一个经由静态分配的结构中,其地址则作为函数结果返回。

#include <time.h>

struct tm *gmtime(const time_t *timep);
struct tm *localtime(const time_t *timep);
                     Both returns pointer to statically allocated broken-down time structure on success, or NULL on error
  • 参数

    • timep:指定时间戳
  • 返回值

    • 成功:返回存储分解时间(tm结构)的所在内存地址(存储在静态分配的内存)
    • 失败:返回NULL

函数gmtime()能够把日历时间转换为一个对应于UTC的分解时间。相形之下,函数localtime()需要考虑时区和夏令时设置,返回对应于系统本地时间的一个分解时间。

tm结构如下所示,日期和时间被分解为多个独立字段,其形式如下:

struct tm {
mt tm_sec;        /* Seconds (0-60) */ 
mt tm_min;         /* Minutes (0-59) */
mt tm_hour;        /* Hours (0-23) */
mt tm_mday;       /* Day of the month (1-31) */
mt tm_mon;        /* Month (0-11) */
mt tm_year;       /* Year since 1900 */
mt tm_wday;       /* Day of the week (Sunday = 0)*/
mt tm_yday;       /* Day in the year (0-365; 1 Jan = 0) */
mt tm_isdst;      /* Daylight saving time flag
                    > 0: DST is in effect;
                    = 0: DST is not effect;
                    < 0: DST information not available */
}

函数mktime()将一个本地时区的分解时间翻译为time_t值,并将其作为函数结果返回。

#include <time.h>

time_t mktime(struct tm *timeptr);
                            Returns seconds since the Epoch corresponding to timeptr on success, or (time_t)-1 on error
  • 参数

    • timeptr:将分解时间置于tm结构的内存地址,函数会忽略tm结构中的tm_wdaytm_yday字段。
  • 返回值

    • 成功:返回时间戳
    • 失败:返回-1

分解时间和打印格式之间的转换

  • 从分解时间转换为打印格式
    在参数tm中提供一个指向分解时间结构的指针,asctime()则会返回一指针,指向经由静态分配的字符串,内含时间,格式则与ctime()相同。

    #include <time.h>
    
    time_t asctime(const struct tm *timeptr);
                               Returns pointer to statically allocated string terminated by newline and \0 on success, or NULL on error

    相形于ctime(),本地时区设置对asctime()没有影响,因为其所转换的是一个分解时间,该时间为处理后的数据。

  • 将打印格式时间转换为分解时间
    函数strptime()strftime()的逆向函数,将包含日期和时间的字符串转换成一分解时间。

    #define _XOPEN_SOURCE
    #include <time.h>
    
    char *strptime(const char *str, const char *format, struct tm *timeptr);
                           Returns pointer to next unprocessed character in str on success, or NULL on error

    该函数按照参数format内的格式要求,对字符串str加以解析,并将转换后的分解时间置于指针timeptr所指向的结构体中。

    • 参数

      • str:由日期和时间组成的字符串
      • format:指定格式
      • timeptr:该指针用来指向转换后的分解时间的结构体
    • 返回值

      • 成功:返回指针,指向str中下一个未经处理的字符
      • 失败:返回NULL

时区

时区定义

  • 系统没有将时区信息直接编码于程序或函数库中,而是以标准格式保存于文件中并加以维护。
  • 这些文件位于/usr/share/zoneinfo中。该目录下的每个文件都包含了一个特定国家或地区内时区制度的相关信息,且往往根据其所描述的时区来加以命名。此外,可以利用子目录对相关时区进行有层次的分组。
  • 在程序中指定使用的时区,实际上是指定该目录下某一时区文件的相对路径名。

为程序指定时区

为运行中的程序指定一个时区,需要将TZ环境变量设置为由一冒号(:)和时区名称组成的字符串(如TZ=":Asia/Shanghai"),其中时区名称定义于/usr/share/zoneinfo中。设置时区会自动影响到函数ctime()localtime()mktime()strftime()
为了获取当前的时区设置,上述函数都会调用tzset(3),对如下3个全局变量进行了初始化:

char *tzname[2];    /* Name of timezone and alternate (DST) timezone */
int daylight;    /* Nonzero if there is an alternate (DST) timezone */
long timezone;    /* Seconds difference between UTC and local standard time */

函数tzset()会首先检查环境变量TZ

  1. 如果尚未设置就采用/etc/localtime中定义的默认时区来初始化时区。
  2. 如果TZ环境变量的值为空,或无法与时区文件名相匹配,那么就使用UTC
  3. 还可将TZDIR环境变量设置为搜寻时区信息的目录名称,以替代默认的/usr/share/zoneinfo

地区(Locale)

地区定义

  • 和时区信息一样,地区信息按标准格式存储于文件中。
  • 地区信息维护于/usr/share/local之下的目录层次结构中。该目录下的每个子目录都包含一特定地区的信息。
  • 当在程序中指定要使用的地区时,实际上是指定了/usr/share/locale下某个子目录的名称。

为程序设置地区

函数setlocale()既可设置也可查询程序的当前地区。

#include <locale.h>

char *setlocale(int category, const char *locale);
                Returns pointer to a (usually statically allocated)string identifying the new or current locale on success, or NULL on error
  • 参数

    • category:选择设置或查询地区的哪一部分
    • locale:可能是一个字符串,指定系统上已定义的一个地区
  • 返回值

    • 成功:返回一个指针指向标识这一类地区设置的字符串(通常是静态分配)。如果仅需查看可将locale参数指定为空
    • 失败:发生错误时返回NULL

更新系统时钟

  • 两个更新系统时钟的接口:settimeofday()adjtime()。系统调用settimeofday()gettimeofday()的逆向操作。

    #define _BSD_SOURCE
    #include <sys/time.h>
    
    int settimeofday(const struct timeval *tv, const struct timezone *tz);
                           Returns 0 on success, or -1 on error

    该函数将tv指向timeval结构体里的秒数和微秒数,设置到系统的日历时间。和函数gettimeofday()一样,tz参数已被废弃,应始终指定为NULL

  • 上述调用所造成的系统时间突然变化可能会对依赖系统时钟单调递增的应用造成有害影响,当对时间做微小调整时,通常推荐使用库函数adjtime(),他将系统时钟逐步调整到正确时间。

    #define _BSD_SOURCE
    #include <sys/time.h>
    
    int adjtime(struct timeval *delta, struct timeval *olddelta);
                           Returns 0 on success, or -1 on error
    • 参数

      • delta:该参数指向一个timeval结构体,指定需要改变时间的秒和微秒数。如果是正数则每秒系统时间都会额外拨快一点点,直到增加完所需时间;如果为负值,时钟以类似方式减慢
      • olddelta:此函数可能无法完成时钟调整,即该参数用来存放剩余未经调整的时间,如果不关心这个可以设为NULL
    • 返回值

      • 成功:返回0
      • 失败:发生错误时返回-1

软件时钟(jiffies)

本书所描述的时间相关的各种系统调用的精度是受限于系统软件时钟(software clock)的分辨率,它的度量单位被称为jiffies

进程时间

进程时间是进程创建后使用的CPU时间数量。出于记录目的,内核把CPU时间分为以下两部分:

  • 用户CPU时间:在用户模式下执行所花费的时间数量
  • 系统CPU时间:在内核模式中执行所花费的时间数量

shelltime命令可以同时获得这两部分的值

系统调用times(),检索进程时间信息,并把结果通过buf指向的结构体返回。

#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
  • 参数

    • buf:指向tms结构体,该结构体如下:

      struct tms{
          clock_t tms_utime;    /* User CPU time used by caller */
          clock_t tms_stime;    /* System CPU time used by caller */
          clock_t tms_cutime;    /* User CPU time of all (waited for) children */
          clock_t tms_cstime;    /* System CPU time of all (waited for) children */
      };
  • 返回值

    • 成功:返回自过去的任意点流逝的以时钟计时单元为单位的时间
    • 失败:发生错误返回-1

函数clock()提供了一个简单的接口用于取得进程时间。它返回一个值描述了调用进程使用的总CPU时间(包括用户和系统)

#include <time.h>

clock_t clock(void);
                Returns total CPU time used by calling process measured in CLOCKS_PER_SEC, or (clock_t)-1 on error

该函数的返回值的计量单位是CLOCKS_PER_SEC,所以我们必须除以这个值来获得进程所使用的CPU时间秒数。

总结

本章主要介绍了真实时间(日历时间)和进程时间(用户和系统时间)的概念,并围绕其介绍了一些相互转换的函数;然后介绍了时区和地区;最后也给出了更新系统时钟的系统调用和库函数。

  • 真实时间
    度量这时间的起点有二:

    1. 某个标准点。为日历时间,适用于需要对数据库记录或文件打上时间戳的程序。
    2. 进程生命周期内的某个固定时间点(通常为程序启动)。称之为流逝时间或挂钟时间,主要针对需要周期性操作或定期从外部输入设备进行度量的程序。
  • 进程时间
    一个进程所使用的CPU时间总量,适用于对程序、算法性能的检查或优化。

标签: Linux, C/C++, TLPI

添加新评论