libevent源码深度剖析之evutil_time.c

第一章 time基础知识

sleep()-------以秒为单位
#include<unistd.h>
unsigned int sleep(unsigned int seconds);
return:若进程暂停到参数seconds 所指定的时间,成功则返回0,若有信号中断则返回剩余秒数。
在linux中,sleep是通过nanosleep实现的。在一些其他系统中(例如POSIX.1),它是通过alarm()来实现的。

usleep()----以微秒为单位
#include<unistd.h>
unsigned int usleep(unsigned int useconds);
return:若进程暂停到参数seconds 所指定的时间,成功则返回0,若有信号中断则返回剩余微秒数。

nanosleep( )---------以纳秒为单位
#include<time.h>
 struct timespec
{
  time_t  tv_sec;         /* 秒seconds */
  long    tv_nsec;        /* 纳秒nanoseconds */
};
int nanosleep(const struct timespec *req, struct timespec *rem);
return: 若进程暂停到参数*req所指定的时间,成功则返回0,若有信号中断则返回-1,并且将剩余微秒数记录在*rem中。
req->tv_sec是以秒为单位,而tv_nsec以毫微秒为单位(10的-9次方秒)。
由于调用nanosleep是是进程进入TASK_INTERRUPTIBLE,这种状态是会相应信号而进入TASK_RUNNING状态的。

1.1 linux下用time(NULL)函数和localtime()获取当前时间

1.1.2 time_t time(time_t *timer)

函数原型: time_t time(time_t *timer)
函数用途: 得到机器的日历时间或者设置日历时间
头 文 件: time.h
输入参数: timer=NULL时,得到机器日历时间, =时间数值时 用于设置日历时间;time_t是一个long类型

1.1.3 struct tm *localtime(const time_t *timer)

函数原型: struct tm *localtime(const time_t *timer)
函数用途: 返回一个以tm结构表达的机器时间信息
头 文 件: time.h
输入参数: timer:使用time()函数获得的机器时间;

//结构tm的定义为:  
  struct  tm  
  {  
      int tm_sec; /*  Seconds:  0-59  (K&R  says  0-61?)  */  
      int tm_min; /*  Minutes:  0-59  */  
      int tm_hour; /*  Hours  since  midnight:  0-23  */  
      int tm_mday; /*  Day  of  the  month:  1-31  */  
      int tm_mon; /*  Months  *since*  january:  0-11  */  
      int tm_year; /*  Years  since  1900  */  
      int tm_wday; /*  Days  since  Sunday  (0-6)  */  
      int tm_yday; /*  Days  since  Jan.  1:  0-365  */  
      int tm_isdst; /*  +1  Daylight  Savings  Time,  0  No  DST,   夏令时,0无夏令时
        *  -1  don't  know  */  
  };

1.2 struct timeval

/*
 * Structure used in select() call, taken from the BSD file sys/time.h.
 */
struct timeval {
        long    tv_sec;         /* seconds */
        long    tv_usec;        /* and microseconds */
};

tv_sec 代表多少秒
tv_usec 代表多少微秒 1000000 微秒 = 1秒

1.2.1 使用例程

#include<stdio.h>
#include<sys/time.h>
#include<unistd.h>

void hello_world(void)
{
    printf("Hello world!!!!\r\n");
}

int main(void)
{
    struct  timeval   tv_begin,tv_end;

    gettimeofday(&tv_begin,NULL);
    hello_world();
    gettimeofday(&tv_end,NULL);
    
    printf("tv_begin_sec:%ld\n",tv_begin.tv_sec);
    printf("tv_begin_usec:%ld\n",tv_begin.tv_usec);
    printf("tv_end_sec:%ld\n",tv_end.tv_sec);
    printf("tv_end_usec:%ld\n",tv_end.tv_usec);

    return 0;
}  

1.3 struct timespec

下面是struct timespec的定义

struct timespec
{
    __time_t tv_sec;        /* Seconds. */
    long   tv_nsec;       /* Nanoseconds. */
};

