Linux编程---时间相关

时间相关的东西很难分类.所以我就写一篇好了.

 

一.系统时间

1.time函数

time_t time(time_t *tloc);

double difftime(time_t time1,time_t timeo);

time返回系统自197011日以来,经历了多少秒的时间.同时可以通过返回值和参数来得到.

difftime则返回两个参数的时间之差.并且结果是作为双精度浮点数返回.

 

 

2.gettimeofday函数

int gettimeofday(struct timeval *restrict tp,void *restrict tzp);

int settimeofday(const struct timeval * tp,const void *tzp);

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

这里都是指的从197011日到现在的时间.

并且三个函数都用到了struct timeval结构体.我就说一下.

struct timeval{

   time_t  tv_sec;  这个表示秒

   suseconds_t tv_usec  这个表示微秒

}

头两个函数的第二个参数一定要写NULL.不然在Linux下会报错.

 

最后一个函数比较有趣,它是用来把系统时钟调快或者调慢.

简单来说,如果参数delta是正,那么加快系统时钟的步伐.为负则减慢时钟的步伐.通常以1%的速率来调整.第二个参数返回仍需调整的时间.

这个函数估计只有对时间要求比较高的服务器程序上才有用吧.

 

 

二.时间格式转换

struct tm *gmtime(const time_t * time);

struct tm *localtime(const time_t * time);

time_t mktime(struct tm *brokentime);

第一个函数由time_t类型的参数转换为struct tim结构体的参数.

第二个函数则会自动修正时区和进行与夏令时有关的调整

第三个函数则是反向转换.并且会设置brokentime中的成员为适当的值.

实际上这几个函数中还用到了tzset来获取时区信息.

struct tm结构中成员类型都是int类型.tm_开头.有以下成员

sec 

min 分钟

hour 小时

mday 月中日期

mon 月份0~11

year 1900年以来的年数

wday 星期几

yday 11日以来的天数0~365

isdst 标志夏令时是否起作用.正表示起作用,0表示不起作用,负数表示无效(应该是会让函数错误返回吧)

 

 

格式化日期与时间

char *asctime(const struct tm *tmptr);

char *ctime(const time_t *timeval);

两个函数都返回如同data命令锁输出的时间字符串.

ctime相当于是asctime(localtime(timeval))这种调用.

 

为了对日期和时间字符串格式进行更为灵活和精确的控制,UNIX系统还提供了另一对函数

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

char *strptime(const char *buf,const char *format,struct tm *timeptr);

第一个函数就类似于sscanf一样.通过%和特定符号来输出相关格式信息.具体的就不多说了,

第二个函数则是第一个函数的逆过程.把串解析到timeptr.

 

三.CPU时间与墙钟时间

CPU时间就是占用CPU的时间.可以进一步分为用户时间(执行程序指令)和系统时间(运行过程中系统服务所用时间).

墙钟时间就是现实中进程运行了的时间.每次运行进程都可以产生不一样的结果.

 

clock_t clock();

这个函数返回CPU时间,包含用户时间和系统调用时间.这个clock表示系统内部使用的时间单位(1/CLOCKS_PER_SEC ).

 

clock_t times(struct tms *buffer);

这个函数可以报告更详细的CPU时间信息.其结果返回到buffer.返回值返回墙钟时间.

tms结构体成员类型全部为clock_t.有以下成员

tms_utime  用户时间

tms_stime  系统时间

tms_cutime  子进程时间.只包含用户时间.只包含waitwaitpid等待了的子进程.

tms_cstime  子进程时间.只包含系统时间.只包含waitwaitpid等待了的子进程.

注意,这个结构体成员类型虽然是clock.但是要用_SC_CLK_TCK来除.因为clock函数是C库函数,times()则是系统调用.两者的基础时间标准不一样.

 

 

四.睡眠与定时

1.sleep函数

unsigned int sleep(unsigned int seconds);

时间参数是以墙钟时间为参数的.并且当捕获信号时,会使其终止阻塞.

2.设置定时器

每个进程都有三个独立无关的时间定时器:墙钟定时器,虚拟定时器和剖面定时器.

ITIMER_REAL: 这个就是墙钟定时器,到时发送SIGALRM信号

ITIMER_VIRTUAL: 虚拟定时器,用户态才计时.它到期会发送一个SIGVTALRM信号给进程

ITIMER_PROF: 进程的CPU时间剖面定时器.在用户态和核态时均走动.到时会发送一个SIGPROF信号给调用进程.

 

unsigned int alarm (unsigned int seconds);

这个函数设置墙钟定时器.以秒为单位,到时则生成SIGALRM信号.对这个信号,进程的默认动作为终止.

如果函数多次调用,那么会覆盖原先的计时时间.并且返回原来还剩下的时间.因此可以用0作为参数.这样就会清除之前的alarm设置,而不发送信号.

 

int setittimer(int which,const struct itimerval *value,struct itimerval *ovalue);

int getitimer(int which,struct itimerval *value);

which参数就是上面的三个宏定义.value则是定时器值的设定.

itimerval结构成员也比较简单.类型都是struct timerval.成员如下

it_interval 定时间隔时间

it_value   定时开始时间

这个间隔时间如果为0,表示定时器只执行一次.否则,表示该定时器每相隔it_interval时间就发送一个信号.

开始时间表示调用后,过了it_value时间后才开始循环定时器.

如果函数参数valueNULL的话,同样也是表示清除定时器.

 

