Linux内核定时器timer_list的使用-程序员宅基地

技术标签: Linux  Linux内核编程  

Linux内核定时器timer_list的使用

内核定时器:

定时器(优势也称为动态地上那个时期或内核定时器)是管理内核流逝时间放的基础。内核经常需要推后执行某些代码,如中断的下半部机制就是为了将工作放到 以后执行。

定时器的使用很简单,你只需要执行一些初始化工作,设置一个超时时间,指定超时发生后的执行函数,然后激活定时器就可以了。指定的函数将在定时器到期时自动执行,注意定时器并周期运行,它在超时后自动撤销,不断地创建和撤销,而且它的执行次数不受限制,故被称为动态定时器。

使用注意:

Timer是Linux内核的一种软中断,被调用函数是异步执行的。
由于是软中断,所以函数被执行的时候是处于非进程的上下文中,所以有以下规则需要遵守:

  1. 不允许访问用户空间
  2. 不能执行会引起休眠的函数和调度(如mutex)
  3. 注意并发数据保护
  4. 系统最高精度为(1000/HZ)ms,若HZ=1000,则最高精度为1ms
  5. 内核添加printk后,由于打印函数耗时会导致执行时间延长,一般为ms级。

调度函数总是在注册它的同一CPU中运行,即便是在SMP(对称多处理器)系统中。

定时器的数据结构:定义在文件<linux/timer.h>中

struct timer_list {
struct list_head  entry;
unsigned long     expires;
void              (*function)(unsigned long);
unsigned long     data;
struct tvec_base  *base;
...
};
  • 字段entry:定时器链表的入口,用来将多个定时器来结成一跳双向循环链表。
  • 字段expires:给出定时器到期时间,时间用节拍来表示,其值为系统启动以来所经过的节拍数。
  • 字段function:定时器到期时执行函数的地址。
  • 字段data:传递给定时器函数的参数。
  • 字段base:定时器内补值,用户不要使用,用来指定此定时器在哪个CPU上执行。

相关函数:

init_timer(struct timer_list*):定时器初始化函数;
add_timer(struct timer_list*):往系统添加定时器;
mod_timer(struct timer_list *, unsigned long jiffier_timerout):修改定时器的超时时间为jiffies_timerout;
d. timer_pending(struct timer_list *):定时器状态查询,如果在系统的定时器列表中则返回1,否则返回0;
del_timer(struct timer_list*):删除定时器。

使用步骤:

init_timer->[timer.expires=? & timer.function=?]->add_timer->[mod_timer]->del_timer.

具体例子:

//timer.c  每1ms打印一次hello world
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
struct timer_list mytimer;
static void myfunc(unsigned long data)
{
	printk("------%s------\n", (char *)data);
	mod_timer(&mytimer,jiffies + HZ/1000);
}
static int __init mytimer_init(void)
{
	//setup_timer(&mytimer, myfunc, (unsigned long)"Hello,world!");
	init_timer(&mytimer);                     //初始化内核定时器
	mytimer.function = myfunc;            //指定定时时间到后的回调函数
    mytimer.data = (unsigned long)"hello world";  //回调函数的参数
    mytimer.expires = jiffies+HZ/1000;        //定时时间 1ms 
	add_timer(&mytimer);   
	return 0;
}
static void __exit mytimer_exit(void)
{
	del_timer(&mytimer);
}
module_init(mytimer_init);
module_exit(mytimer_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("KUNAlY");
MODULE_DESCRIPTION("Demo for timer");

Makefile文件:

obj-m:=timer.o
PWD:=$(shell pwd)
KERNELPATH:=/lib/modules/$(shell uname -r)/build
all:
	make -C $(KERNELPATH) M=$(PWD) modules
clean:
	make -C $(KERNELPATH) M=$(PWD) clean

执行步骤:

即可看到执行效果,在内核每1ms打印一次hello world,timer_list的最大精度为1ms,下面进行检测误差。

 

检测误差:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/timer.h>

static struct timer_list tm;  //定义一个定时器
struct timeval oldtv;         //获取系统时间,用来计时

void myfunction(unsigned long arg)
{
    struct timeval tv;
    char *data = (char*)arg;
    
    printk("%s: %lu, %s\n", __func__, jiffies, data); 

    do_gettimeofday(&tv);    //获取新的当前时间
    printk("%s:%ldms\n", __func__,
        tv.tv_usec/1000- oldtv.tv_usec/1000);    //与上次中断间隔 ms=us/1000 

    //do_gettimeofday(&oldtv);   //获取当前时间
	oldtv = tv;
	tm.expires = jiffies+HZ/1000;    //定时时间1ms
    add_timer(&tm);        //重新开始计时
}

static int __init demo_init(void)
{
    printk(KERN_INFO "%s : start!...\n",__func__);
    init_timer(&tm);    //初始化内核定时器
    do_gettimeofday(&oldtv);        //获取当前时间
    tm.function = myfunction;            //指定定时时间到后的回调函数
    tm.data = (unsigned long)"hello world";        //回调函数的参数
    tm.expires = jiffies+HZ/1000;        //定时时间 1ms
    add_timer(&tm);        //将定时器加入内核

    return 0;
}

static void __exit demo_exit(void)
{
    printk(KERN_INFO "%s: exit!...\n",__func__);  
    del_timer(&tm);        //注销定时器
}

module_init(demo_init);
module_exit(demo_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kunaly");
MODULE_DESCRIPTION("Demo for timer_test");

执行步骤:

执行结果(每隔几次就会有误差,不能每次精确到1ms):

 

注意:

本运行环境为:centos7,内核版本为linux-kernel-3.10.0-957.el7.x86_64

新版本的内核4.x, 5.x ,不断更新中,运行会出现一定的偏差,个别函数找不到了,而网上大多都是基于2.6版本的API,导致我再用Ubuntu时使用的5.2版本的内核总是出现一些错误,相关函数需要重新替换。

因需求是精度到1ms的定时器,所以还得再看看内核中hrtimer的使用。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_33406883/article/details/99633062

智能推荐

spring-cloud-hystrix之spring-cloud-1.3.4(Dalston.SR3)-boot(1.5.6) 学习笔记_springcloud dalston.sr3怎么引用-程序员宅基地

文章浏览阅读341次。spring-cloud-hystrix之spring-cloud-1.3.4(Dalston.SR3)-boot(1.5.6) 学习笔记概述hystrix可单独使用,也可与feign搭配使用,详见HystrixTargeter参考 https://fangjian0423.github.io/2017/02/19/springcloud-hystrix/http://blog.csdn.n_springcloud dalston.sr3怎么引用

ubuntu下将本地vscode编辑器通过ssh和远程服务器进行连接并使用远程的Python环境_ubuntu 通过 ssh 连接到远程服务器方便编辑代码的工具-程序员宅基地

文章浏览阅读991次。前言:在程序员的日常开发工作中,会遇到这样的情况,我们需要用自己本地的工作笔记本连接到大的服务器进行开发,因为大服务器的硬件性能强悍,有很多软件环境。我们在自己的笔记本进行开发需要用到这些软硬件环境,尤其在深度学习项目开发过程中,需要非常好的显卡,而显卡资源一般都部署在大服务器上。下面讲具体步骤:ssh和vscode的安装不再赘述,网上有很多教程。1,打开vscode编辑器,进入"扩展:商店":在搜索框里搜索Remote-SSH,下载好这个插件。2,点击左下角绿色部分"打开远程窗口",出现下图内容_ubuntu 通过 ssh 连接到远程服务器方便编辑代码的工具

GPU加速在前端的应用-程序员宅基地

文章浏览阅读3.2k次,点赞4次,收藏13次。概述GPU(Graphics Processing Unit) 图形处理单元,又称图形处理器,是我们所周知的显卡的核心部件,是显卡的“心脏”。按照字面意..._前台应用加速核心

C#串口通信从入门到精通(11)——串口接收的数据存储到本地文件(txt、csv)_读取并保存串口发送来的数据-程序员宅基地

文章浏览阅读4.3k次,点赞5次,收藏18次。本文详细介绍如何将串口通信将数据进行本次存储,主要介绍常用的csv、txt格式文件,由多年实战经验总结,绝对干货!_读取并保存串口发送来的数据

synchronized锁住的是什么?_如果为put方法加synchronized锁,锁住的是什么?是一个put还是所有put方法?-程序员宅基地

文章浏览阅读229次。对象锁:同步方法锁住的是对象A.一个对象+有多个synchronized方法:某一时刻内,只要有一个线程去调用其中一个synchronized 方法,其他的线程都要等待。B.一个对象中+synchronized方法+非synchronized方法:某个线程访问同步方法不影响其他线程访问非同步方法C.多个对象+多个同步方法:访问不同的对象的同步方法不会相互影响全局锁:静态同步方法锁的是当前的..._如果为put方法加synchronized锁,锁住的是什么?是一个put还是所有put方法?

outlook邮箱邮件内容乱码_邮件标题乱码问题解决一例-程序员宅基地

文章浏览阅读2.5k次。最近有用户反馈在mac下通过airmail、outlook这样的邮件客户端查看系统发出邮件的标题显示为乱码,但是windows下正常。通过邮件客户端保存为eml,用sublime text 2打开,可以发现展示为以下效果:Date: Wed, 01 Jul 2015 19:54:57 +0800From: To: "=?GBK?B?zt6+zA==?=" Subject: [MTLÑз¢Ö§³Å..._outlook 2013 转发邮件 乱码

随便推点

python自定义复杂排序的使用(含案例)_python 自定义复合排序-程序员宅基地

文章浏览阅读619次。python自定义复杂排序的使用(含案例)_python 自定义复合排序

Vue组件-程序员宅基地

文章浏览阅读2.8k次,点赞86次,收藏44次。在这一小节中,重点要理解的就是组件的编程思想。组件表示页面中的部分功能(包含自己的逻辑与样式),可以组合多个组件实现完整的页面功能。问题是,如何确定页面中哪些内容划分到一个组件中呢?你可以将组件当作一种函数或者是对象来考虑(函数的功能是单一的),根据[单一功能原则]来判定组件的范围。也就是说,一个组件原则上只能负责一个功能。如果它需要负责更多的功能,这时候就应该考虑将它拆分成更小的组件。定义:组件是可复用的Vue实例,准确讲它是的实例,继承自Vue分类:有状态组件(有data属性),

Springer期刊 latex投稿经验分享(包含latex模板)_投期刊要求latex-程序员宅基地

文章浏览阅读7.1k次。Springer期刊 latex投稿经验分享_投期刊要求latex

PLM助力国防军工制造业工艺数字化-程序员宅基地

文章浏览阅读192次。1制造业数字化工艺目标1) 通过PLM项目的实施能够理顺企...

攻防_漏洞_文件包含_文件包含漏洞详解_ctf 文件包含-程序员宅基地

文章浏览阅读3.7k次,点赞5次,收藏17次。文件包含漏洞详解_ctf 文件包含

javaweb-java占位符的使用_%x java-程序员宅基地

文章浏览阅读970次。文章目录java占位符具体使用搭配转换符的标志java占位符拼接字符串显示十分麻烦,因此java中存在大量占位符用来规范输出诸如:String.format("0.5(-%d)+", i),类似c语言的printf方法,比“0.5(-”+…更加简洁易懂String类的format()方法具体使用format(String format, Object… args) 新字符串使用本地语言环境,制定字符串格式和参数生成格式化的新字符串。format(Locale locale, String fo_%x java

推荐文章

热门文章

相关标签