时间与系统限制
本文是作者阅读TLPI(The Linux Programer Interface的总结),为了突出重点,避免一刀砍,我不会过多的去介绍基本的概念和用法,我重点会去介绍原理和细节。因此对于本文的读者,至少要求读过APUE,或者是实际有写过相关代码的程序员,因为知识有点零散,所以我会尽可能以FAQ的形式呈现给读者。
对于一个程序来说,主要关注下面两种时间类型:
真实时间: 度量这个时间的起点有两个: 一为某个标准点也就是日历时间,适用于需要对数据库记录或文件打时间戳,二为进程生命周期内的某个固定时点,后者为流逝时间或者墙上时间。主要针对需要周期性操作或定期从外部输入设备进行度量的程序。
进程时间: 一个进程所使用的CPU时间总量,适用于对程序、算法性能的检查或优化。
大多数计算机体系结构都内置有硬件时钟,使得内核得以计算真实时间和进程时间。
日历时间存储类型为time_t的变量中,是自Epoch以来的秒数来度量的,Epoch亦即通用协调时间(UTC)的1970年1月1日早晨零点。通过如下API获得:
#include <time.h>
time_t time(time_t *t);
Linux最早的时候只提供了time用于求日历时间,但是因为其精度问题,后来又引入了gettimeofday
这个系统调用,此时time系统调用就显得多余了,因此就将time实现为调用gettimeofday
的一个库函数。
#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
char *ctime(const time_t *timep);
char *ctime_r(const time_t *timep, char *buf); //可重入版本
通过ctime将time_t的类型的数据转换为可打印的字符串,其打印的格式为:"Wed Jun 30 21:49:08 1993\n"
,没有办法定制这个时间的格式,只能通过字符串截取来获取其中的部分字符串来使用。
Linux下通过struct tm
这个结构用于表示分解时间:
struct tm {
int tm_sec; /* seconds */
int tm_min; /* minutes */
int tm_hour; /* hours */
int tm_mday; /* day of the month */
int tm_mon; /* month */
int tm_year; /* year */
int tm_wday; /* day of the week */
int tm_yday; /* day in the year */
int tm_isdst; /* daylight saving time */
};
通过下列API可用于将time_t转换为分解时间。
#include <time.h>
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);
gmtime和localtime的区别就在于,后者会考虑到时区和夏令时,会返回一个对应于系统本地时间的一个分解时间。
#include <time.h>
time_t mktime(struct tm *tm);
注: mktime可能会修改timeptr所指向的结构体,mktime不要求tm结构体的字段都在指定范围的限制中,任何一个字段的值超出了范围,mktime都会将其调整回有效范围内。
#include <time.h>
char *asctime(const struct tm *tm);
char *asctime_r(const struct tm *tm, char *buf);
#include <time.h>
size_t strftime(char *s, size_t max, const char *format,
const struct tm *tm);
#define _XOPEN_SOURCE /* See feature_test_macros(7) */
#include <time.h>
char *strptime(const char *s, const char *format, struct tm *tm);
strftime
是将分解时间转换成指定的格式的字符串,strptime
则是将指定格式的字符串,转换为分解时间。
不同的国家使用不同的时区和夏时制,对于要输入和输出时间的程序来说,必须要对系统所处的时区和夏时制加以考虑。时间信息往往既浩繁又多变,出于这个原因,系统没有将其直接编码于程序或函数库中,而是以标准格式保存于文件中,并加以维护。这些文件都放在/usr/share/zoneinfo
目录下,该目录下的每个文件都包含了一个特定国家或地区内时区制度相关信息。系统的本地时间由/etc/localtime
时区文件定义,通常链接到/usr/share/zoneinfo
目录中的某个时区文件。因此如果要更改时区,只需要重新链接/etc/localtime
到/usr/share/zoneinfo
目录中正确的时区文件即可。这些操作都是直接通过命令行即可完成,那么如何在程序中指定时区呢?如果是一个运行中的程序可以通过设置TZ环境变量来给运行中的程序指定一个时区。TZ环境变量的值的格式为:时区名称
,时区名称可以参考/usr/share/zoneinfo
目录下的文件名。
注:时区文件的格式可以通过man 5 tzfile
查看到,可以通过man 8 zic
命令来创建时区文件,此外man 8 zdump
可以更具指定时区文件来显示当前时间
在谈时间类型的时候,说过有两种类型的时间,一种就是真实时间,另一种就是进程时间,记录了进程使用CPU的时间数量,linux下通过struct tms
结构来表示:
struct tms {
clock_t tms_utime; /* user time */
clock_t tms_stime; /* system time */
clock_t tms_cutime; /* user time of children */
clock_t tms_cstime; /* system time of children */
};
为了获取进程时间,linux提供了times
系统调用,其函数声明如下:
#include <sys/times.h>
clock_t times(struct tms *buf);
times返回的是clock_t类型的,这是一个用时钟计时单元为单位度量的类型,通过sysconf(_SC_CLK_TCK)
,可以获取每秒包含的时钟计时单元,最后就可以换算为秒。除了times外,还有一个clock系统调用,可以获取进程总的CPU时间:
#include <time.h>
clock_t clock(void);
clock返回的也是clock_t类型的,但是和times不同的是,clock返回的计量单位是CLOCKS_PER_SEC,通常这个值是一个常量10000,times和clock采用了两种不同的计量单位,这是历史原因导致的。
但凡UNIX实现,都会对各种系统特性和资源加以限制,并提供各种标准所定义的选项,例如:
尽管可以把假定的限制和选项硬性写入程序编码,但这将破坏程序的可移植性,因为限制和选项可能会有所不同。因此C语言标准和SUSv3为此提供了两种重要的途径来获取限制和选项。
sysconf pathconf fpathconf
提供给应用程序调用以检查系统实现的限制和选项。 getconf system_var
getconf path_var pathname
[root@localhost ~]# getconf NAME_MAX /boot
255
[root@localhost ~]# getconf ARG_MAX
2097152
getconf可以获取系统限制,也可以获取文件相关限制,因为不同的文件系统限制不同,所以文件限制多了一个参数,用来表明是哪个文件系统。
#include <unistd.h>
long sysconf(int name);
可以通过man 7 posixoptions
和man sysconf
来查看支持的name值.在某些系统调用的man文档中你也可以找到一些name值,用于获取和这个系统调用相关的限制。
#include <unistd.h>
long fpathconf(int fd, int name);
long pathconf(char *path, int name);
与文件相关的限制,都是两个参数,一个是要获取限制的选项名称,另一个就是相关的一个文件,因为对文件限制来说,不同的文件系统限制值不同。因此需要指定一个文件来指明要获取的限制是针对哪个文件系统的。