Linux应用编程:定时器

目录

    • 定时任务sleep
    • 定时器setitimer
      • setitimer
      • sample
    • POSIX Timer
      • 创建定时器timer_create
      • 启动定时器timer_settime
      • 剩余时间timer_gettime
      • 定时器超限timer_getoverrun
      • 删除定时器timer_delete
      • sample1:信号方式
      • sample2:线程形式

定时任务sleep

嵌入式Linux应用编程中,通常会需要定时完成一些任务,最平常可见的方法就是使用sleep函数。

while(condition)
{// timed_tasksleep(20); // 每隔20s就执行一次任务timed_task
}

但是sleep()函数是一个阻塞式函数,会在指定的时间内暂停整个线程。此时就会浪费cpu执行。此时希望程序做一些其他事情。于是就可以使用定时器。

定时器setitimer

setitimer

#include <sys/time.h> // 头文件
int getitimer(int which, struct itimerval *curr_value);
int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value);
struct itimerval 
{struct timeval it_interval; /*next value*/ struct timeval it_value;   /*current value*/  
};
struct timeval 
{time_t      tv_sec;         /* seconds */suseconds_t tv_usec;        /* microseconds 1/1000000 seconds */
};
  • which:
    ITIMER_REAL:以系统真实的时间来计算,它送出SIGALRM信号。
    ITIMER_VIRTUAL:以该进程在用户态下花费的时间来计算,它送出SIGVTALRM信号。
    ITIMER_PROF:以该进程在用户态下和内核态下所费的时间来计算,它送出SIGPROF信号

  • new_value:
    参数用来对计时器进行设置,it_interval为计时间隔,it_value为延时时长
    在setitimer方法调用成功后,延时it_value后触发一次SIGALRM信号,
    以后每隔it_interval触发一次SIGALRM信号。

settimer工作机制是,先对it_value倒计时,当it_value为零时触发信号,然后重置为it_interval,继续对it_value倒计时,一直这样循环下去。基于此机制,setitimer既可以用来延时执行,也可定时执行。假如it_value为0是不会触发信号的,所以要能触发信号,it_value得大于0;如果it_interval为零,只会延时,不会定时(也就是说只会触发一次信号)。

  • old_value

参数,通常用不上,设置为NULL,它是用来存储上一次setitimer调用时设置的new_value值。

sample

每隔2s执行一次timer_handler函数

#include <stdio.h>
#include <sys/time.h>
#include <string.h>
#include <signal.h>
#include <time.h>void timer_handler(int signum) {// 执行定时器到期时需要做的操作// 注意:定时器处理函数应该尽量保持简短,避免执行耗时操作printf("timed\n");
}void set_timer(int seconds) {struct itimerval timer;timer.it_value.tv_sec = seconds;  // 第一次定时器到期的秒数timer.it_value.tv_usec = 0;       // 第一次定时器到期的微秒数// timer.it_interval = timer.it_value;timer.it_interval.tv_sec = seconds;  // 定时器周期的秒数(如果为0,则只执行一次)timer.it_interval.tv_usec = 0;       // 定时器周期的微秒数// 设置定时器信号处理函数// signal(SIGALRM, timer_handler);struct sigaction act;act.sa_handler = timer_handler;act.sa_flags = 0;sigemptyset(&act.sa_mask); sigaction(SIGALRM,&act,NULL); //设置信号 SIGALRM 的处理函数为 timer_handler// 启动定时器setitimer(ITIMER_REAL, &timer, NULL);
}/*停止setitimer定时器*/
void delete_setitimer() 
{struct itimerval value; value.it_value.tv_sec = 0; value.it_value.tv_usec = 0; value.it_interval = value.it_value; setitimer(ITIMER_REAL, &value, NULL); 
}int main()
{set_timer(2);while(1);return 0;
}

但是这个定时器有一个缺点,就是一个进程只能使用一个定时器,假如应用需要同时维护多个 Interval 不同的计时器,必须自己写代码来维护,而且该定时器的精度是ms。如果需要多个定时器和更高精度的定时,可以使用POSIX Timer

