0%

Linux系统编程-时间


专注于用户空间的系统级编程–即内核之上的所有内容。

UNIX系统把绝对时间表示成新纪元至今所经过的秒数,新纪元定义成1970年1月1日早上00:00:00 UTC。
UTC(协调世界时,Coordinated Universal Time)相当于GMT或Zulu时间。

时间的数据结构

原始表示:

#incliude <time.h>
typedef long time_t;

微秒级精度

#include <sys/time.h>

struct timeval {
    time_t      tv_sec;
    suseconds_t tv_usec;
};

纳秒级精度

#include <time.h>

struct timespec {
    time_t  tv_sec;
    long    tv_nsec;
};

时间描述

#include <time.h>

struct tm
{
  int tm_sec;           /* Seconds. [0-60] (1 leap second) */
  int tm_min;           /* Minutes. [0-59] */
  int tm_hour;          /* Hours.   [0-23] */
  int tm_mday;          /* Day.     [1-31] */
  int tm_mon;           /* Month.   [0-11] */
  int tm_year;          /* Year - 1900.  */
  int tm_wday;          /* Day of week. [0-6] */
  int tm_yday;          /* Days in year.[0-365] */
  int tm_isdst;         /* DST.     [-1/0/1]*/

# ifdef __USE_BSD
  long int tm_gmtoff;       /* Seconds east of UTC.  */
  const char *tm_zone;      /* Timezone abbreviation.  */
# else
  long int __tm_gmtoff;     /* Seconds east of UTC.  */
  const char *__tm_zone;    /* Timezone abbreviation.  */
# endif
};

时间API

在Linux上,所有使用POSIX时钟的函数都需要将目标文件与librt链接:

$ gcc -Wall -W -O2 -lrt -g -o a a.c

取得、设置目前时间:

#include <time.h>

time_t time(time_t *t);
int stime(time_t *t);

time()如果参数t非NULL,也将当前时间写入到提供的指针t中。


对某些需要较高精准度的需求,Linux提供了以下接口,提供微秒级精度支持:

#include <sys/time.h>

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

当前时间tv指向timeval结构体。结构提timezone和参数tz已经废止,都不应该在Linux中使用,建议传NULL。


用于纳秒级的高级接口:

#include <time.h>

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

文字时间格式函数:

#include <time.h>

//将tm转换为ASCII字符串
char * asctime(const struct tm *tm);
char * asctime_r(const struct tm *tm, char *buf);

//将time_t转换为ASCII字符串
char * ctime(const struct time_t *tm);
char * ctime_r(const struct time_t *tm, char *buf);

//将tm转换为time_t,使用本地时间
time_t mktime(struct tm *tm);

//将time_t转换为tm,使用UTC时区格式
struct tm * gmtime(const time_t *tp);
struct tm * gmtime_r(const time_t *tp, struct tm *result);

//将time_t转换为tm,使用本地时区格式
struct tm * localtime(const time_t *tp);
struct tm * localtime_r(const time_t *tp, struct tm *result);

//计算秒差
double difftime(time_t time1, time_t time2);

后缀_r的函数不使用静态分配的指针,而是通过参数传递。


睡眠函数:

#include <unistd.h>

unsigned int sleep(unsigned int seconds);   //秒级
void usleep(unsigned long usec);            //微秒级

纳秒级精度睡眠

#include <time.h>

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

这个函数功能是暂停某个线程直到你规定的时间后恢复,参数req就是你要暂停的时间。
由于调用nanosleep是使线程进入TASK_INTERRUPTIBLE,这种状态是会相应信号而进入TASK_RUNNING状态的,
这就意味着有可能会没有等到你规定的时间就因为其它信号而唤醒,
此时函数返回-1,且还剩余的时间会被记录在rem中(rem不为空的情况下)。


实现睡眠的高级方法??

#include <time.h>  

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

select()实现sleep可移植实现:

struct timeval tv = {.tv_sec = 0, .tv_usec = 750};

/* sleep for 750 us */
select(0, NULL, NULL, NULL, &tv);

select的精确度为10毫秒,在10毫秒以上很精确,sleep 可以在多线程中使用,只阻塞本线程,不影响所属进程中的其它线程。
Linux下短延时推荐使用select函数。

定时器

简单定时器接口:

#include <unistd.h>

unsigned int alarm(unsigned int seconds);

想要成功调用该函数,需要为SIGALRM信号注册一个信号处理函数:

signal(SIGALRM, alarm_handler);

计时器

#include <sys/time.h>

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

高级定时器

#include <signal.h>
#include <time.h>

int timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid);
int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspec *ovalue);
int timer_gettime(timer_t timerid, struct itimerspec *value);
int timer_getoverrun(timer_t timerid);
int timer_delete(timer_t timerid);

*关于短延迟 sleep usleep nanosleep select