一、相关时间函数
1. gettimeofday()
获取日历时间。
#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);
- timeval结构体
struct timeval {
time_t tv_sec; // 秒
suseconds_t tv_usec; // 微秒(long int)
};
2. time()
返回自Epoch(格林威治标准时间1970.01.01 0:00AM)以来的秒数。
#include <time.h>
time_t time(time_t *timep);
参数timep存储返回的时间。若timep为空,则直能从函数返回值获得。
time_t t = time(NULL);
3. clock()
计时函数。返回值为从程序启动到调用该函数所占用CPU的时间,实际为CPU时钟计时单元(clock tick)数。
#include <time.h>
clock_t clock(void) ;
由于不同系统或编译器对于每秒的时钟单元数定义不同,所以直接输出会有所不同。所以还定义了常量CLOCKS_PER_SEC,表示一秒钟会有多少个时钟计时单元,其定义如下:
#define CLOCKS_PER_SEC ((clock_t)1000)
CLOCKS_PER_SEC在Linux 4.15.0-32-generic
系统上数值为1000000。
二、间隔定时器
1. setitimerval()
使用系统调用setitimer()
来创建间隔定时器,这种定时器会在一定时间后到期,并可以在到期后每隔一段时间到期一次。
#include <sys/time.h>
int setitimer(int which, const struct itimerval *new_value,
struct itimerval *old_value);
which参数的可选项:
ITIMER_REAL
以真实时间计时,到期产生SIGALARM信号ITIMER_VIRTUAL
以虚拟时间(用户模式下CPU时间)计时ITIMER_PROF
创建profiling定时器以内核态与用户态CPU时间总和计时,到期会产生SIGPROF信号
itimerval结构体
struct itimerval {
struct timeval it_interval; // 间隔定时器的间隔时间
struct timeval it_value; // 定时器到期的剩余时间
};
其中,it_value表示距离定时器到期的剩余时间,it_interval记录定时器的周期时间(或不进行周期性定时,it_interval中两个值同为0时表示没有周期性定时,即一次性定时器)。若进行周期性定时,则在每次到时后会将it_interval重新存储倒计时的间隔时间。
2. getitimerval()
获取定时器当前状态。
#include <sys/time.h>
int getitimerval(int which, struct itimerval *curr_value );
curr_value存储定时器当前状态,其内容与调用setitimerval()返回的old_value内容相同。
3. 实时定时器的使用
/* 运行说明:
./timer 1 800000 1 0
第二个参数为倒计时的秒数,第三个参数为倒计时的微秒数,
第四个参数为定时器间隔时间的秒数,第五个参数为定时器间隔时间的微秒数
后四个参数可以省略,默认为 2 0 0 0
*/
#include <iostream>
#include <string.h>
#include <sys/time.h>
#include <signal.h>
static volatile sig_atomic_t gotAlam = 0;
/* 打印时间,includeTimer表示是否为第一次打印,第一次只打印前两个数字 */
static void displayTimes( const char *msg, bool includeTimer ) {
struct itimerval itv;
static struct timeval start; // 起始状态
struct timeval curr; // 当前状态
static int callNum = 0; // 当前函数被调用次数
if( callNum == 0 ) {
if( gettimeofday( &start, nullptr ) == -1 ) {
perror( "gettimeofday" );
}
}
/* 每20行打印一次提示信息 */
if( callNum % 20 == 0 ) {
printf(" Elapsed Value Interval\n");
}
if( gettimeofday( &curr, NULL ) == -1 ) {
perror( "gettimeofday" );
}
printf("%-7s %6.2f", msg, curr.tv_sec - start.tv_sec + (curr.tv_usec - start.tv_usec) / 1000000.0 );
/* 可以打印后两个数字 */
if( includeTimer ) {
if( getitimer( ITIMER_REAL, &itv ) == -1 ) {
perror( "getitimer" );
}
printf(" %6.2f %6.2f", itv.it_value.tv_sec + itv.it_value.tv_usec / 1000000.0,
itv.it_interval.tv_sec + itv.it_interval.tv_usec / 1000000.0);
}
printf("\n");
callNum++;
}
/* 信号处理函数 */
static void sigalrmHandler( int sig ) {
gotAlam = 1;
}
int main( int argc, char **argv ) {
struct itimerval itv;
clock_t preClock;
int maxSigs = 0; // 信号触发最大次数
int sigCnt = 0; // 信号已触发次数
struct sigaction sa;
sigemptyset( &sa.sa_mask );
sa.sa_flags = 0;
sa.sa_handler = sigalrmHandler;
if( sigaction( SIGALRM, &sa, NULL ) == -1 ) {
perror( "sigaction" );
}
maxSigs = ( itv.it_interval.tv_sec == 0 && itv.it_interval.tv_usec == 0 ) ? 1 : 3;
displayTimes( "start:", false );
itv.it_value.tv_sec = (argc > 1) ? atoi( argv[1] ) : 2;
itv.it_value.tv_usec = (argc > 2) ? atoi( argv[2] ) : 0;
itv.it_interval.tv_sec = (argc > 3) ? atoi( argv[3] ) : 0;
itv.it_interval.tv_usec = (argc > 4) ? atoi( argv[4] ) : 0;
if( setitimer( ITIMER_REAL, &itv, 0 ) == -1 ) {
perror( "setitimer" );
}
preClock = clock();
while( true ) {
while( ( clock() - preClock ) * 10 / CLOCKS_PER_SEC < 5 ) {
/* 定时器时间到 */
if( gotAlam ) {
gotAlam = false;
displayTimes( "ALARM:", true );
sigCnt++;
if( sigCnt >= maxSigs ) {
printf("That's all folk\n");
exit( EXIT_SUCCESS );
}
}
}
preClock = clock();
displayTimes( "Main:", true );
}
}
三、为阻塞操作设置超时
1. alarm()
创建一次性实时定时器。
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
seconds表示倒计时的秒数。到期后会发送SIGALARM信号。
调用alarm(0)可以屏蔽所有现有定时器。
2. 给read()设置读超时
#include <iostream>
#include <string.h>
#include <sys/time.h>
#include <signal.h>
#include <unistd.h>
using namespace std;
const int BUFFER_SIZE = 200;
/* 信号处理函数 */
static void handler( int sig ) {
printf("caught signal\n");
}
int main( int argc, char **argv ) {
struct sigaction sa;
char buf[BUFFER_SIZE];
ssize_t numRead;
int savedErrno;
sa.sa_flags = ( argc > 2 ) ? SA_RESTART : 0;
sigemptyset( &sa.sa_mask );
sa.sa_handler = handler;
if( sigaction( SIGALRM, &sa, NULL ) == -1 ) {
perror("sigaction");
}
/* 设置倒计时 */
alarm( (argc > 1) ? atoi(argv[1]) : 10 );
numRead = read( STDIN_FILENO, buf, BUFFER_SIZE - 1 );
savedErrno = errno;
alarm(0); // 将现有定时器屏蔽
errno = savedErrno;
if( numRead == -1 ) {
if( errno == EINTR ) { // read系统调用被信号打断,即收到超时信号
printf("Read timed out\n");
} else {
perror("read");
}
} else { // 未超时
printf("Successful read %ld bytes : %.*s", long(numRead), int(numRead), buf);
}
exit(EXIT_SUCCESS);
}