linux时间、睡眠、定时器详解


内容梗概
1 时钟种类
1.1 系统时钟:
system timer

long hz;
hz = sysconf (_SC_CLK_TCK);
printf(“system timer is %ld\n”,hz);

1.2 POSIX时钟:
CLOCK_MONOTONIC:线性增长时间
CLOCK_REALTIME:墙上时间
CLOCK_PROCESS_CPUTIME_ID:进程占用CPU时间
CLOCK_THREAD_CPUTIME_ID:线程占用CPU时间

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

1.3 时钟API比较

GETTIME SETTIME RESOLUTION CLOCK_TYPE
time() stime() second system clock
gettimeofday() settimeofday() us system clock
clock_gettime() clock_settime() ns posix clock

2.睡眠API
usleep()微妙级精度睡眠
nanosleep()纳秒级精度睡眠
clock_nanosleep()符合POSIX标准,为最高级的睡眠调用
select 可移植睡眠实现

3.定时器API
3.1 间歇定时器
#include <sys/time.h>
int getitimer (int which, struct itimerval *value);
int setitimer (int which, const struct itimerval *value, struct itimerval *ovalue);

3.2 posix定时器
#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);

0 时间

0.1 进程时间

进程消耗的时间,包含用户空间代码占用时间和内核在该进程中消耗的时间

0.2 单调时间

时间源严格线性增长。
单调时间的重要性不在于当前值,而是在于保证时间源线性增长
用来计算两次时间采样的差值

0.3 墙上时间(或真实时间)

墙上时间用于衡量绝对时间
单调时间用于计算相对时间
进程时间用于测量进程在运行过程中占用CPU的时间

Unix系统使用大纪元(定义为1970年1月1日00:00:00)以来经过的秒数
表示绝对时间

软件时钟是由内核维护的时钟,操作系统通过软件时钟来追踪时间进程。
内核初始化一个遵循特定频率的周期计时器,一般称之为系统计时器(system )
当一个计时器间隔结束时,内核将消耗时间增加一个单位,称之为一个tick或者jiffy
linux中的系统定时器频率称之为HZ

posix定义运行时确定系统计时器频率的方式

long hz;
hz = sysconf (_SC_CLK_TCK);
printf("system timer is %ld\n",hz);

high resolution timers:高分辨率定时器

1.时间的数据结构

1.1 time_t:

表示大纪元以来已流逝的秒数
定义在头文件<time.h>
typedef long time_t;

1.2 struct timeval:

毫秒级精度
定义在头文件<sys/time.h>
struct timeval {
time_t tv_sec;//seconds
suseconds_t tv_usec;//microseconds 通常为整型
};

1.3 struct timespec:

纳秒级精度
定义在头文件<time.h>
struct timespec {
time_t tv_sec;//seconds
long tv_nsec;//nanoseconds [0 … 999999999]
};

注意:纳秒级或毫秒级精度是时间数据结构所能表示的精度
但是,在实际使用过程中,系统计时器可能无法实现这个精度
在函数中尽量采用可用的高精度时间数据结构,这样可以利用系统所提供的各种精度

1.4 struct tm:

定义在头文件<time.h>
struct tm {
int tm_sec; /* seconds 0~59 /
int tm_min; /
minutes 0~59 /
int tm_hour; /
hours 0~23 /
int tm_mday; /
the day of the month 1~31 /
int tm_mon; /
the month 0~11 /
int tm_year; /
the year 从1900年以来的年数*/
int tm_wday; /* the day of the week 0~6*/
int tm_yday; /* the day in the year 0~365*/
//以下变量暂无实用价值
int tm_isdst; /* daylight savings time /
#ifdef _BSD_SOURCE
long tm_gmtoff; /
time zone’s offset from
GMT */
const char tm_zone; / time zone
abbreviation /
#endif /
_BSD_SOURCE */
};

2.POSIX时钟种类

CLOCK_MONOTONIC:线性增长时钟
CLOCK_REALTIME:系统级真实时间(墙上时间)
CLOCK_PROCESS_CPUTIME_ID:处理器分配给每个进程的高精度时钟
CLOCK_THREAD_CPUTIME_ID:类似进程时钟,处理器分配给每个线程的高精度时钟

