专注于用户空间的系统级编程–即内核之上的所有内容。
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);