POSIX Timer

  • 几个函数
// 创建一个定时器
int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
// 启动一个定时器
int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspect *ovalue);
// 获取定时器剩余时间
int timer_gettime(timer_t timerid,struct itimerspec *value);
// 取得一个定时器的超限运行次数
int timer_getoverrun(timer_t timerid);
// 删除定时器
int timer_delete (timer_t timerid);
  • 和setitimer比较

1、setitimer一个进程同一时刻只能有一个 timer。若需要同时维护多个 Interval 不同的计时器,必须自己写代码来维护。 POSIX Timer,一个进程可以创建任意多个 Timer。
2、setitmer 计时器时间到达时,只能使用信号方式通知使用 timer 的进程,而 POSIX timer 多种通知方式,比如信号,或者启动线程。
3、使用 setitimer 时,通知信号的类别不能改变:SIGALARM,SIGPROF 等,而这些都是传统信号,而不是实时信号,因此有 timer overrun 的问题;而 POSIX Timer 则可以使用实时信号。
4、 setimer 的精度是 ms,POSIX Timer 支持 ns 级别的时钟精度。

创建定时器timer_create

int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid)

该定时器属于进程级别的资源,在fork的时候,子进程不能继承该定时器。

clock_id:说明定时器是基于哪个时钟的。

CLOCK_REALTIME :Systemwide realtime clock.
CLOCK_MONOTONIC:Represents monotonic time. Cannot be set.
CLOCK_PROCESS_CPUTIME_ID :High resolution per-process timer.
CLOCK_THREAD_CPUTIME_ID :Thread-specific timer.
CLOCK_REALTIME_HR :High resolution version of CLOCK_REALTIME.
CLOCK_MONOTONIC_HR :High resolution version of CLOCK_MONOTONIC.

evp:指定了定时器到期要产生的异步通知。

struct sigevent
{int sigev_notify; //notification type 定时器到时间采取的行动int sigev_signo; //signal number 信号码union sigval   sigev_value; //signal valuevoid (*sigev_notify_function)(union sigval);pthread_attr_t *sigev_notify_attributes;
}union sigval
{int sival_int; //integer valuevoid *sival_ptr; //pointer value
}

如果evp为NULL,那么定时器到期会产生默认的信号,对 CLOCK_REALTIMER来说,默认信号就是SIGALRM。

evp->sigev_signo:设置定时器产生后发出的信号类型。

如果要产生除默认信号之外的其它信号,程序必须将evp->sigev_signo设置为期望的信号码

evp->sigev_notify:说明了定时器到期时应该采取的行动

SIGEV_NONE:什么都不做,只提供通过timer_gettime和timer_getoverrun查询超时信息。
SIGEV_SIGNAL: 当定时器到期,内核会将sigev_signo所指定的信号传送给进程。在信号处理程序中,si_value会被设定会sigev_value。
SIGEV_THREAD: 当定时器到期,内核会(在此进程内)以sigev_notification_attributes为线程属性创建一个线程,并且让它执行sigev_notify_function,传入sigev_value作为为一个参数。

evp->sigev_value:来区分是哪个定时器引起的信号或者线程。

如果几个定时器产生了同一个信号,处理程序可以用 evp->sigev_value来区分是哪个定时器产生了信号。要实现这种功能,程序必须在为信号安装处理程序时,使用struct sigaction的成员sa_flags中的标志符SA_SIGINFO。

timerid:创建的定时器id。

启动定时器timer_settime

int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspect *ovalue);struct itimespec{struct timespec it_interval; struct timespec it_value;   
}; 

timer_create()所创建的定时器并未启动。要将它关联到一个到期时间以及启动时钟周期,可以使用timer_settime()

flag:如果flags的值为TIMER_ABSTIME,则value所指定的时间值会被解读成绝对值(此值的默认的解读方式为相对于当前的时间)。

工作机制和setitimer类似,如果ovalue的值不是NULL,则之前的定时器到期时间会被存入其所提供的itimerspec。如果定时器之前处在未启动状态,则此结构的成员全都会被设定成0。

