Linux中timerfd系列函数使用指南

timerfd_create, timerfd_settime, timerfd_gettime系列函数将定时器的实现与文件描述符绑定在一起,定时器超时的那一刻文件描述符变得可读,因此可以很好的与 select、poll 和 epoll 结合在一起使用。

timerfd_create 系统调用将创建一个定时器并与一个文件描述符进行关联。
timerfd_settime 启动或停止一个定时器。
timerfd_gettime 用于获取距下一个到期的定时器的时间信息。

#include <sys/timerfd.h>/*timerfd_create 调用成功,返回一个文件描述符;调用失败,返回-1, errno被设置为具体的错误码
*/
int timerfd_create(int clockid, int flags);/*timerfd_settime 和 timerfd_gettime 调用成功,返回0;调用失败,返回-1,errno被设置为具体的错误码
*/
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

clockid 参数用于指定定时器类型,必须指定为下列参数值中的一个。参数值的具体含义,这里不赘述,请使用man手册查看。

参数值描述
CLOCK_REALTIME
CLOCK_MONOTONIC
CLOCK_BOOTTIME (Since Linux 3.15)
CLOCK_REALTIME_ALARM (since Linux 3.11)
CLOCK_BOOTTIME_ALARM (since Linux 3.11)

clockid 常用的参数值为 CLOCK_REALTIMECLOCK_MONOTONIC
CLOCK_REALTIME 表示的测量真实时间(即挂钟 wall-clock)的全系统时钟。
CLOCK_MONOTONIC 表示定时器使用的时钟是一个单调递增的时钟,不受系统时间的调整影响 。(man clock_gettime)

flags 参数可以为 TFD_NONBLOCKTFD_CLOEXEC 的或运算。

timerfd_settime

fd 参数表示 timerfd_create 创建返回的文件描述符。

new_value 参数用于指定定时器的过期时间,old_value 如果不为空,用于获取调用 timerfd_settime 时的计数器设置,具体见下面 timerfd_gettime 的描述。

new_valueold_value 使用结构体 itimerspec 表示,该结构体类型如下所示:

struct timespec {time_t tv_sec;                /* Seconds */long   tv_nsec;               /* Nanoseconds */
};struct itimerspec {struct timespec it_interval;  /* 定时器的周期间隔 */struct timespec it_value;     /* 初始过期时间 */
};

对于参数 new_valuenew_value.it_value 设置了定时器的初始过期时间,若new_value.it_value 的两个字段都被设置为0,则表示暂停该定时器;new_value.it_interval 表示定时器的时间间隔,若new_value.it_interval 的两个字段参数都为0,则表示该定时器只会过期一次,否则,定时器每重复 new_value.it_interval 的时间间隔过期一次。

默认情况下(flags 设置为0),定时器使用相对时间计时,即从调用 timerfd_settime 设置超时时间的那一时刻起 new_value.it_value 指定的时间间隔后,定时器会超时。

需要注意,可以多次调用 timerfd_settime,若上次调用 timerfd_settime 启动的定时器还为超时,则这次调用的 timerfd_settime 会覆盖掉上次还没超时的定时器,即上次设置的定时器会失效。

下面看两个例子:

示例一:设置一个定时器,死等定时器超时(通过read进行阻塞读)

#include <iostream>
#include <sys/timerfd.h>
#include <cstring>
#include <unistd.h>int main()
{struct timespec curr_time;bzero(&curr_time, sizeof(curr_time));// timerfd 为阻塞的int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);// 获取调用 timerfd_create 时的时间clock_gettime(CLOCK_MONOTONIC, &curr_time);std::cout << "timerfd_create time: " << "sec-" << curr_time.tv_sec << " nano-" << curr_time.tv_nsec << std::endl;sleep(3);struct itimerspec new_val;bzero(&new_val, sizeof(new_val));new_val.it_value.tv_sec = 5;    // 5s后定时器超时timerfd_settime(timerfd, 0, &new_val, nullptr);// 获取调用 timerfd_settime 时的时间bzero(&curr_time, sizeof(curr_time));clock_gettime(CLOCK_MONOTONIC, &curr_time);std::cout << "timerfd_settime time: " << "sec-" << curr_time.tv_sec << " nano-" << curr_time.tv_nsec << std::endl;uint64_t howmany;// 阻塞读ssize_t n = read(timerfd, &howmany, sizeof(howmany));// 获取定时器超时时刻bzero(&curr_time, sizeof(curr_time));clock_gettime(CLOCK_MONOTONIC, &curr_time);std::cout << "timerfd timeout time: " << "sec-" << curr_time.tv_sec << " nano-" << curr_time.tv_nsec << std::endl;
}/*
运行结果:
timerfd_create time: sec-101418 nano-833894291
timerfd_settime time: sec-101421 nano-834455725
timerfd timeout time: sec-101426 nano-834540240
*/

