Linux应用层定时器timer使用---timerfd_create()、timerfd_settime()、timerfd_gettime()

一、定时器timerfd相关函数及说明

#include <sys/timerfd.h>

int timerfd_create(int clockid, int flags);

int timerfd_settime(int fd, int flags,
				   const struct itimerspec *new_value,
				   struct itimerspec *old_value);

int timerfd_gettime(int fd, struct itimerspec *curr_value);
timerfd_create()创建一个定时器描述符timerfd
clockid时间类型一般使用CLOCK_REALTIME、CLOCK_MONOTONIC、CLOCK_BOOTTIME_ALARM(可唤醒系统),定义在time.h中如下图
flags为0或者O_CLOEXEC/O_NONBLOCK
返回值timerfd(文件描述符)
/*
 * The IDs of the various system clocks (for POSIX.1b interval timers):
 */
#define CLOCK_REALTIME			0
#define CLOCK_MONOTONIC			1
#define CLOCK_PROCESS_CPUTIME_ID	2
#define CLOCK_THREAD_CPUTIME_ID		3
#define CLOCK_MONOTONIC_RAW		4
#define CLOCK_REALTIME_COARSE		5
#define CLOCK_MONOTONIC_COARSE		6
#define CLOCK_BOOTTIME			7
#define CLOCK_REALTIME_ALARM		8
#define CLOCK_BOOTTIME_ALARM		9

timerfd_settime()用来启动或关闭fd指定的定时器
fdtimerfd_create函数返回的定时器文件描述符timerfd
flags1代表设置的是绝对时间;为0代表相对时间
new_value指定新的超时时间,设定new_value.it_value非零则启动定时器,为零关闭定时器,如果new_value.it_interval为0,则定时器只定时一次,即初始那次,否则之后每隔设定时间it_interval超时一次
old_value不为null,则返回定时器这次设置之前的超时时间
timerfd_gettime()获得定时器距离下次超时还剩下的时间。如果调用时定时器已经到期,并且该定时器处于循环模式(设置超时时间时it_interval不为0),那么调用此函数之后定时器重新开始计时

二、 使用read读取timefd超时事件通知的说明

read(2)
If  the  timer  has  already expired one or more times since its settings were last modified using 
timerfd_settime(), or since the last successful read(2), then the buffer given to read(2) returns 
an unsigned 8-byte integer (uint64_t) containing the number of expirations that have occurred.  
(The returned value is in host byte order, i.e., the native byte order for integers on the host machine.)

If no timer expirations have occurred at the time of the read(2), then the call either blocks until 
the next timer expiration, or fails with the error EAGAIN if the file descriptor has been made 
nonblocking (via the use of the fcntl(2) F_SETFL oper‐ation to set the O_NONBLOCK flag).

A read(2) will fail with the error EINVAL if the size of the supplied buffer is less than 8 bytes.

翻译过来是:当定时器超时,read读事件发生即可读,返回超时次数(从上次调用timerfd_settime()启动开始或上次read成功读取开始),它是一个8字节的unit64_t类型整数,如果定时器没有发生超时事件,则read将阻塞;若timerfd为阻塞模式,否则返回EAGAIN 错误(O_NONBLOCK模式);如果read时提供的缓冲区小于8字节将以EINVAL错误返回。

三、timefd程序实例

该实例在Linux系统下可使用man手册查看“man timerfd_create”,在man手册该条说明的最下方有示例代码以及测试结果。

#include <sys/timerfd.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>        /* Definition of uint64_t */

#define handle_error(msg) \
	   do { printf(msg); exit(EXIT_FAILURE); } while (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);
}


int main(int argc, char *argv[])
{
   struct itimerspec new_value;
   int max_exp, fd;
   struct timespec now;
   uint64_t exp, tot_exp;
   ssize_t s;

   if ((argc != 2) && (argc != 4)) 
   {
	   ssize_t s;
	   printf("%s %d\n",__FUNCTION__, __LINE__);
	   if ((argc != 2) && (argc != 4)) 
	   {
		   fprintf(stderr, "%s init-secs [interval-secs max-exp]\n", argv[0]);
		   handle_error("argc argv is error");
	   }
   }

   if (clock_gettime(CLOCK_REALTIME, &now) == -1)
   {
      handle_error("clock_gettime");
   }

   /* Create a CLOCK_REALTIME absolute timer with initial
	  expiration and interval as specified in command line */

   new_value.it_value.tv_sec = now.tv_sec + atoi(argv[1]);
   new_value.it_value.tv_nsec = now.tv_nsec;
   if (argc == 2) {
	   new_value.it_interval.tv_sec = 0;
	   max_exp = 1;
   } else {
	   new_value.it_interval.tv_sec = atoi(argv[2]);
	   max_exp = atoi(argv[3]);
   }
   new_value.it_interval.tv_nsec = 0;

   fd = timerfd_create(CLOCK_REALTIME, 0);
   if (fd == -1)
	   handle_error("timerfd_create");

   if (timerfd_settime(fd, TFD_TIMER_ABSTIME, &new_value, NULL) == -1)
	   handle_error("timerfd_settime");

   print_elapsed_time();
   printf("timer started\n");

   for (tot_exp = 0; tot_exp < max_exp;) {
	   s = read(fd, &exp, sizeof(uint64_t));
	   if (s != sizeof(uint64_t))
		   handle_error("read");

	   tot_exp += exp;
	   print_elapsed_time();
	   printf("read: %llu; total=%llu\n",
			   (unsigned long long) exp,
			   (unsigned long long) tot_exp);
   }

   exit(EXIT_SUCCESS);

}