剩余时间timer_gettime

//获得一个活动定时器的剩余时间
int timer_gettime(timer_t timerid,struct itimerspec *value);

定时器超限timer_getoverrun

//取得一个定时器的超限运行次数
int timer_getoverrun(timer_t timerid);

有可能一个定时器到期了,而同一定时器上一次到期时产生的信号还处于挂起状态。在这种情况下,其中的一个信号可能会丢失。这就是定时器超限。程序可以通过调 用timer_getoverrun来确定一个特定的定时器出现这种超限的次数。定时器超限只能发生在同一个定时器产生的信号上。由多个定时器,甚至是那 些使用相同的时钟和信号的定时器,所产生的信号都会排队而不会丢失。

执行成功时,timer_getoverrun()会返回定时器初次到期与通知进程(例如通过信号)定时器已到期之间额外发生的定时器到期次数。举例来说,一个1ms的定时器运行了10ms,则此调用会返回9。如果超限运行的次数等于或大于DELAYTIMER_MAX,则此调用会返回DELAYTIMER_MAX

执行失败时,此函数会返回-1并将errno设定会EINVAL,这个唯一的错误情况代表timerid指定了无效的定时器。

删除定时器timer_delete

int timer_delete (timer_t timerid);

执行成功时:销毁timerid的定时器,并返回0;

执行失败时:此调用会返回-1并将errno设定会 EINVAL,这个唯一的错误情况代表timerid不是一个有效的定时器。

sample1:信号方式

编译:gcc timer_sample1.c -o timer_sample1 -lrt

rt库:librt 库

#include <stdio.h>
#include <sys/time.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#include <time.h> /*定时器到期产生信号的handle*/
void handle(int signum)
{time_t t;char p[32];time(&t);strftime(p, sizeof(p), "%T", localtime(&t));printf("time is %s\n", p);
}int main()
{struct sigevent evp; // 创建定时器的参数struct itimerspec ts; // 设置定时器时间timer_t timer; // 定时器idint ret;evp.sigev_value.sival_ptr = &timer; // 传递定时器idevp.sigev_notify = SIGEV_SIGNAL; // 设置为信号触发方式evp.sigev_signo = SIGUSR1; // 定时器到时产生SIGUSR1信号signal(SIGUSR1, handle); // 绑定信号handleret = timer_create(CLOCK_REALTIME, &evp, &timer); // 创建信号if( ret )perror("timer_create");ts.it_interval.tv_sec = 1;ts.it_interval.tv_nsec = 0; // 间隔1sts.it_value.tv_sec = 3;ts.it_value.tv_nsec = 0; // 第一次定时器为3s// ret = timer_settime(timer, TIMER_ABSTIME, &ts, NULL);ret = timer_settime(timer, 0, &ts, NULL);if( ret )perror("timer_settime");while(1);
}

sample2:线程形式

#include <stdio.h>
#include <sys/time.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#include <time.h> void* handle(void* sigval_v)
{union sigval v = (sigval)sigval_v;time_t t;char p[32];time(&t);strftime(p, sizeof(p), "%T", localtime(&t));printf("%s thread %lu, val = %d, signal captured.\n", p, pthread_self(), v.sival_int);return;
}int main()
{struct sigevent evp;struct itimerspec ts; // timer_t timer; // 定时器idint ret;memset(&evp, 0, sizeof(evp));  // 必须要有这个,不然会出现段错误evp.sigev_value.sival_ptr = &timer; // 传递定时器id// evp.sigev_notify = SIGEV_SIGNAL; // 设置为信号触发方式evp.sigev_notify = SIGEV_THREAD; // 设置为线程模式evp.sigev_notify_function = handle; // 设置线程的函数evp.sigev_value.sival_int = 3;   //和sival_ptr作为handle()的参数ret = timer_create(CLOCK_REALTIME, &evp, &timer); // 创建信号if( ret )perror("timer_create");ts.it_interval.tv_sec = 1;ts.it_interval.tv_nsec = 0; // 间隔1sts.it_value.tv_sec = 3;ts.it_value.tv_nsec = 0; // 第一次定时器为3sret = timer_settime(timer, TIMER_ABSTIME, &ts, NULL);// ret = timer_settime(timer, 0, &ts, NULL);if( ret )perror("timer_settime");while(1);
}/**** 执行结果
05:41:04 thread 2230478592, val = 3, signal captured.
05:41:05 thread 2230478592, val = 3, signal captured.
05:41:05 thread 2230478592, val = 3, signal captured
****/

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

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