示例二:设置一个周期定时器

// 省略相关头文件int main()
{struct itimerspec expire_time;bzero(&expire_time, sizeof(expire_time));int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);int epollfd = epoll_create1(EPOLL_CLOEXEC);struct epoll_event events[5];bzero(events, sizeof(events));struct epoll_event timer_event;bzero(&timer_event, sizeof(timer_event));timer_event.events = EPOLLIN;timer_event.data.fd = timerfd;expire_time.it_value.tv_sec = 5;// 5s 的定时周期expire_time.it_interval.tv_sec = 5;if (timerfd_settime(timerfd, 0, &expire_time, nullptr) < 0) {error_message("timerfd_create error");}epoll_ctl(epollfd, EPOLL_CTL_ADD, timerfd, &timer_event);struct timespec curr_time;while (true) {int num = epoll_wait(epollfd, events, 5, -1);bzero(&curr_time, sizeof(curr_time));clock_gettime(CLOCK_MONOTONIC, &curr_time);std::cout << "curr time: sec-" << curr_time.tv_sec << ", nano-" << curr_time.tv_nsec << std::endl;for (int i = 0; i < num; ++i) {int fd = events[i].data.fd;if (fd == timerfd) {std::cout << "timer timeout once." << std::endl;uint64_t howmany;ssize_t n = read(timerfd, &howmany, sizeof(howmany));std::cout << "howmany: " << howmany << std::endl;}}}
}/*
运行输出:
curr time: sec-103737, nano-656002232
timer timeout once.
howmany: 1
curr time: sec-103742, nano-655951279
timer timeout once.
howmany: 1
curr time: sec-103747, nano-655957241
timer timeout once.
howmany: 1
curr time: sec-103752, nano-655909670
timer timeout once.
howmany: 1
...
*/

若要使用绝对时间设置超时时间,可以设置第二个参数 flags

flags 参数可以由下列值进行或运算设置,若使用默认行为,则设置为0。

  • TFD_TIMER_ABSTIME 若被设置,则当 new_value.it_value 所指定的时刻到达时,定时器超时。
  • TFD_TIMER_CANCEL_ON_SET 若被设置。If this flag is specified along with TFD_TIMER_ABSTIME and the clock for this timer is CLOCK_REALTIME or CLOCK_REALTIME_ALARM, then mark this timer as cancelable if the real-time clock undergoes a discontinuous change (settimeofday(2), clock_settime(2), or similar). When such changes occur, a current or future read(2) from the file descriptor will fail with the error ECANCELED.

下面给出一个使用绝对时间设置定时器超时的例子:

int main()
{int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);struct itimerspec new_val;struct timespec curr_time;bzero(&new_val, sizeof(new_val));bzero(&curr_time, sizeof(curr_time));clock_gettime(CLOCK_MONOTONIC, &curr_time);std::cout << "curr_time: " << curr_time.tv_sec << " seconds, " << curr_time.tv_nsec << "nanoseconds" << std::endl;// 使用绝对时间设置定时器的超时时间new_val.it_value.tv_sec = curr_time.tv_sec + 5;timerfd_settime(timerfd, TFD_TIMER_ABSTIME, &new_val, nullptr);uint64_t howmany;ssize_t n = read(timerfd, &howmany, sizeof(howmany));bzero(&curr_time, sizeof(curr_time));clock_gettime(CLOCK_MONOTONIC, &curr_time);std::cout << "curr_time: " << curr_time.tv_sec << " seconds, " << curr_time.tv_nsec << "nanoseconds" << std::endl;close(timerfd);
}

timerfd_gettime

timerfd_gettime 用于获取 fd 所指定的定时器的时间信息,具体地,将定时器的到期时刻距离调用timerfd_gettime 时刻之间的时间间隔存储到 curr_value.it_value 中,而 curr_value.it_interval 保存了下一个定时事件的定时周期。下面看一个例子:

int main()
{int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);struct itimerspec new_val, old_val;bzero(&new_val, sizeof(new_val));bzero(&old_val, sizeof(old_val));clock_gettime(CLOCK_MONOTONIC, &old_val.it_value);// 调用 timerfd_settiem 时的时间std::cout << "sec: " << old_val.it_value.tv_sec << ", nano: " << old_val.it_value.tv_nsec << std::endl;bzero(&old_val, sizeof(old_val));new_val.it_value.tv_sec = 10;timerfd_settime(timerfd, 0, &new_val, &old_val);// 因为 timerfd_settime 在此处是第一次调用,因此 old_val 的值为0std::cout << "sec: " << old_val.it_value.tv_sec << ", nano: " << old_val.it_value.tv_nsec << std::endl;sleep(3);bzero(&old_val, sizeof(old_val));clock_gettime(CLOCK_MONOTONIC, &old_val.it_value);// sleep 3s 后,获取当前时刻的时间std::cout << "sec: " << old_val.it_value.tv_sec << ", nano: " << old_val.it_value.tv_nsec << std::endl;bzero(&old_val, sizeof(old_val));timerfd_settime(timerfd, 0, &new_val, &old_val);// 第二次调用 timerfd_settime,// 因为上一次调用 timerfd_settime 设置的定时器还未超时,因此上次的定时器失效// 距离上次设置的定时器超时还剩的时间间隔会被设置到 old_val 中std::cout << "sec: " << old_val.it_value.tv_sec << ", nano: " << old_val.it_value.tv_nsec << std::endl;uint64_t howmany;// 阻塞读ssize_t n = read(timerfd, &howmany, sizeof(howmany));// 定时器超时时刻bzero(&old_val, sizeof(old_val));clock_gettime(CLOCK_MONOTONIC, &old_val.it_value);std::cout << "sec: " << old_val.it_value.tv_sec << ", nano: " << old_val.it_value.tv_nsec << std::endl;
}// 观察输出结果:
/*
sec: 105111, nano: 956075955
sec: 0, nano: 0
sec: 105114, nano: 956573191
sec: 6, nano: 999455855
sec: 105124, nano: 956734201
*/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/641223.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OSPF协议LSDB同步过程和邻居状态机

知识改变命运&#xff0c;技术就是要分享&#xff0c;有问题随时联系&#xff0c;免费答疑&#xff0c;欢迎联系&#xff01; 厦门微思网络​​​​​​ https://www.xmws.cn 华为认证\华为HCIA-Datacom\华为HCIP-Datacom\华为HCIE-Datacom Linux\RHCE\RHCE 9.0\RHCA\ Oracle O…

技术型企业如何选择安全、性价比高的FTP替代方案?

FTP作为世界上第一款文件传输协议&#xff0c;在全球范围内应用广泛&#xff0c;它解决了文件传输协议空白的问题&#xff0c;为文件传输场景提供了专业的解决方案。 但随着网络技术的演进&#xff0c;技术型企业进行文件传输的需求也更多元和复杂&#xff0c;FTP的缺陷也更多的…

Flutter中的AppLifecycleListener:应用生命周期监听器介绍及使用

引言 当你在Flutter中需要监听应用程序的生命周期变化时&#xff0c;可以使用AppLifecycleListener。在Flutter 3.13中&#xff0c;AppLifecycleListener被添加到Framework中&#xff0c;用于监听应用程序的生命周期变化&#xff0c;并响应退出应用程序的请求等支持。 在Flut…

CmakeList教程

一、CmakeList介绍&#xff1a; cmake 是一个跨平台、开源的构建系统。它是一个集软件构建、测试、打包于一身的软件。它使用与平台和编译器独立的配置文件来对软件编译过程进行控制。它会通过写的语句自动生成一个MakeFile,从而实现高效编译 二、CmakeList的常用指令 1.指定…

【 CSS 】定位

不要因为小小的失败而放弃大大的梦想&#xff0c;每一次坚持都是通向成功的一步。- 马克吐温 1. 定位 1.1 为何使用定位 我们先来看一个效果&#xff0c;同时思考一下用标准流或浮动能否实现类似的效果&#xff1f; 场景1: 某个元素可以自由的在一个盒子内移动位置&#xff0c…

#Uniapp:uni.request(OBJECT)