可以通过 int clock_gettime(clockid_t clk_id, struct timespec *tp)来获取
参数 clk_id : 检索和设置的clk_id指定的时钟时间。可以设置如下:

  • CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时,如果系统时间被用户改成其他,则对应的时间相应改变
  • CLOCK_REALTIME_COARSE:和CLOCK_REALTIME类似,但是执行速度快,精度低
  • CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
  • CLOCK_MONOTONIC_COARSE :和CLOCK_MONOTONIC类似,但是执行速度快,精度低
  • CLOCK_BOOTTIME:和CLOCK_MONOTONIC 类似,但是包括了系统休眠的时间。
  • CLOCK_PROCESS_CPUTIME_ID:本进程到当前代码系统CPU花费的时间
  • CLOCK_THREAD_CPUTIME_ID:本线程到当前代码系统CPU花费的时间

参数:tp 返回时间值

  • 返回值 0成功, 1失败。

例如
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);

1.3.1 使用例程

#include <time.h>
#include <stdio.h>
// 返回自系统开机以来的毫秒数(tick)
unsigned long GetTickCount()
{
    struct timespec ts;

    clock_gettime(CLOCK_MONOTONIC, &ts);

    return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
}


int main()
{
    struct timespec time1 = { 0, 0 };

    clock_gettime(CLOCK_REALTIME, &time1);
    printf("CLOCK_REALTIME: %ld, %ld\n", time1.tv_sec, time1.tv_nsec);

    clock_gettime(CLOCK_MONOTONIC, &time1);
    printf("CLOCK_MONOTONIC: %ld, %ld\n", time1.tv_sec, time1.tv_nsec);

    clock_gettime(CLOCK_MONOTONIC_RAW, &time1);
    printf("CLOCK_MONOTONIC_RAW: %ld, %ld\n", time1.tv_sec, time1.tv_nsec);

    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time1);
    printf("CLOCK_PROCESS_CPUTIME_ID: %ld, %ld\n", time1.tv_sec,
    time1.tv_nsec);

    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time1);
    printf("CLOCK_THREAD_CPUTIME_ID: %ld, %ld\n", time1.tv_sec,
    time1.tv_nsec);

    printf("\n%ld\n", time(NULL));

    printf("tick count in ms: %lu\n", GetTickCount());

    return 0;
}

运行结果:
在这里插入图片描述

1.4 evutil_time.c中的应用

快速确定linux下long long最大值

		#define INT_MAX ((int)(~0U>>1))
		#define INT_MIN (-INT_MAX - 1)
		#define UINT_MAX (~0U)
		#define LONG_MAX ((long)(~0UL>>1))
		#define LONG_MIN (-LONG_MAX - 1)
		#define ULONG_MAX (~0UL)
		#define LLONG_MAX ((long long)(~0ULL>>1))
		#define LLONG_MIN (-LLONG_MAX - 1)
		#define ULLONG_MAX (~0ULL) 
/* Define to 1 if you have the <memory.h> header file. */
#define EVENT__HAVE_MEMORY_H 1

/* Define to 1 if you have the `mmap' function. */
#define EVENT__HAVE_MMAP 1

/* Define to 1 if you have the `nanosleep' function. */
#define EVENT__HAVE_NANOSLEEP 1

/* Define to 1 if you have the <netdb.h> header file. */
#define EVENT__HAVE_NETDB_H 1

/* Define to 1 if you have the <netinet/in6.h> header file. */
/* #undef EVENT__HAVE_NETINET_IN6_H */

/* Define to 1 if you have the <netinet/in.h> header file. */
#define EVENT__HAVE_NETINET_IN_H 1

/* Define to 1 if you have the <netinet/tcp.h> header file. */
#define EVENT__HAVE_NETINET_TCP_H 1