相关文章

Wargames与bash知识16

Wargames与bash知识16 Bandit24 关卡提示&#xff1a; 一个守护进程正在端口30002上侦听&#xff0c;如果给定bandit24的密码和一个4位数的密码&#xff0c;它将为您提供bandit25的密码。没有办法检索pincode&#xff0c;除非遍历所有10000个组合&#xff0c;称为暴力强制。您…

用React给XXL-JOB开发一个新皮肤(三):实现登录页和Layout骨架

目录 一. 简述二. 接口服务调整 2.1. 登录接口2.2. 登出接口2.3. 修改密码接口2.4. 修改配置文件 三. 前端HTTP 请求四. 登录页面 4.1. 搭建登录页面4.2. 对接登录接口 五. Layout 骨架 5.1. 搭建骨架5.2. Header5.3. 修改密码5.4. 退出登录 六. 其他 一. 简述 上一篇文章我…

视频监控平台的管理员账号在所有客户端都无法登录的问题解决

目 录 一、问题描述 二、问题排查 1、看问题提示 2、看日志信息 3、问题定位 三、问题解决 1. 添加权限角色 2、添加操作用户 3、验证 一、问题描述 AS-V1000视频监控平台安装部署完成后&#xff0c;发现管理员admin不能到web客户端&#xff0c;觉…

D25XB100-ASEMI家用电器整流桥D25XB100

编辑&#xff1a;ll D25XB100-ASEMI家用电器整流桥D25XB100 型号&#xff1a;D25XB100 品牌&#xff1a;ASEMI 封装&#xff1a;GBJ-5&#xff08;带康铜丝&#xff09; 平均正向整流电流&#xff08;Id&#xff09;&#xff1a;25A 最大反向击穿电压&#xff08;VRM&…

AcWing 103. 电影(map、pair连用or离散化)

题目 方法一&#xff08;mappair&#xff09; 其实上面这么长巴拉巴拉就是在说 首先&#xff0c;每个科学家会的语言都不同。但是呢每部电影的字幕和语言是不一样的&#xff08;字幕和语言一定不相同&#xff09; 要求找到一部电影使得在场能听懂的科学家最多&#xff08;如果存…

Linux Kernel Stack Overflow/Linux 内核栈溢出

不同于Linux应用程序的栈能够动态增长&#xff0c;Linux内核栈是固定的&#xff0c;并且比较小&#xff0c;比如Linux 2.6.x内核&#xff0c;在X86 32位架构上一般是4K或8K&#xff08;在进行内核编译时&#xff0c;Kernel hacking下进行配置&#xff0c;默认8K&#xff09;&am…

上门回收小程序开发,让回收更加简单

资源回收一直是当下深受大众关注的话题&#xff0c;如何做到资源不浪费&#xff0c;成为了大众要考虑的问题。在人们环保意识的加深下&#xff0c;回收行业也是获得了大众的关注&#xff0c;逐渐形成了一个新的商业模式。 随着互联网技术的发展&#xff0c;回收行业也更加方便…

C/S架构,集成三维影像后处理功能,自主版权的一套医院PACS系统源码

一、PACS简介 PACS&#xff08;PictureArchivingandCommunicationsSystem&#xff09;即图像存储与传输系统&#xff0c;是应用于医院的数字医疗设备如CT、MR&#xff08;磁共振&#xff09;、US&#xff08;超声成像&#xff09;、X光机、DSA&#xff08;数字减影&#xff09…