POSIX定义了四个时钟源,但只有CLOCK_REALTIME是必须实现的
因此,可移植的代码只使用CLOCK_REALTIME

3. clock_getres()获取时间源精度

#include <time.h>
int clock_getres (clockid_t clock_id, struct timespec *res);

示例:获取POSIX时钟的时钟源

struct timespec ts_1;
	clockid_t clocks[] = {
		CLOCK_REALTIME,
		CLOCK_MONOTONIC,
		CLOCK_PROCESS_CPUTIME_ID,
		CLOCK_THREAD_CPUTIME_ID,
		(clockid_t) -1 };
	
	for (i = 0; clocks[i] != (clockid_t) -1; i++) {	
		ret = clock_getres(clocks[i], &ts_1);
		if (ret)
			perror("clock_getres");
		else
			printf("clock = %d sec = %ld nsec = %ld\n",
				clocks[i], ts_1.tv_sec, ts_1.tv_nsec);
	}
$ ./a.out
clock = 0 sec = 0 nsec = 10000000 //10ms
clock = 1 sec = 0 nsec = 10000000 //10ms
clock = 2 sec = 0 nsec = 1
clock = 3 sec = 0 nsec = 1

CLOCK_REALTIME 和 CLOCK_MONOTONIC都和系统定时器精度有关
CLOCK_PROCESS_CPUTIME_ID 和CLOCK_THREAD_CPUTIME_ID和
TSC(Time Stamp Counter, N纳秒级精度)有关

4.获取当前时间

4.1 time()获取从大纪元以来流逝的秒数

#include <time.h>
time_t time (time_t *t);
time_t忽略了闰秒,认为被4整除的年份都是闰年
time_t表示法的要点不在于精确,在于一致。

4.2 gettimeofday()函数扩展了time(),可提供微妙级精度的支持

#include <sys/time.h>
int gettimeofday (struct timeval *tv, struct timezone *tz);
第二个参数忽略
示例:

int ret;
	struct timeval tv;

	ret = gettimeofday(&tv, NULL);
	if (ret)
		perror("gettimeofday");
	else
		printf("seconds=%ld useconds=%ld\n",
			(long) tv.tv_sec, (long) tv.tv_usec);

4.3 clock_gettime()获取一个指定时钟源的时间,可提供纳秒级精度的支持

#include <time.h>
int clock_gettime (clockid_t clock_id, struct timespec *ts);

示例:

clockid_t clocks[] = {
		CLOCK_REALTIME,
		CLOCK_MONOTONIC,
		CLOCK_PROCESS_CPUTIME_ID,
		CLOCK_THREAD_CPUTIME_ID,
		(clockid_t) -1 };
	struct timespec ts_1;
	
	for (i = 0; clocks[i] != (clockid_t) -1; i++) {	
		ret = clock_gettime(clocks[i], &ts_1);
		if (ret)
			perror("clock_getres");
		else
			printf("clock = %d sec = %ld nsec = %ld\n",
				clocks[i], ts_1.tv_sec, ts_1.tv_nsec);
	}
	int sum = 0;
	i = 4 * 10^8;	
	while(--i > 0) {
		sum += i;
	}
	
	for (i = 0; clocks[i] != (clockid_t) -1; i++) {	
		ret = clock_gettime(clocks[i], &ts_1);
		if (ret)
			perror("clock_getres");
		else
			printf("clock = %d sec = %ld nsec = %ld\n",
				clocks[i], ts_1.tv_sec, ts_1.tv_nsec);
	}
$ ./a.out
clock = 0 sec = 94546 nsec = 530000000
clock = 1 sec = 94546 nsec = 530000000
clock = 2 sec = 0 nsec = 10000000
clock = 3 sec = 0 nsec = 10000000
clock = 0 sec = 94564 nsec = 30000000
clock = 1 sec = 94564 nsec = 30000000
clock = 2 sec = 17 nsec = 510000000
clock = 3 sec = 17 nsec = 510000000

4.4 times()获取正在运行的当前进程及其子进程的进程时间

