延迟和定时器管理

文章目录

  • 1 内核中时间概念
  • 2 标准定时器
    • jiffies和HZ
    • 定时器API
    • 标准定时器案例
  • 3 高精度定时器(HRT)
    • 高精度定时器案例
  • 4 内核中延迟和睡眠
    • 原子上下文
    • 非原子上下文

1 内核中时间概念

时间概念对计算机来说有些模糊,事实上内核必须在硬件的帮助下才能计算和管理时间。硬件为内核提供了一个系统定时器用于计算流失的时间。系统定时器以某种频率自发触发时钟中断,该频率可以通过编程预定,称为节拍率(系统定时器的频率)。

因为节拍率对内核来说是可知的,所以内核知道连续两次时钟中断的间隔时间,这个间隔时间就是节拍。内核就是靠这种已知的时钟中断间隔来计算实际时间(绝对时间)和系统运行时间。实际时间,就是某一天的日期和时间,内核通过控制时钟中断维护实际时间,另外内核也为用户空间提供了一组系统调用以获取实际日期和实际时间。系统运行时间:就是系统启动开始所经过的时间,对用户空间和内核都很用,因为许多程序都必须清楚流失的时间,通过两次读取运行时间再计算它们的差,就可以得到相对的流逝过的时间了。

系统定时器(内核定时器)是我们接下来讨论的内容。

2 标准定时器

标准定时器是内核定时器,以jiffies为粒度运行

jiffies和HZ

jiffies是在<linux/jiffies.h>中声明的内核时间单元。为了理解jiffies,需要引入一个新的常量HZ,注意,这是个常量,系统定时器频率(节拍率)是通过静态预处理定义的,也就是HZ(赫兹),在系统启动时按照HZ值对硬件进行设置。它是jiffies在1s内增加的次数,HZ的大小取决于硬件和内核版本,决定了时钟中断触发的频率。

全局变量jiffies用来记录自系统启动以来产生的节拍总数。启动时,内核将该变量初始化为0,此后,每次时钟中断处理程序都会增加该变量的值。因为一秒内时钟中断的次数等于HZ,所以jiffies一秒内增加的值也就为HZ。

jiffies变量总是无符号长整数(unsigned long),因此,在32位体系结构上是32位,在64位体系结构上是64位。32位的jiffies变量,可能会溢出,如果频率为1000Hz,49.7天后就会溢出。而使用64位的jiffies,不会看到它溢出。为了解决这个问题,<linux/jiffies.h>引入和定义了另一个变量:

extern u64 jiffies_64;

32位系统上采用这种方式时,jiffies取整个64位jiffies_64变量的低32位。jiffies_64将指向高位。在64位平台上,jiffies = jiffies_64

定时器API

定时器由结构timer_list表示,定义在文件linux/timer.h中:

struct timer_list {struct list_head entry;		/* 定时器链表的入口 */unsigned long expires;		/* 以jiffies为单位的定时值 */spinlock_t lock;			/* 保护定时器的锁 */unsigned long magic;		void (*function)(unsigned long);	/* 定时器处理函数 */unsigned long data;					/* 传给处理函数的长整型参数 */struct tvec_t_base_s *base;			/* 定时器内部值,用户不要使用 */
};

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

  1. 设置定时器
    设置定时器,可以用setup_timer函数:
void setup_timer(struct timer_list *timer,void (*function)(unsigned long),unsigned long data)

也可以使用init_time函数:

init_timer(struct timer_list *timer);

setup_timer是对init_timer的包装。

  1. 激活定时器
    函数add_timer( )根据参数struct timer_list变量的expires值将定时器插入到合适的动态定时器的链表中,并激活定时器。函数首先检测定时器是否处于挂起状态,如果挂起给出警告信息并退出,否则插入合适的定时器链表。
void add_timer(struct timer_list *timer)
  1. 设置过期时间
 int mod_timer(struct timer_list *timer, unsigned long expires);

函数mod_timer( )主要用于更改动态定时器的到期时间,从而可更改定时器的执行顺序,相当于执行如下代码序列:

del_timer(timer);
timer->expires=expires;
add_timer(timer);

mod_timer会修改过期时间并激活定时器

  1. 如果需要在定时器超时前停止定时器,可以使用del_timer()函数
int del_timer(struct timer_list * timer);
int del_timer_sync(struct timer_list * timer);

