[原]定时器与超时的设置

王良 18/08/23 09:43:00


一、相关时间函数

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);
}
作者:liushall 发表于 2018/08/23 09:43:00 原文链接 https://blog.csdn.net/liushall/article/details/81974895
阅读:50