注意,虽说是精确到了微秒的级别,但是基础时间片仍然是按照系统频率来的.所以每次时间返回应该是系统时间片的整数倍.并且由于系统并发进程不同,每次这种信号也会稍微晚一些才发送到进程.

 

五.实时时钟与定时

对于一些时间要求比较高的应用会要求时间尽可能的精确,所以就用到了实时时钟.

1.实时时钟

Linux系统定义有如下几种时钟:

CLOCK_REALTIME: 系统范围的时钟,它给出自Epoch以来的时间,是唯一一个所有系统都必须支持的时钟.因此这种类型是可以移植的.

CLOCK_MONOTONIC: 也是系统范围的时钟,自从过去某一点开始以来的时间(秒和纳秒).这个时钟在系统启动之后便不能改变.

CLOCK_PROCESS_CPUTIME_ID: 进城范围的时钟,它给出进程特定的CPU时间

CLOCK_THREAD_CPUTIME_ID: 这是线程范围的时钟,它给出线程特定的CPU时间

 

实时时钟多用timespec结构体

struct timespec{
   time_t  tv_sec; 秒数

   long  tv_nsec;  纳秒数

};

 

设置时钟分辨率和时间值函数如下

int clock_getres(clockid_t clock_id,struct timespec *res);

int clock_gettime(clockid_t clock_id,struct timespec *tp);

int clock_settime(clockid_t clock_id,const struct timespec *tp);

clockid_t类型等价于int.参数中的clock_id表示类型.用上面的4个宏填入.

getres用于设置参数resclock_id指定的时钟分辨率.

getime用于读取实时时钟,它返回clock_id指定时钟的当前值到tp所指结构(书上没讲的太清楚,我就按自己意思理解吧).

settime只有root用户才能调用.并且根据clock_id来设置对应的时钟.用其tp中时间的倒数来确定其频率.Linux,时钟粒度的最小值至少是一个滴答,_SC_CLK_TCK.

 

2.实时睡眠

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

这个函数比sleep粒度更细.req为指定睡眠时间.如果是时间到了返回值为0,被信号中断则返回-1,并且设置errnoEINTR,并且当rem不为NULL的时候,存储剩余睡眠时间到rem所指结构.也就是说如果还想睡就用rem为参数继续调用nanosleep.

 

3.实时定时器

实时定时器的分辨率为纳秒级.并且是动态创建的,至多可达32.实时定时器既可以设置相对时间,也可以设置绝对时间,并且除开SIGALRM信号外,还可以指定其他实时信号作为定时器的到期信号.

 

创建和删除定时器

int timer_create(clockid_t clockid,struct sigevent *restrict evp,timer_t *restrict timerid);

int timer_delete(timer_t timerid);

对于创建函数而言,clockid参数可以指定定时器使用的时钟.也就是上面的那四个宏定义.第三个参数返回其定时器ID.并且保持ID直到被删除.第二个参数就复杂了.如果是NULL,那么使用默认信号交付.这个结构体主要有下面几个成员:

int sigev_notify   通知类型

int sigev_signo    信号类型

union signval sigev_value    信号参数值

void (*)(union sigval)  sigev_notify_function   通知函数

(pthread_attr_t  *)  sigev_notify_attributes    通知属性

sigev_notify可以取值为

SIGEV_NONE    不生成异步信号

SIGEV_SIGNAL  生成排队的信号,并随信号携带一个应用定义的值,因为是排队的,所以一定是实时信号,或者说可靠信号.

SIGEV_THREAD  执行一个通知函数,这个是线程里面的高级用法,在线程那篇我会补充上的.

 

delete函数就很简单来.create传出来的ID赋给其参数即可.并且如果当前计时器还在计时,删除之后不必担心还会再来一个信号.

对于进程调用exitexec之后也会自动删除定时器.

 

create之所以没有时间相关的参数,其实是为了精确延时.如果都设置在一个函数里面,那么很可能会有较大的延迟时间.所以创建函数和设置函数是分开的.下面我就说设置函数

int timer_gettime(timer_t timerid,struct itimerspec * value);

int timer_settime(timer_t timerid,int flags,const struct itimerspec *restrict value,struct itimerspec *restrcit ovalue);

gettime可以存储timerid指定定时器中的剩余时间到value所指结构中.

settime则用value设置timerid指定的定时器.value的结构体中有两个struct timespec结构体,成员如下:

it_value;    定时时间

it_interval;  间隔时间

具体的timespec中的内容已经写过了,我就不多写了.

这里可以说的是,系统分辨率仍然存在,如果你设置的时间是分辨率的两个连续整数倍之间一个值,那么会取时间较大的一个值.

 

还有就是setimeflags参数了.目前只有一个参数宏可以选择TIMER_ABSTIME.这个表示绝对时间.而之前我们设置的都是相对时间.那么绝对时间和相对时间怎么定义的呢?

相对时间是根据你使用CPU时间的大小来分配的,绝对时间是墙钟时间.所以相对时间延时比较大,绝对时间延时小.这是我个人的理解,书上写的不太清楚,百度资料也少,如有错误还请指正.

 

定时器超期计数

为了避免循环定时发送多个信号排队占用大量资源,又允许应用检测循环次数,所以设置了定时器超期计数.

int timer_getoverrun(timer_t timerid);

这个函数就可以获得定时器有多少个循环没有被处理.如果信号总是被处理了,那么函数的返回值总是0.

标准规定最大超期数为DELAYTIMER_MAX-1.一般就是MAXINT的值.

 

 

Linux编程---时间相关,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。