Linux 定时器介绍
以下内容为本人的著作,如需要转载,请声明原文链接微信公众号「englyf」https://www.cnblogs.com/englyf/p/16651865.html
曾经常去沙县小吃,就为了蹭上一碗4块钱的葱油拌面,听着边上的几位小哥老说
华仔,有软硬之分。
其实写代码也有这种讲究。
在linux系统中定时器有分为软定时和硬件定时器,硬件定时器一般指的是CPU的一种底层寄存器,它负责按照固定时间频率产生中断信号,形成信号源。基于硬件提供的信号源,系统就可以按照信号中断来计数,计数在固定频率下对应固定的时间,根据预设的时间参数即可产生定时中断信号,这就是软定时。
这里主要讲软定时器,而硬件定时器涉及到硬件手册这里略过。
1. 利用内核节拍器相关定时器实现定时
linux内核有可调节的系统节拍,由于节拍依据硬件定时器的定时中断计数得来,节拍频率设定后,节拍周期恒定,根据节拍数可以推得精确时间。从系统启动以来记录的节拍数存放在全局变量jiffies中,系统启动时自动设置jiffies为0。
#include <linux/jiffies.h>
高节拍数可以计算更高的时间精度,但是会频繁触发系统中断,牺牲系统效率。
定义定时器
struct timer_list {
struct list_head entry; // 定时器链表的入口
unsigned long expires; // 定时器超时节拍数
struct tvec_base *base; // 定时器内部值,用户不要使用
void (*function)(unsigned long); // 定时处理函数
unsigned long data; // 要传递给定时处理函数的参数
int slack;
};
设置节拍数expires时,可以使用函数msecs_to_jiffies将毫秒值转化为节拍数。
初始化定时器
void init_timer(struct timer_list *timer);
注册定时器到内核,并启动
void add_timer(struct timer_list *timer);
删除定时器
int del_timer(struct timer_list *timer);
如果程序运行在多核处理器上,此函数有可能导致运行出错,建议改用del_timer_sync。
同步删除定时器
int del_timer_sync(struct timer_list *timer);
如果程序运行在多处理器上,此函数会等待其它处理器对此定时器的操作完成。另外,此函数不能用在中断上下文中。
修改定时值并启动定时器
int mod_timer(struct timer_list *timer, unsigned long expires);
注意:在应用层开发过程中,一般不会使用内核的函数来设定定时器。
2. 应用层的alarm闹钟
在应用层开发时,设置闹钟参数,并启动闹钟定时器非常方便
#include<unistd.h>
unsigned int alarm(unsigned int seconds);
注意:每个进程只允许设置一个闹钟,重复设置会覆盖前一个闹钟。
当时间到达seconds秒后,会有SIGALRM信号发送给当前进程,可以通过函数signal注册该信号的回调处理函数callback_fun
#include <signal.h>
typedef void (*sig_t)(int);
sig_t signal(int signum, sig_t handler);
3. 利用POSIX中内置的定时器接口
设定闹钟适用的情形比较简单,而为了更灵活地使用定时功能,可以用到POSIX中的定时器功能。
创建定时器
#include <time.h>
int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
通过clock_id可以指定时钟源,evp传入超时通知配置参数,timerid返回被创建的定时器的id。evp如果为NULL,超时触发时,默认发送信号SIGALRM通知进程。
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.
结构体sigevent
union sigval
{
int sival_int; //integer value
void *sival_ptr; //pointer value
}
struct sigevent
{
int sigev_notify; //notification type
int sigev_signo; //signal number
union sigval sigev_value; //signal value
void (*sigev_notify_function)(union sigval);
pthread_attr_t *sigev_notify_attributes;
}
类型timer_t
#ifndef _TIMER_T
#define _TIMER_T
typedef int timer_t; /* timer identifier type */
#endif /* ifndef _TIMER_T */
设置定时器,比如初次触发时间,循环触发的周期等。设置完成后启动定时器。
int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspec *ovalue);
struct timespec{
time_t tv_sec;
long tv_nsec;
};
struct itimerspec {
struct timespec it_interval;
struct timespec it_value;
};
获取定时剩余时间
int timer_gettime(timer_t timerid, struct itimerspec *value);
获取定时器超限的次数
int timer_getoverrun(timer_t timerid);
定时器超时后发送的同一个信号如果挂起未处理,那么在下次超时发生后,上一个信号会丢失,这就是定时器的超限。
删除定时器
int timer_delete (timer_t timerid);
示例:超时触发信号
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
void sig_handler(int signo)
{
time_t t;
char str[32];
time(&t);
strftime(str, sizeof(str), "%T", localtime(&t));
printf("handler %s::%d\n", str, signo);
}
int main()
{
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = sig_handler;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
if (sigaction(SIGUSR1, &act, NULL) == -1) {
perror("fail to sigaction");
exit(-1);
}
timer_t timerid;
struct sigevent evp;
memset(&evp, 0, sizeof(evp));
// 定时器超时触发信号 SIGUSR1
evp.sigev_notify = SIGEV_SIGNAL;
evp.sigev_signo = SIGUSR1;
if (timer_create(CLOCK_REALTIME, &evp, &timerid) == -1) {
perror("fail to timer_create");
exit(-1);
}
// 设置初始触发时间4秒,之后每2秒再次触发
struct itimerspec its;
its.it_value.tv_sec = 4;
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 2;
its.it_interval.tv_nsec = 0;
if (timer_settime(timerid, 0, &its, 0) == -1) {
perror("fail to timer_settime");
exit(-1);
}
while(1);
return 0;
}
上面的代码中注册信号响应回调用了函数sigaction,其实这里用函数signal也可以的。
示例:超时启动子线程
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
void timer_thread(union sigval v)
{
time_t t;
char str[32];
time(&t);
strftime(str, sizeof(str), "%T", localtime(&t));
printf("timer_thread %s::%d\n", str, v.sival_int);
}
int main()
{
timer_t timerid;
struct sigevent evp;
memset(&evp, 0, sizeof(evp));
evp.sigev_notify = SIGEV_THREAD;
evp.sigev_value.sival_int = 123;
evp.sigev_notify_function = timer_thread;
if (timer_create(CLOCK_REALTIME, &evp, &timerid) == -1) {
perror("fail to timer_create");
exit(-1);
}
struct itimerspec its;
its.it_value.tv_sec = 4;
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 2;
its.it_interval.tv_nsec = 0;
if (timer_settime(timerid, 0, &its, 0) == -1) {
perror("fail to timer_settime");
exit(-1);
}
while(1);
return 0;
}
Linux 定时器介绍的更多相关文章
- linux下定时器介绍1
POSIX Timer 间隔定时器 setitimer 有一些重要的缺点,POSIX Timer 对 setitimer 进行了增强,克服了 setitimer 的诸多问题: 首先,一个进程同一时刻只 ...
- Linux 定时器应用【转】
Linux 定时器应用 实验目的 阅读 Linux 相关源代码,学习 Linux 系统中的时钟和定时器原理,即,ITIMER_REAL实时计数,ITIMER_VIRTUAL 统计进程在用户模式执行的时 ...
- 01 Linux入门介绍
一.Linux 初步介绍 Linux的优点 免费的,开源的 支持多线程,多用户 安全性好 对内存和文件管理优越 系统稳定 消耗资源少 Linux的缺点 操作相对困难 一些专业软件以及游戏支持度不足 L ...
- linux定时器用法
linux定时器 原文出自http://www.cnblogs.com/processakai/archive/2012/04/11/2442294.html 今天看书看到了关于alarm的一些用法 ...
- Linux Epoll介绍和程序实例
Linux Epoll介绍和程序实例 1. Epoll是何方神圣? Epoll但是当前在Linux下开发大规模并发网络程序的热门人选,Epoll 在Linux2.6内核中正式引入,和select类似, ...
- Linux入门介绍
Linux入门介绍 一.Linux 初步介绍 Linux的优点 免费的,开源的 支持多线程,多用户 安全性好 对内存和文件管理优越 系统稳定 消耗资源少 Linux的缺点 操作相对困难 一些专业软件以 ...
- linux定时器crontab
linux定时器crontab用法: 1.基本格式 : * * * * * command 分 时 日 月 周 命令 第1列表示分钟1-59 每分钟用*或者 */1表示 第2列表示小时1-23(0表示 ...
- Linux 系统目录介绍
bin : bin 是Binary 二进制的缩写,就是可执行文件了.Bin目录下是用户常用的命令. sbin: 此目录下也是二进制文件 ,不过这里的命令是 超级用户如 root 这样的用户使用的. e ...
- Linux命令介绍
资料链接:(Linux基本命令介绍)http://note.youdao.com/share/?id=36c07917f8d3e6437c1e764c3516a3f2&type=note#/ ...
随机推荐
- SAP FPM 相关包 APB_FPM_CORE
related interface: APB_FPM_COREAPB_FPM_CORE_4_EXT_SCAPB_FPM_CORE_INTERNALAPB_FPM_CORE_RESTRICTED
- linux系统漏洞lynis扫描
1.下载lynis https://cisofy.com/downloads/lynis/ 或者 yum --enablerepo=epel -y install lynis 2. rz上传压缩包解压 ...
- rxjava回调地狱-kotlin协程来帮忙
本文探讨的是在tomcat服务端接口编程中, 异步servlet场景下( 参考我另外一个文章),用rxjava来改造接口为全流程异步方式 好处不用说 tomcat的worker线程利用率大幅提高,接口 ...
- 如何用Fiddler对APP进行网络测试
什么是Fiddler Fiddler是一个http协议调试代理工具,它能够记录并检查所有你的电脑和互联网之间的http通讯,设置断点,查看所有的"进出"Fiddler的数据(指co ...
- 事务@Transactional注解的属性
事务的传播行为 当事务方法被另一个事务方法调用时,必须指定事务应该如何传播.例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行.事务的传播行为可以由传播属性指定.Sprin ...
- 如何用车辆历史违章查询API接口进行快速开发
最近公司项目有一个车辆的历史违章查询显示的小功能,想着如果用现成的API就可以大大提高开发效率,所以在网上的API商店搜索了一番,发现了 APISpace,它里面的车辆历史违章查询API非常符合我的开 ...
- Unity-2D像素晶格化消融
效果展示: ShaderLab Shader功能:图像变白+根据顶点的y值作透明裁剪: 才是可操作属性: IsDead: 控制像素变白,片元着色阶段IsDead小于0将颜色改为白色: Percent: ...
- 云表平台VS永中office,谁更胜一筹?
20年前,永中软件在国外办公软件独霸江山以及盗版软件肆意横行的夹缝中呱呱坠地. 经过永中人日日夜夜的不懈奋斗,永中Office V1.0面世. 永中科技 从创立之初永中软件便预见了独立创新.自主可控的 ...
- linux系统中Nginx+FFmPeg+vlc实现网页视频播放配置过程
linux系统中Nginx+FFmPeg实现网页监控视频播放配置过程 1.安装好的nginx上添加模块nginx-http-fiv-module-master 此模块是rtmp模块的升级版,有它所有的 ...
- Rails_via牛客网
题目 链接:https://ac.nowcoder.com/acm/contest/28537/D 来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32768K,其他语言 ...