KIBANA可视化管理界面说明

更说明转自https://blog.csdn.net/IT_ZRS/article/details/125496588 1 主要结构功能 使用浏览器访问 ip:5601 默认端口&#xff0c;进入首页 Discover&#xff1a;日志管理视图 主要进行搜索和查询Visualize&#xff1a;统计视图 构建可视化的图表Dashboard&#xf…

【目标检测】YOLOv5算法实现(八):模型验证

本系列文章记录本人硕士阶段YOLO系列目标检测算法自学及其代码实现的过程。其中算法具体实现借鉴于ultralytics YOLO源码Github&#xff0c;删减了源码中部分内容&#xff0c;满足个人科研需求。   本系列文章主要以YOLOv5为例完成算法的实现&#xff0c;后续修改、增加相关模…

JavaWeb,CSS的学习

CSS&#xff0c;层叠样式表&#xff08;Cascading Style Sheets&#xff09;&#xff0c;能够对网页中元素位置的排版进行像素级精确控制&#xff0c;支持几乎所有的字体字号样式&#xff0c;拥有网页对象和模型样式编辑的能力&#xff0c;简单来说&#xff0c;美化页面。 CSS…

c++临时对象的探讨及相关性能提升

产生临时对象的情况 我们定义一个类进行测试 class tempVal { public:int v1, v2;tempVal(int v1 0, int v2 0);tempVal(const tempVal& t) :v1(t.v1), v2(t.v2) {cout << "调用拷贝构造函数" << endl;}virtual ~tempVal() {cout << "…

【python】——turtle动态画

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

AR HUD全面「上新」

AR HUD赛道正在迎来新的时代。 上周&#xff0c;蔚来ET9正式发布亮相&#xff0c;新车定位为D级行政旗舰轿车&#xff0c;其中&#xff0c;在智能座舱交互层面&#xff0c;继理想L系列、长安深蓝S7之后&#xff0c;也首次取消仪表盘&#xff0c;取而代之的是业内首个全焦段AR H…

分块矩阵的定义、计算

目录 一、定义 二、分块矩阵的加减乘法 三、考点 一、定义 分块&#xff0c;顾名思义&#xff0c;将整个矩阵分成几部分&#xff0c;如下图所示 二、分块矩阵的加减乘法 三、考点 分块矩阵的考点不多&#xff0c;一般来说&#xff0c;有一种&#xff1a; 求分块矩阵的转置…

PHP如何拆分中文名字(包括少数民族名字)

/*** param string|null $name* return array|null*/ function splitName($name) {if (empty($name) || empty(trim($name))) {return null;}//该正则是用来提取$name参数里面的中文字符的。preg_match_all(/[\x{4e00}-\x{9fff}]/u, $name, $matchers);$matchersCount isset($…

2024年,谷歌云首席技术官眼中的生成AI三大支柱,来看看有啥新花样

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

App在线封装的革命性创新

随着移动互联网的蓬勃发展&#xff0c;App已经成为我们日常生活中不可或缺的一部分。从购物、交通、社交到娱乐&#xff0c;几乎每个人的智能手机都装载着数十个应用程序&#xff0c;以满足各式各样的需求。然而&#xff0c;对于许多非技术背景的企业家和小型企业而言&#xff…

java---final以及抽象类

final - 修饰变量&#xff1a;变量不能被改变 //演示final修饰变量class Aoo{final int num 5;void show(){//num 55; //编译错误&#xff0c;final的变量不能被改变}} - 修饰方法&#xff1a;方法不能被重写 //演示final修饰方法class Boo{final void show(){}}class Coo ex…

Spring整理-Spring Bean的作用域

在Spring框架中,Bean的作用域定义了Bean实例的生命周期和可见性。Spring提供了多种作用域选项,适用于不同的应用需求。 Spring中的主要Bean作用域 Singleton:默认的作用域。在Spring IoC容器中,对于每个Spring Bean配置,只创建一个实例。适用于无状态的服务,如配置、工具…