四、实例测试

编译下上面程序为timerfd_app,后面带3个参数,第一个参数为5代表第一次定时器超时时间为5s,第二个参数为后续该周期定时器的超时时间,这里设置周期定时器为3s,第三个参数为本程序最多监控的超时次数(这个参数无所谓,只是为了打印显示),这里设置为100。

root@ubuntu:/home/book/Desktop# ./timerfd_app 5 3 100
0.000: timer started
5.000: read: 1; total=1
8.001: read: 1; total=2
11.001: read: 1; total=3
14.002: read: 1; total=4
^Z                                    #type control-Z to suspend the program
[1]+  Stopped                 ./timerfd_app 5 3 100
root@ubuntu:/home/book/Desktop# fg    # Resume execution after a few seconds
./timerfd_app 5 3 100
37.679: read: 7; total=11
38.001: read: 1; total=12
41.004: read: 1; total=13
44.001: read: 1; total=14
^C                                    # type control-C to suspend the program
root@ubuntu:/home/book/Desktop# 

应用程序设置了一个定时器首次超时时间5s,后续间隔3s的重复定时器。read返回值为超时次数,正常每次的超时次数都为1,但是当使用ctrl+z暂停应用23s后再重启恢复应用运行,read读取的超时次数为7,与实际相符合,程序中total值打印的是超时次数的总和。

我们可根据man手册的这个示例代码学习应用层定时器的相关使用,大家有兴趣也可以看下内核里面定时器的使用Linux内核定时器—init_timer()、add_timer()与mod_timer()使用

  • 1
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
timerfd_settime函数是一个Linux系统调用函数,用于创建并设置一个用于定时器的文件描述符。该函数需要传入timerfd文件描述符、设置定时器的参数(包括定时器到期的时间和定时器触发的时间间隔)以及flags参数。 使用该函数的步骤如下: 1. 创建一个timerfd文件描述符,可以使用系统调用函数timerfd_create来创建。 2. 定义一个struct itimerspec结构体,用于设置定时器的参数。该结构体包含两个成员:it_interval和it_value。it_interval表示定时器触发的时间间隔,it_value表示定时器到期的时间。 3. 调用timerfd_settime函数,将定时器参数设置到timerfd文件描述符中。 4. 读取timerfd文件描述符中的数据,当定时器到期时,该文件描述符会变为可读状态,可以使用read函数从中读取数据。 下面是一个简单的例子,演示如何使用timerfd_settime函数: ```c #include <sys/timerfd.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <stdint.h> int main() { int timer_fd; uint64_t exp; // 创建一个timerfd文件描述符 timer_fd = timerfd_create(CLOCK_MONOTONIC, 0); if (timer_fd == -1) { perror("timerfd_create"); exit(EXIT_FAILURE); } // 设置定时器参数 struct itimerspec timer_value; timer_value.it_interval.tv_sec = 1; // 定时器触发间隔1秒 timer_value.it_interval.tv_nsec = 0; timer_value.it_value.tv_sec = 5; // 定时器5秒后到期 timer_value.it_value.tv_nsec = 0; // 设置定时器参数到timerfd文件描述符中 if (timerfd_settime(timer_fd, 0, &timer_value, NULL) == -1) { perror("timerfd_settime"); exit(EXIT_FAILURE); } // 读取timerfd文件描述符中的数据 ssize_t s = read(timer_fd, &exp, sizeof(uint64_t)); if (s != sizeof(uint64_t)) { perror("read"); exit(EXIT_FAILURE); } printf("Timer expired\n"); close(timer_fd); exit(EXIT_SUCCESS); } ``` 在上述代码中,我们创建了一个定时器,设置为5秒后到期,每隔1秒触发一次。当定时器到期时,程序会打印一条消息,然后退出。 注意,在这个例子中,我们只读取了一次timerfd文件描述符中的数据,也就是说,程序只会等待定时器到期一次。如果需要等待多次定时器到期,需要在读取完数据后重新设置定时器参数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值