uni.request(OBJECT) 发起网络请求。 示例 uni.request({url: https://www.example.com/request, //仅为示例&#xff0c;并非真实接口地址。data: {text: uni.request},header: {custom-header: hello //自定义请求头信息},success: (res) > {console.log(res.data);thi…

微服务基础概念、架构图、划分图

基础概念 1、微服务 微服务架构风格&#xff0c;就像是把一个单独的应用程序&#xff0c;就像是把一个单独的应用程序开发为一套小服务&#xff0c;每个小服务运行在自己的进程中&#xff0c;并使用轻量级机制通信&#xff0c;通常是http api。这些服务围绕业务能力来构建。并…

Java 日期处理

主要从以下三方面讲解&#xff1a; java.util 包提供了 Date 类来封装当前的日期和时间。java.util 包提供了 Calendar 类用来设置和获取日期数据的特定部分。java.text 包提供了 SimpleDateFormat 类来格式化日期的格式。 Date类 Date类的构造函数 Date 类主要提供了两个构…

小程序学习-21

目前小程序分包大小有以下限制&#xff1a; 整个小程序所有分包大小不超过 20M单个分包/主包大小不能超过 2M 独立分包&#xff1a;"independent": true

docker compose安装milvus

下载对应版本的milvus-standalone-docker-compose.yml wget https://github.com/milvus-io/milvus/releases/download/v2.3.5/milvus-standalone-docker-compose.yml重新命令为docker-compose.yml mv milvus-standalone-docker-compose.yml docker-compose.yml启动milvus doc…

记一次 stackoverflowerror 线上排查过程

一.线上 stackOverFlowError xxx日,突然收到线上日志关键字频繁告警 classCastException.从字面上的报警来看,仅仅是类型转换异常,查看细则发现其实是 stackOverFlowError.很多同学面试的时候总会被问到有没有遇到过线上stackOverFlowError?有么有遇到栈溢出?具体栈溢出怎么来…

postman测试导入文件

01 上传文件参数 1.选择请求方式 选择post请求方式&#xff0c;输入请求地址 2.填写Headers Key&#xff1a;Content-Type &#xff1b; Value&#xff1a;multipart/form-data 如下图 3.填写body 选择form-data&#xff0c;key选择file类型后value会出现按钮&#xff0…

(十二)Head first design patterns代理模式(c++)

代理模式 代理模式&#xff1a;创建一个proxy对象&#xff0c;并为这个对象提供替身或者占位符以对这个对象进行控制。 典型例子&#xff1a;智能指针... 例子&#xff1a;比如说有一个talk接口&#xff0c;所有的people需要实现talk接口。但有些人有唱歌技能。不能在talk接…

表单的总数据为什么可以写成一个空对象,不用具体的写表单中绑定的值,vue3

<el-form :model"form" label-width"120px"><el-form-item label"Activity name"><el-input v-model"form.name" /></el-form-item> </el-form> const form ref({})from为空对象 在v-model里写form…

verde生成网格坐标

文章目录 网格坐标区域调整 Verde是Python用于地理空间数据处理的一个库&#xff0c;由于采用了一些机器学习的方法&#xff0c;所以除了科学计算三件套之外&#xff0c;还需要基于sklearn模块。考虑到依赖关系&#xff0c;这里比较推荐用conda安装。 conda install verde --c…

分布式websocket即时通信(IM)系统保证消息可靠性【第八期】

b站上面本期视频版本&#xff0c;观看视频食用更佳&#xff01;点击即可跳转,找不到视频可以直接搜索我 目前叫 呆呆呆呆梦 目前已经写的文章有。并且有对应视频版本。 git项目地址 【IM即时通信系统&#xff08;企聊聊&#xff09;】点击可跳转 sprinboot单体项目升级成sprin…

Windows 10中的驱动程序与device guard的兼容性

文章目录 Windows 10中的驱动程序与device guard的兼容性windows的device guard是什么如何构建兼容的驱动程序如何验证驱动程序的兼容性驱动程序验证程序兼容性检查启用基于虚拟化的隔离代码完整性HLK测试&#xff08;桌面和服务器&#xff09;Device Guard准备工具DGReadiness…

VsCode容器开发 - VsCode连接远程服务器上的docker

VsCode容器开发 - VsCode连接远程服务器上的docker 前言 之前在服务器上的Docker内开发&#xff0c;文件编辑起来就很不爽。不如使用VsCode直接打开远程服务器上的Docker&#xff0c;这样就能在VsCode里直接无缝编辑Docker里的文件了。 但是百度和必应得到的中文结果都很奇葩…

openssl3.2/test/certs - 013 - primary server-EKU root: sroot-cert

文章目录 openssl3.2/test/certs - 013 - primary server-EKU root: sroot-cert概述笔记END openssl3.2/test/certs - 013 - primary server-EKU root: sroot-cert 概述 openssl3.2 - 官方demo学习 - test - certs 笔记 // \file my_openssl_linux_log_doc_013.txt // \not…

Java 实现二叉排序树(BST)

文章目录 介绍实现先定义一个节点树测试 总结 介绍 二叉排序树&#xff08;Binary Search Tree&#xff0c;简称BST&#xff09;是一种特殊的二叉树&#xff0c;其中每个节点的值都大于其左子树的任意节点值&#xff0c;而小于其右子树的任意节点值。 它具有以下特点&#xf…