/* On Linux kernels at least up to 2.6.24.4, epoll can't handle timeout
 * values bigger than (LONG_MAX - 999ULL)/HZ.  HZ in the wild can be
 * as big as 1000, and LONG_MAX can be as small as (1<<31)-1, so the
 * largest number of msec we can support here is 2147482.  Let's
 * round that down by 47 seconds.
 * /
long evutil_tv_to_msec_(const struct timeval *tv)
{
	if (tv->tv_usec > 1000000 || tv->tv_sec > MAX_SECONDS_IN_MSEC_LONG)
		return -1;

	return (tv->tv_sec * 1000) + ((tv->tv_usec + 999) / 1000);
}

/*
  Replacement for usleep on platforms that don't have one.  Not guaranteed to
  be any more finegrained than 1 msec.
 */
void
evutil_usleep_(const struct timeval *tv)
{
	if (!tv)
		return;
#if defined(_WIN32)
	{
		long msec = evutil_tv_to_msec_(tv);
		Sleep((DWORD)msec);
	}
#elif defined(EVENT__HAVE_NANOSLEEP)
	{
		struct timespec ts;
		ts.tv_sec = tv->tv_sec;
		ts.tv_nsec = tv->tv_usec*1000;
		nanosleep(&ts, NULL);
	}
#elif defined(EVENT__HAVE_USLEEP)
	/* Some systems don't like to usleep more than 999999 usec */
	sleep(tv->tv_sec);
	usleep(tv->tv_usec);
#else
	{
		struct timeval tv2 = *tv;
		select(0, NULL, NULL, NULL, &tv2);
	}
#endif
}