函数del_timer( )返回整数,可能的取值是0和1,对于活动定时器,返回1,对于不活动定时器返回0。del_timer_sync等待定时器处理函数执行完成函数。
当del_timer返回后,可以保证的只是:定时器不会再被激活,但是在多处理器机器上定时器可能已经在其他处理器上运行了,所以删除定时器是需要等待可能在其他处理器上运行的定时器处理程序都退出,这时就要使用del_timer_sync()函数执行删除工作。
应该在模块清理例程中释放定时器,可以单独检查定时器是否正在运行:

int timer_pending(struct timer_list *timer);

这个函数检查是否有触发的定时器回调函数挂起。

标准定时器案例

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/timer.h>static struct timer_list my_timer;void my_timer_callback(unsigned long data)
{printk("%s callled (%ld).\n",__FUNCTION__,jiffies );
}static int __init my_init(void)
{int retval;printk("Timer module loaded\n");setup_timer(&my_timer,my_timer_callback,0);printk("Setup timer to fire in 300ms (%ld)\n",jiffies);retval = mod_timer(&my_timer,jiffies+msecs_to_jiffies(300));if(ret){printk("Timer firing failed\n");}return 0;
}static void my_exit(void)
{int retval;retval = del_timer(&my_timer);if(retval){printk("The timer is still in use..\n");}pr_info("Tiner module unloaded.\n");
}module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");

3 高精度定时器(HRT)

标准定时器不够准确,其准确度为毫秒,内核2.6.16引入了高精度定时器,其精度达到微秒(取决于平台,最高可达纳秒)。标准定时器区取决于HZ(因为它们依赖于jiffies),而HRT实现基于ktime。

在系统上使用HRT时,要确认内核和硬件支持它。换句话说,必须用于平台相关的代码来访问硬件HRT。

需要头文件:

#include <linux/hrttimer.h>

在内核中HRT表示为hrttimer的实例:

*** struct hrtimer - the basic hrtimer structure* @node:	timerqueue node, which also manages node.expires,*		the absolute expiry time in the hrtimers internal*		representation. The time is related to the clock on*		which the timer is based. Is setup by adding*		slack to the _softexpires value. For non range timers*		identical to _softexpires.* @_softexpires: the absolute earliest expiry time of the hrtimer.*		The time which was given as expiry time when the timer*		was armed.* @function:	timer expiry callback function* @base:	pointer to the timer base (per cpu and per clock)* @state:	state information (See bit values above)* @is_rel:	Set if the timer was armed relative** The hrtimer structure must be initialized by hrtimer_init()*/
struct hrtimer {struct timerqueue_node		node;ktime_t				_softexpires;enum hrtimer_restart		(*function)(struct hrtimer *);struct hrtimer_clock_base	*base;u8				state;u8				is_rel;
};

HRT的初始化的步骤如下:

  1. 初始化hrttimer。hrttimer初始化之前,需要设置ktime,它代表持续时间。hrttimer_init初始化高精度定时
extern void hrtimer_init(struct hrtimer *timer, clockid_t which_clock,enum hrtimer_mode mode);
  1. 启动hrttimer,使用hrttimer_start函数:
/*** hrtimer_start - (re)start an hrtimer* @timer:	the timer to be added* @tim:	expiry time* @mode:	timer mode: absolute (HRTIMER_MODE_ABS) or*		relative (HRTIMER_MODE_REL), and pinned (HRTIMER_MODE_PINNED);*		softirq based mode is considered for debug purpose only!*/
static inline void hrtimer_start(struct hrtimer *timer, ktime_t tim,const enum hrtimer_mode mode)

mode代表到期模式,对于实际时间,它应该是HRTIMER_MODE_ABS,对于相对于现在的时间,是HRTIMER_MODE_REL

  1. 取消hrtimer。
extern int hrtimer_cancel(struct hrtimer *timer);
extern int hrtimer_try_to_cancel(struct hrtimer *timer);

如果定时器处于激活状态或者回调函数正在运行,hrtimer_try_to_cancel会失败,返回-1,hrtimer_cancel会等待回调函数完成。

为了防止定时器自动重启,hrtimer回调函数必须返回HRTIMER_NORESTART

