以下内容为本人的著作,如需要转载,请声明原文链接微信公众号「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 定时器介绍的更多相关文章

  1. linux下定时器介绍1

    POSIX Timer 间隔定时器 setitimer 有一些重要的缺点,POSIX Timer 对 setitimer 进行了增强,克服了 setitimer 的诸多问题: 首先,一个进程同一时刻只 ...

  2. Linux 定时器应用【转】

    Linux 定时器应用 实验目的 阅读 Linux 相关源代码,学习 Linux 系统中的时钟和定时器原理,即,ITIMER_REAL实时计数,ITIMER_VIRTUAL 统计进程在用户模式执行的时 ...

  3. 01 Linux入门介绍

    一.Linux 初步介绍 Linux的优点 免费的,开源的 支持多线程,多用户 安全性好 对内存和文件管理优越 系统稳定 消耗资源少 Linux的缺点 操作相对困难 一些专业软件以及游戏支持度不足 L ...

  4. linux定时器用法

    linux定时器  原文出自http://www.cnblogs.com/processakai/archive/2012/04/11/2442294.html 今天看书看到了关于alarm的一些用法 ...

  5. Linux Epoll介绍和程序实例

    Linux Epoll介绍和程序实例 1. Epoll是何方神圣? Epoll但是当前在Linux下开发大规模并发网络程序的热门人选,Epoll 在Linux2.6内核中正式引入,和select类似, ...

  6. Linux入门介绍

    Linux入门介绍 一.Linux 初步介绍 Linux的优点 免费的,开源的 支持多线程,多用户 安全性好 对内存和文件管理优越 系统稳定 消耗资源少 Linux的缺点 操作相对困难 一些专业软件以 ...

  7. linux定时器crontab

    linux定时器crontab用法: 1.基本格式 : * * * * * command 分 时 日 月 周 命令 第1列表示分钟1-59 每分钟用*或者 */1表示 第2列表示小时1-23(0表示 ...

  8. Linux 系统目录介绍

    bin : bin 是Binary 二进制的缩写,就是可执行文件了.Bin目录下是用户常用的命令. sbin: 此目录下也是二进制文件 ,不过这里的命令是 超级用户如 root 这样的用户使用的. e ...

  9. Linux命令介绍

    资料链接:(Linux基本命令介绍)http://note.youdao.com/share/?id=36c07917f8d3e6437c1e764c3516a3f2&type=note#/ ...

随机推荐

  1. Excel表函数自动生成SQL

    前言 在平常的工作中,多多掌握一点这样的小技巧,能够帮助我们省去很多时间: 1.数据库对应的表如下: 2.excel中需要导入的数据如下: 3.excel中sql的写法: ="insert ...

  2. 『现学现忘』Docker基础 — 42、补充:save和load命令说明

    目录 1.save命令 2.load命令 1.save命令 将指定的一个或多个镜像保存成.tar格式的文件,进行打包归档. 查看docker save帮助命令,如下: [root@192 ~]# do ...

  3. Git下载(快速快速快速下载!!)

    在安装Git环境的时候,需要下载Git的安装包,但是官网的下载网速实在是太慢的(几十M的安装包,下载速度只有几十K) (所以可以在镜像中下载,速度超快) Git镜像下载链接-------------- ...

  4. Tapdata Cloud 版本上新 | 支持通知自配置,支持GP、MQ数据源,界面更友好!

    Tapdata Cloud https://cloud.tapdata.net Tapdata Cloud 是国内首家异构数据库实时同步云平台,目前支持Oracle.MySQL.PG.SQL Serv ...

  5. MC34063降压电路

    MC34063芯片由温度自动补偿功能的基准电压发生器.比较器.占空比可控振荡器. 触发器和大电流输出开关电路等组成,具有功能齐全.价格低廉.体积小.效率高.仅需少量外部元器件等优点,其主要特性如表所示 ...

  6. 选择结构-扩展if-else语句和练习用if语句实现考试成绩划分

    判断语句3--if..else if...else if语句第三种格式: if...else if ...else if (判断条件1) { 执行语句1; } else if (判断条件2) { 执行 ...

  7. 发现Mapstruct的一个bug

    前言 在一次需求的开发过程中,发现一个对象插入数据库时某个字段总是为空.简化后的代码如下: @Autowired private PersonService personService; public ...

  8. C++多文件源程序

    一.多文件结构的源代码组织 一个C++程序开发工程(project)可以包含多个源程序文件,一个源程序文件(.cpp)可以包含多个函数.一个函数只能集中放在一个源程序文件中,不能将其定义代码拆开存放在 ...

  9. 一文解析Pinia和Vuex,带你全面理解这两个Vue状态管理模式

    Pinia和Vuex一样都是是vue的全局状态管理器.其实Pinia就是Vuex5,只不过为了尊重原作者的贡献就沿用了这个看起来很甜的名字Pinia. 本文将通过Vue3的形式对两者的不同实现方式进行 ...

  10. 聊聊 Redis 是如何进行请求处理

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com/archives/674 本文使用的Redis 5.0源码 感觉这部分的代码还是挺有意思 ...