#include <sys/times.h>
struct tms {
clock_t tms_utime; /* user time consumed /
clock_t tms_stime; /
system time consumed /
clock_t tms_cutime; /
user time consumed by children /
clock_t tms_cstime; /
system time consumed by children */
};
clock_t times (struct tms *buf);

示例:

struct tms process_timer;
	
	times(&process_timer);
	printf("process timer:tms_utime  %d\n", process_timer.tms_utime); 
	printf("process timer:tms_stime  %d\n", process_timer.tms_stime); 
	printf("process timer:tms_cutime %d\n", process_timer.tms_cutime);
	printf("process timer:tms_cstime %d\n", process_timer.tms_cstime);
	
	i = 400000000;	
	while(--i > 0) {
		sum += i;
	}
	printf("hello times() which is in <sys/time!s!.h>\n");
	times(&process_timer);
	printf("process timer:tms_utime  %d\n", process_timer.tms_utime); 
	printf("process timer:tms_stime  %d\n", process_timer.tms_stime); 
	printf("process timer:tms_cutime %d\n", process_timer.tms_cutime);
	printf("process timer:tms_cstime %d\n", process_timer.tms_cstime);
$: ./a.out
process timer:tms_utime  0
process timer:tms_stime  0
process timer:tms_cutime 0
process timer:tms_cstime 0
hello times() which is in <sys/time!s!.h>
process timer:tms_utime  1750  //unit:tick. current val equal 10ms
process timer:tms_stime  0
process timer:tms_cutime 0
process timer:tms_cstime 0

5.设置时间

#include <time.h>
int stime (time_t *t);
与time()相对应

#include <sys/time.h>
int settimeofday (const struct timeval *tv, const struct timezone *tz);
与gettimeofday()相对应, 微妙级精度

#include <time.h>
int clock_settime (clockid_t clock_id, const struct timespec *ts);
与clock_gettime()相对应, 纳秒级精度

6.校时

#include <sys/time.h>
int adjtime (const struct timeval *delta, struct timeval *olddelta);

绝对时间发生改变
而相对时间不改变
机制是:调整时钟改变的速度

7.睡眠和等待

7.1 sleep()秒级精度睡眠

#include <unistd.h>
unsigned int sleep (unsigned int seconds);
秒级精度
示例:

unsigned int s = 5;
	while ((s = sleep (s)))
		;

7.2 usleep()微妙级精度睡眠

/* BSD version /
#include <unistd.h>
void usleep (unsigned long usec);
/
SUSv2 version */
#define _XOPEN_SOURCE 500
#include <unistd.h>
int usleep (useconds_t usec);

为了提高可移植性,假设参数为无符号整型,并且不能依赖其返回值
示例:

unsigned int usecs = 200;
	usleep (usecs);

7.3 nanosleep()纳秒级精度睡眠

#include <time.h>
int nanosleep (const struct timespec *req, struct timespec *rem);

示例:

struct timespec req = { .tv_sec = 1, .tv_nsec = 0 };
	struct timespec rem, *a = &req, *b = &rem;
	struct timespec *tmp = a;
	
	/* sleep for 1s */
	while (nanosleep (a, b) && errno == EINTR) {	
		a = b;
		b = tmp;
	}

优点:
纳秒级精度
符合POSIX.1b标准
实现无需信号

7.3 clock_nanosleep()符合POSIX标准,为最高级的睡眠调用

#include <time.h>
int clock_nanosleep (clockid_t clock_id,
int flags,
const struct timespec *req,
struct timespec *rem);
示例:

//相对睡眠
	struct timespec ts = { .tv_sec = 1, .tv_nsec = 500000000 };
	int ret;
	ret = clock_nanosleep (CLOCK_MONOTONIC, 0, &ts, NULL);
	if (ret)
		perror ("clock_nanosleep");
//绝对睡眠
	struct timespec ts;
	int ret;
	/* we want to sleep until one second from NOW */
	ret = clock_gettime (CLOCK_MONOTONIC, &ts);
	if (ret) {
		perror (”clock_gettime”);
		return;
	}
	ts.tv_sec += 1;
	printf (”We want to sleep until sec=%ld nsec=%ld\n”,
	ts.tv_sec, ts.tv_nsec);
	ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL);
	if (ret)
		perror (”clock_nanosleep”);