高精度定时器案例

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>#define MS_TO_NS(x) (x * 1E6L)static struct hrtimer hr_timer;enum hrtimer_restart my_hrtimer_callback( struct hrtimer *timer )
{pr_info( "my_hrtimer_callback called (%ld).\n", jiffies );return HRTIMER_NORESTART;
}static int hrt_init_module( void )
{ktime_t ktime;unsigned long delay_in_ms = 200L;pr_info("HR Timer module installing\n");/** ktime = ktime_set(0, 200 * 1000 * 1000);* 200 ms = 10 * 1000 * 1000 ns*/ktime = ktime_set( 0, MS_TO_NS(delay_in_ms) );hrtimer_init( &hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL );hr_timer.function = &my_hrtimer_callback;pr_info( "Starting timer to fire in %ldms (%ld)\n", \delay_in_ms, jiffies );hrtimer_start( &hr_timer, ktime, HRTIMER_MODE_REL );return 0;
}static void hrt_cleanup_module( void )
{int ret;ret = hrtimer_cancel( &hr_timer );if (ret)pr_info("The timer was still in use...\n");pr_info("HR Timer module uninstalling\n");return;
}module_init(hrt_init_module);
module_exit(hrt_cleanup_module);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("John Madieu <john.madieu@gmail.com>");
MODULE_DESCRIPTION("Standard timer example");

4 内核中延迟和睡眠

延迟有两种类型,取决于代码运行的上下文:原子的或非原子的。处理内核延迟要包含的头文件是#include <linux/delay.h>

原子上下文

原子上下文中的任务(如ISR)不能进入睡眠状态,无法进行调度。这就是原子上下文延迟必须使用循环-等待循环的原因。内核提供Xdelay系列函数,在繁忙循环中消耗足够长的时间(基于jiffies),得到所需的延迟。

  • ndelay(unsigned long nsecs)
  • udelay(unsigned long usecs)
  • mdelay(unsigned long msecs)

应该时钟使用udelay(),因为ndelay()的精度取决于硬件定时器的精度。不建议使用mdelay()。

非原子上下文

在非原子上下文中,内核提供sleep系列函数,使用那个函数取决于需要延迟多长时间。

  • udelay(unsigned long usecs):基于繁忙-等待循环。如果需要睡眠数微秒(小于等于10us左右),则应该使用该函数。
  • usleep_range(unsigned long min,unsigned long max):依赖于hrttimer,睡眠数微秒到数毫秒(10us-20ms)时建议使用它,避免使用udelay()的繁忙-等待循环
  • msleep(unsigned long msecs):由jiffies传统定时器支持,对于数毫秒以上的长睡眠(10ms+)请使用该函数。

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

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

相关文章

Web开发工具(插件)收集

1.IE Developer Toolbar 浏览和修改&#xff0c;选定Web页上的特定元素&#xff0c;查看HTML对象的类名、ID&#xff0c;以及类似链接路径、tab顺序、快捷键等。 2.HttpWatch Professional 一款强大的网页数据分析工具,可以查看当前网页的http数据 FireFox插件 FireFox下插件实…

cin、cin.get()、cin.getline()、getline()、gets()等函数的用法

转载&#xff0c;并经过本人补充cin、cin.get()、cin.getline()、getline()、gets()等函数的用法2007/10/27 22:51学C的时候&#xff0c;这几个输入函数弄的有点迷糊&#xff1b;这里做个小结&#xff0c;为了自己复习&#xff0c;也希望对后来者能有所帮助&#xff0c;如果有差…

Java StringBuilder subSequence()方法与示例

StringBuilder类subSequence()方法 (StringBuilder Class subSequence() method) subSequence() method is available in java.lang package. subSequence()方法在java.lang包中可用。 subSequence() method is used to return the new set of a character sequence that is a …

Linux设备驱动开发---设备树的概念

文章目录1 设备树机制命名约定别名、标签和phandleDT编译器2 表示和寻址设备SPI和I2C寻址平台设备寻址3 处理资源提取特定应用数据文本字符串单元格和无符号的32位整数布尔提取并分析子节点4 平台驱动程序与DTOF匹配风格处理非设备树平台平台数据与DT设备树&#xff08;DT&…

【转】C#中数组复制的4种方法

C#中数组复制的4种方法 from&#xff1a;http://blog.csdn.net/burningcpu/article/details/1434167今天旁边的同事MM叫我调了一段程序&#xff0c;她想复制一个数组&#xff0c;int[] pins {9,3,4,9};int [] alias pins;这里出了错误&#xff0c;也是错误的根源&#xff0c…

Java StringBuilder codePointAt()方法与示例

StringBuilder类codePointAt()方法 (StringBuilder Class codePointAt() method) codePointAt() method is available in java.lang package. codePointAt()方法在java.lang包中可用。 codePointAt() method is used to return the Unicode code point at the given indices an…

用户虚拟地址转化成物理地址,物理地址转换成内核虚拟地址,内核虚拟地址转换成物理地址,虚拟地址和对应页的关系