int evutil_date_rfc1123(char *date, const size_t datelen, const struct tm *tm)
{
	static const char *DAYS[] =
		{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
	static const char *MONTHS[] =
		{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

	time_t t = time(NULL);

#ifndef _WIN32
	struct tm sys;
#endif

	/* If `tm` is null, set system's current time. */
	if (tm == NULL) {
#ifdef _WIN32
		/** TODO: detect _gmtime64()/_gmtime64_s() */
		tm = gmtime(&t);
#else
		gmtime_r(&t, &sys);
		tm = &sys;
#endif
	}

	return evutil_snprintf(
		date, datelen, "%s, %02d %s %4d %02d:%02d:%02d GMT",
		DAYS[tm->tm_wday], tm->tm_mday, MONTHS[tm->tm_mon],
		1900 + tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec);
}

/*
   This function assumes it's called repeatedly with a
   not-actually-so-monotonic time source whose outputs are in 'tv'. It
   implements a trivial ratcheting mechanism so that the values never go
   backwards.
 */
static void adjust_monotonic_time(struct evutil_monotonic_timer *base, struct timeval *tv)
{
	evutil_timeradd(tv, &base->adjust_monotonic_clock, tv);

	if (evutil_timercmp(tv, &base->last_time, <)) {
		/* Guess it wasn't monotonic after all. */
		struct timeval adjust;
		evutil_timersub(&base->last_time, tv, &adjust);
		evutil_timeradd(&adjust, &base->adjust_monotonic_clock,
		    &base->adjust_monotonic_clock);
		*tv = base->last_time;
	}
	base->last_time = *tv;
}

/*
   Allocate a new struct evutil_monotonic_timer
 */
struct evutil_monotonic_timer *
evutil_monotonic_timer_new(void)
{
  struct evutil_monotonic_timer *p = NULL;

  p = mm_malloc(sizeof(*p));
  if (!p) goto done;

  memset(p, 0, sizeof(*p));

 done:
  return p;
}

1.5 linux timerfd系列函数总结

1.5.1 timerfd系列函数

timerfd是Linux为用户程序提供的一个定时器接口。这个接口基于文件描述符,通过文件描述符的可读事件进行超时通知,因此可以配合select/poll/epoll等使用。
下面对timerfd系列函数先做一个简单的介绍:

  • timerfd_create()函数
#include <sys/timerfd.h>
int timerfd_create(int clockid, int flags);
/* 
timerfd_create()函数创建一个定时器对象,同时返回一个与之关联的文件描述符。
clockid:clockid标识指定的时钟计数器,可选值(CLOCK_REALTIME、CLOCK_MONOTONIC。。。)
CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时,中间时刻如果系统时间被用户改成其他,则对应的时间相应改变
CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
flags:参数flags(TFD_NONBLOCK(非阻塞模式)/TFD_CLOEXEC(表示当程序执行exec函数时本fd将被系统自动关闭,表示不传递)
*/
  • timerfd_settime()函数
#include <sys/timerfd.h>

struct timespec {
    time_t tv_sec;                /* Seconds */
    long   tv_nsec;               /* Nanoseconds */
};

struct itimerspec {
    struct timespec it_interval;  /* Interval for periodic timer (定时间隔周期)*/
    struct timespec it_value;     /* Initial expiration (第一次超时时间)*/
};
int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value);
/*
    timerfd_settime()此函数用于设置新的超时时间,并开始计时,能够启动和停止定时器;
    fd: 参数fd是timerfd_create函数返回的文件句柄
    flags:参数flags为1代表设置的是绝对时间(TFD_TIMER_ABSTIME 表示绝对定时器);为0代表相对时间。
    new_value: 参数new_value指定定时器的超时时间以及超时间隔时间
    old_value: 如果old_value不为NULL, old_vlaue返回之前定时器设置的超时时间,具体参考timerfd_gettime()函数
    
    ** it_interval不为0则表示是周期性定时器。
       it_value和it_interval都为0表示停止定时器
*/
  • timerfd_gettime()函数
int timerfd_gettime(int fd, struct itimerspec *curr_value);
/*
    timerfd_gettime()函数获取距离下次超时剩余的时间
   curr_value.it_value 字段表示距离下次超时的时间,如果改值为0,表示计时器已经解除
   改字段表示的值永远是一个相对值,无论TFD_TIMER_ABSTIME是否被设置
  curr_value.it_interval 定时器间隔时间
*/
uint64_t exp = 0;
read(fd, &exp, sizeof(uint64_t)); 
//可以用read函数读取计时器的超时次数,改值是一个8字节无符号的长整型
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <sys/epoll.h>
#include <sys/timerfd.h>

#if 0
struct timespec {
    time_t tv_sec;                /* Seconds */
    long   tv_nsec;               /* Nanoseconds */
};

struct itimerspec {
    struct timespec it_interval;  /* Interval for periodic timer */
    struct timespec it_value;     /* Initial expiration */
};
#endif

#define EPOLL_LISTEN_CNT        256
#define EPOLL_LISTEN_TIMEOUT    500

#define LOG_DEBUG_ON 1

#ifdef LOG_DEBUG_ON 
#define LOG_DEBUG(fmt, args...) \
    do {  \
        printf("[DEBUG]:");\
        printf(fmt "\n", ##args); \
    } while(0);
#define LOG_INFO(fmt, args...) \
    do { \
        printf("[INFO]:");\
        printf(fmt "\n", ##args); \
    } while(0);
#define LOG_WARNING(fmt, args...) \
    do { \
        printf("[WARNING]:");\
        printf(fmt "\n", ##args); \
    } while(0);
#else
#define LOG_DEBUG(fmt, args...) 
#define LOG_INFO(fmt, args...) 
#define LOG_WARNING(fmt, args...) 
#endif
#define LOG_ERROR(fmt, args...) \
    do{ \
        printf("[ERROR]:");\
        printf(fmt "\n", ##args);\
    }while(0);

#define handle_error(msg) \
        do { perror(msg); exit(EXIT_FAILURE); } while (0)

static int g_epollfd = -1;
static int g_timerfd = -1;
uint64_t tot_exp = 0;

static void help(void)
{
    exit(0);
}

static void print_elapsed_time(void)
{
    static struct timespec start;
    struct timespec curr;
    static int first_call = 1;
    int secs, nsecs;
    
    if (first_call) {
        first_call = 0;
        if (clock_gettime(CLOCK_MONOTONIC, &start) == -1) 
            handle_error("clock_gettime");
    }   
    
    if (clock_gettime(CLOCK_MONOTONIC, &curr) == -1) 
        handle_error("clock_gettime");
    
    secs = curr.tv_sec - start.tv_sec;
    nsecs = curr.tv_nsec - start.tv_nsec;
    if (nsecs < 0) {
        secs--;
        nsecs += 1000000000;
    }   
    printf("%d.%03d: ", secs, (nsecs + 500000) / 1000000);
}

void timerfd_handler(int fd)
{
    uint64_t exp = 0;
    
    read(fd, &exp, sizeof(uint64_t)); 
    tot_exp += exp;
    print_elapsed_time();
    printf("read: %llu, total: %llu\n", (unsigned long long)exp, (unsigned long long)tot_exp);

    return;
}

void epoll_event_handle(void)
{
    int i = 0;
    int fd_cnt = 0;
    int sfd;
    struct epoll_event events[EPOLL_LISTEN_CNT];    

    memset(events, 0, sizeof(events));
    while(1) 
    {   
        /* wait epoll event */
        fd_cnt = epoll_wait(g_epollfd, events, EPOLL_LISTEN_CNT, EPOLL_LISTEN_TIMEOUT); 
        for(i = 0; i < fd_cnt; i++) 
        {   
            sfd = events[i].data.fd;
            if(events[i].events & EPOLLIN) 
            {   
                if (sfd == g_timerfd) 
                {
                    timerfd_handler(sfd); 
                }   
            }   
        } 
    }   
}

int epoll_add_fd(int fd)
{
    int ret;
    struct epoll_event event;

    memset(&event, 0, sizeof(event));
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET;

    ret = epoll_ctl(g_epollfd, EPOLL_CTL_ADD, fd, &event);
    if(ret < 0) {
        LOG_ERROR("epoll_ctl Add fd:%d error, Error:[%d:%s]", fd, errno, strerror(errno));
        return -1;
    }

    LOG_DEBUG("epoll add fd:%d--->%d success", fd, g_epollfd);
    return 0;    
}

int epollfd_init()
{
    int epfd;

    /* create epoll fd */
    epfd = epoll_create(EPOLL_LISTEN_CNT); 
    if (epfd < 0) {
        LOG_ERROR("epoll_create error, Error:[%d:%s]", errno, strerror(errno));
        return -1;
    }
    g_epollfd = epfd;
    LOG_DEBUG("epoll fd:%d create success", epfd);

    return epfd;
}

int timerfd_init()
{
    int tmfd;
    int ret;
    struct itimerspec new_value;

    new_value.it_value.tv_sec = 2;
    new_value.it_value.tv_nsec = 0;
    new_value.it_interval.tv_sec = 1;
    new_value.it_interval.tv_nsec = 0;
    
    tmfd = timerfd_create(CLOCK_MONOTONIC, 0);
    if (tmfd < 0) {
        LOG_ERROR("timerfd_create error, Error:[%d:%s]", errno, strerror(errno));
        return -1;
    }

    ret = timerfd_settime(tmfd, 0, &new_value, NULL);
    if (ret < 0) {
        LOG_ERROR("timerfd_settime error, Error:[%d:%s]", errno, strerror(errno));
        close(tmfd);
        return -1;
    }

    if (epoll_add_fd(tmfd)) {
        close(tmfd);
        return -1;
    }
    g_timerfd = tmfd;

    return 0;
}

int main(int argc, char **argv)
{
    if (epollfd_init() < 0) {
        return -1;
    }

    if (timerfd_init()) {
        return -1;
    }

    /* event handle */
    epoll_event_handle();

    return 0;
}

参考:
https://www.cnblogs.com/hushaojun/p/7990951.html
https://www.cnblogs.com/wenqiang/p/6698371.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值