如果程序对时间的要求不严格,可采用相对睡眠,
如果程序对时间的要求严格,可采用绝对睡眠,以此避免潜在的竞争条件

7.4 select 可移植睡眠实现

void milliseconds_sleep(unsigned long ms)
{
    int err;
    struct timeval tv;
    tv.tv_sec = ms/1000;
    tv.tv_usec = (ms%1000)*1000;

    do{
       err = select(0,NULL,NULL,NULL,&tv);
    }while(err<0 && errno==EINTR);
}

7.5 睡眠超限原因

原因1:调度行为
原因2:定时器的粒度较大

7.6睡眠使用注意事项

如果可能的话,尽量避免使用睡眠
使用睡眠忙等待事件发生是糟糕的设计
应使用在文件描述符上阻塞,允许内核处理睡眠和唤醒进程的代码

8.定时器

内核提供了在一定时间后通知进程的机制。
定时器超时所需的时间叫做延迟(delay),或者叫做超时值(expiration)

8.1 简单的闹钟

#include <unistd.h>
unsigned int alarm (unsigned int seconds);

示例:

void alarm_handler (int signum)
{
	printf (”Five seconds passed!\n”);
}

void func (void)
{
	signal (SIGALRM, alarm_handler);
	alarm (5);
	pause ();
}

8.2 间歇定时器

#include <sys/time.h>
int getitimer (int which, struct itimerval *value);
int setitimer (int which, const struct itimerval *value, struct itimerval *ovalue);

8.3 高级定时器

#include <signal.h>
#include <time.h>
int timer_create (clockid_t clockid, struct sigevent *evp, timer_t *timerid);
建立一个与POSIX时钟clockid相关联的新定时器
clockid_t clockid: 指定POSIX时钟
CLOCK_REALTIME,
CLOCK_MONOTONIC,
CLOCK_PROCESS_CPUTIME_ID
CLOCK_THREAD_CPUTIME_ID,
struct sigevent *evp:定义了定时器到期时的异步通知方式
若evp == NULL, sigev_notify为SIGEV_SIGNAL
sigev_signo为SIGALRM
sigev_val为定时器ID
#include <signal.h>
struct sigevent {
union sigval sigev_value;
int sigev_signo;
int sigev_notify;
//SIGEV_NONE
//SIGEV_SIGNAL:发送sigev_signo信号给进程
//SIGEV_THREAD:在当前进程创建新线程,
// 执行sigev_notify_function函数
// 参数为sigev_value
// sigev_notify_attributes为线程属性
void (*sigev_notify_function)(union sigval);
pthread_attr_t *sigev_notify_attributes;
};

union sigval {
	int sival_int;
	void *sival_ptr;
};

timer_t *timerid:定时器ID

#include <time.h>
int timer_settime (timer_t timerid,
int flags,
const struct itimerspec *value,
struct itimerspec *ovalue);
设置timerid指定的定时器的过期时间为value

struct itimerspec {
struct timespec it_interval; /* next value /
struct timespec it_value; /
current value */
};
struct itimerspec *value:
it_value指定了定时器的过期时间。
it_interval指定了定时器的重载时间
当定时器过期时,使用it_interval的值更新it_value
如果it_interva == 0,定时器在it_value过期后停止运行
int flags:
TIMER_ABSTIME:it_value指定的时间为绝对时间
0:it_value指定的时间为相对时间
struct itimerspec *ovalue:
存储之前定时器的设置

#include <time.h>
int timer_gettime (timer_t timerid, struct itimerspec *value);

#include <time.h>
int timer_getoverrun (timer_t timerid);
确定定时器有无超限
示例:

ret = timer_getoverrun (timer2);
	if (ret == -1)
		perror ("timer_getoverrun");
	else if (ret == 0)
		printf ("no overrun\n");
	else
		printf ("%d overrun(s)\n", ret);

#include <time.h>
int timer_delete (timer_t timerid);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值