文章目录1. 用户虚拟地址转换成物理地址2. 内核虚拟地址转换成物理地址3. 物理地址转换成内核虚拟地址4 内核虚拟地址和对应页5 根据进程号获取进程描述符1. 用户虚拟地址转换成物理地址 static void get_pgtable_macro(void) {printk("PAGE_OFFSET 0x%lx\n", PAGE…

简单三层架构(登录)

1&#xff0c;首先导包 dao //获取数据String username request.getParameter("username");String password request.getParameter("password");//传递到Service层UserService service new UserService();//这里的UserService 需要创建到service包下Use…

通过隐藏option实现select的联动效果

开始的时候需求是根据一定条件隐藏一部分<option>标签&#xff0c;类似联动效果&#xff0c;但是目前的html规范并没有为<option>提供隐藏的效果&#xff0c;因此常用的设置display或者visibility无效。网上大部分解决方案是删除<option>节点或<option>…

Java SimpleTimeZone setEndRule()方法与示例

SimpleTimeZone类setEndRule()方法 (SimpleTimeZone Class setEndRule() method) Syntax: 句法&#xff1a; public void setEndRule(int en_mm, int en_dd, int en_time);public void setEndRule(int en_mm, int en_dd, int en_dow, int en_time);public void setEndRule(int…

Linux设备驱动开发--- DMA

文章目录1 设置DMA映射缓存一致性和DMADMA映射一致映射流式DMA映射2 完成的概念3 DMA引擎API分配DMA从通道设置从设备和控制器指定参数获取事务描述符提交事务发布待处理DMA请求并等待回调通知4 程序单缓冲区映射分散聚集映射DMA是计算机系统的一项功能&#xff0c;它允许设备在…

类加载器

一、类加载器 1&#xff0c;什么是类加载器&#xff1f; 类加载器就是用来加载字节码文件 2&#xff0c;类加载器的种类有哪些&#xff1f; 1&#xff09;BootStrap&#xff1a;引导类加载器&#xff1a;加载都是最基础的文件 2&#xff09;ExtClassLoader&#xff1a;扩展类加…

一个用java读取XML文件的简单方法(转)

XML文件 book.xml <book> <person> <first>Kiran</first> <last>Pai</last> <age>22</age> </person> <person> <first>Bill</first> <last>Gates</last> <age>46</age&g…

Java ObjectStreamField getName()方法与示例

ObjectStreamField类的getName()方法 (ObjectStreamField Class getName() method) getName() method is available in java.io package. getName()方法在java.io包中可用。 getName() method is used to get the name of this ObjectStreamField field. getName()方法用于获取…

【css】CSS中折叠margin的问题

为什么要翻译这篇说明&#xff1f;css2本有人已翻译过&#xff0c;但看一下&#xff0c;很粗糙&#xff08;不是说自己就怎么怎么样啊&#xff0c;翻译者真的是很值得敬佩的&#xff01;&#xff09;&#xff0c;近来跟css与xhtml接触得越来越多&#xff0c;但接触得越多&#…

算法---链表

文章目录反转链表合并两个有序链表删除重复元素反转链表 反转链表包括两种&#xff0c;反转全部元素或者反转部分元素。在这里&#xff0c;我们约定&#xff1a;数据元素类型是struct LinkNode&#xff0c;要反转链表的第一个节点是head&#xff0c;head的前面一个节点是pre&a…

SSM

二、环境设置&#xff08;MyEclipse&#xff09; 1&#xff0c;字体设置 window–>Preference->General->Appearance->Colors and Fonts->Basic Text->Font 2&#xff0c;workspace字符集设置 window–>Preference->General->Appearance->W…

IOS NSArray,NSDictionary

小结&#xff1a; NSArray有序的集合&#xff1b; NSDictionary无序的集合&#xff0c;可排序&#xff1b; 增删改查 ------NSArray----------- create : 1)NSArray *array [NSArray arrayWithObjects:"Henry","Jones", "Susan", "Smith&q…

Java PropertyPermission equals()方法与示例

PropertyPermission类equals()方法 (PropertyPermission Class equals() method) equals() method is available in java.util package. equals()方法在java.util包中可用。 equals() method is used to check whether this object and the given object (ob) are equal or not…

c#配合oracle快速导入excel方法--原创(6万条记录5分钟左右)

原理&#xff1a;用c#采用读取Excel数据源方式将数据读入c#的datatable,循环datatable,将datatable中的数据用stringbuilder拼成insert into (字段名) valus (值);每5条插入一个符号&#xff08;作用是将sql字符串限制在4000字符以内&#xff09;&#xff0c;然后将拼成的字符串…