定时器timer_create timerfd_create

一、timer_create

进程可以通过调用timer_create()创建特定的定时器,定时器是每个进程自己的,不是在fork时继承的,不会被传递给子进程。
编译时加编译选项 -lrt。
包含头文件

1. timer_create

int timer_create(clockid_t clockid, struct sigevent *sevp, timer_t  *timerid);

参数: 
(1) clockid定义了定时器计时的方法,有如下几个值: 
	CLOCK_REALTIME : 可设置的系统范围的实时时钟
	CLOCK_MONOTONIC : 单调递增的时钟,系统启动后不会被改变 
(2) timerid
	定时器ID存储在timerid中,定时器ID在当前进程中是唯一的,除非定时器被删除。 
(3) sevp  
union sigval {
	int sival_int; 	
	void *sival_ptr; 
}; 
struct sigevent 
{ 	
    int sigev_notify;					//notification method 	
    int sigev_signo;					//notification signal 	
    union signal sigev_value;						//data passed with notification 	
    void (*sigev_notify_function)(union sigval);	//function used for thread
    pthread_attr_t *sigev_notify_attribute;			//attribute for notification
};
参数sevp指出该如何通知调用者定时器超时信息,根据sevp.sigev_notify字段,该字段有如下值: 
SIGEV_NONE: 	定时器超时后不使用异步通知,可能的情况是使用timer_gettime来监控定时器 
SIGEV_SIGNAL:  	一旦超时,产生一个信号,任何时候,至多只有一个信号会发送到队列里面,可以使用
				timer_getoverrun来获取超时次数
SIGEV_THREAD : 	新建一个线程去处理,该线程执行sigev_notif_function为入口函数

2. timer_settime

int timer_settime(timer_t timerid, int flags, const struct itimerspec *new_value, 
												struct itimerspec *old_value)

struct timespec {
		time_t tv_sec;
		long tv_nsec;
};
strict itimerspec {
		struct timespec it_interval;
		struct timespec it_value;
};
flags : 
	0 相对时间 
	1 绝对时间(TIMER_ABSTIME)
new_value:    
	如果参数new_value不为0,则启动定时器。如果定时器已经启动则覆盖之前的定时器设置
	如果参数new_value为0则关闭定时器。

3. timer_gettime

int timer_gettime(timer_t timerid, struct itimerspec *curr_value);
获得一个活动定时器的剩余时间,如果返回0说明已经超时

4. timer_getoverrun

timer_getoverrun(timer_t timerid)  

有可能一个定时器到期了,而同一定时器上一次到期时产生的信号还处于挂起状态。在这种情况下,其中的一个信号可能会丢失。这就是定时器超限。
程序可以通过调用timer_getoverrun来确定一个特定的定时器出现这种超限的次数。定时器超限只能发生在同一个定时器产生的信号上。由多个定时器甚至是那些使用相同的时钟和信号的定时器,所产生的信号都会排队而不会丢失。
执行成功时,timer_getoverrun()会返回定时器初次到期与通知进程(例如通过信号)定时器已到期之间额外发生的定时器到期次数。
举例来说,一个1ms的定时器运行了10ms,则此调用会返回9。如果超限运行的次数等于或大于DELAYTIMER_MAX,则此调用会返回DELAYTIMER_MAX。
执行失败时,此函数会返回-1并将errno设定会EINVAL,这个唯一的错误情况代表timerid指定了无效的定时器。

5. timer_delete

int timer_delete(timer_t timerid);
删除定时器

6. 使用示例

https://blog.csdn.net/weixin_42639771/article/details/90607501

二、timerfd_create

1. 相关数据结构体

(1) struct timespec struct itimerspec

struct timespec {
    time_t tv_sec;                /* Seconds */
    long   tv_nsec;               /* Nanoseconds */
};
struct itimerspec {
    struct timespec it_interval;  /*定时间隔周期*/
    struct timespec it_value;     /* Initial expiration (第一次定时/到时/超时时间)*/
};

it_interval不为0则表示是周期性定时器。
it_value和it_interval都为0表示停止定时器

(2) time_t

time_t是long类型,打印时使用%ld
time_t ltime = time(NULL);
printf("now time:%ld\n", ltime);

(3) timer_fd的读操作

使用epoll监听timer_fd,定时时间到后必须读timerd,不然会一直存在epoll事件,因为timerfd可读。
读timerfd的读取类型是uint64_t,可以直接使用,如果编译报错加头文件stdint.h。
timerfd读出来的值一般是1,表示超时次数。

int timerfd;
uint64_t exp;
read(timerfd, &exp, sizeof(exp));
printf("exp = %llu\n", (unsigned long long)exp);	//需要强制转换一下

2. timerfd_create

int timerfd_create(int clockid, int flags)

1. clockid:
CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0  开始计时,中间时刻
				如果系统时间被用户改成其他,则对应的时间相应改变
CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
2. flags:
TFD_NONBLOCK: 	非阻塞模式)     
TFD_CLOEXEC:	表示当程序执行exec函数时本fd将被系统自动关闭,表示不传递

使用时一般  timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);

3、timerfd_settime

int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, 
										struct itimerspec *old_value)
flags:      
  	0 相对时间。  
  	1 绝对时间(TFD_TIMER_ABSTIME)
new_value: 
   	定时器的超时时间以及超时间隔时间
old_value: 
	如果不为NULL, old_vlaue返回之前定时器设置的超时时间,具体参考timerfd_gettime()函数

如果flags设置为1,那么epoll监听立马就有事件可读,并且读出的timerfd不是1,因为开机已经过去了很久。
如果设置为0,那么会按照设定的时间定第一个定时器,到时后读出的超时次数是1。

4、一个timerfd使用例子

/********************************************************
* Filename: timerfd.c
* Author: zhangwj
* Desprition: a sample program of timerfd
* Date: 2017-04-17
* Warnning:
********************************************************/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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;
}

你可能感兴趣的:(linux定时器)