5-3 Linux内核计时、延时函数与内核定时器【转】

转自:http://www.xuebuyuan.com/510594.html

5-3 Linux内核计时、延时函数与内核定时器 

计时

1、 
内核时钟

1.1   
内核通过定时器(timer)中断来跟踪时间流

1.2   
硬件定时器以周期性的间隔产生时间中断,这个间隔(即频率)由内核根据HZ来确定,HZ是一个与体系结构无关的常数。

1.3   
这个时间间隔通常取1ms到10ms.

2、 
jiffies计算器

2.1每次当定时器中断发生时,内核内部通过一个64位的变量jiffies_64做加一计数。

2.2驱动程序开发者通常访问的是jiffies变量,它是jiffes_64的低32位。

2.3jiffies是unsigned
long型的变量,该变量被声明为volatile,这样可避免编译器对访问该变量的语句的优化。

2.4jiffies记录了自最近一次Linux启动后到当前的时间间隔(即时钟中断发生的次数)。驱动程序常利用jiffies来计算不同事件间的时间间隔。

3、HZ

3.1 HZ表每秒中产生的定时中断次数

3.2 HZ就代表1秒,HZ/2就代表0.5秒

4、使用jiffies计数器

  
#include<linux/jiffies.h>

  
Unsigned long  current_i,  stamp_1, 
stamp_half,  stamp_n;

  
Current_i = jiffies;  //读取当前的值

  
Stamp_1 = current_i+HZ;  //未来的一秒

  
Stamp_half = current_i +HZ/2 ; //未来的半秒

  
Stamp_n = current_i + n*HZ/1000; //未来的n毫秒

5、为了防止jiffies溢出导致问题,最好使用宏比较

  
#include<linux/jiffies.h>

  
Int time_after(unsigned long a, unsigned long b);//判断a代表的时间是否在b之后。

  
Int time_before(unsigned long a,unsigned long b);

  
Int time_after_eq(unsigned long a ,unsigned long b);

  
Int time_before_eq(unsigned long a,unsigned long b);

6、

(1)用户空间的timeval

struct timeval{

 
time_t tv_sev;//秒、

 
suseconds_t tv_usec;//毫秒

}

(2)用户空间的timespec

 
struct timespec{

    
time_t tv_sec;//秒

    
long  tv_nsec;//纳秒

  
}

7、内核空间jiffies和用户空间的timeval、timevspec的转换。

 
#include<linux/time.h>

  
unsigned long timespec_to_jiffies(struct timespec* value);

void jiffies_to_timespec(unsigned long jiffie,struct timespec* value);

unsigned long timeval_to_jiffies(struct timeval* value);

void jiffies_to_timeval(unsigned long jiffies, struct 
timeval* value);

8、获取当前时间

  
#include<linux/time.h>
   void do_gettimeofday(struct timeval*  
tv);

  
struct timespec current_kernel_time(void);

9、使用jiffies延时(如果对延时的精度要求不是很高时,用忙等待)

  
unsigned long j = jiffies + delay*HZ

  
while(jiffies<j){  //jiffies表当前的滴答值,是不停地走的。
  
/*do nothing*/

  
}

10、长延迟。

while(time_before(jiffies,end_time)){

  
Schedule();//在end_time之前,则调度

}

 

#include<linux/sched.h>
    signed long schedule_timeout(signed long timeout);//使用jiffies表示的延迟。先做超时,

典型应用:

 set_current_state(TASK_INTERRUPTIBLE);//设置状态值,设置为可中断的睡眠

 schedule_timeout(delay);//延时

11、短延时(忙等待延时,不发生休眠)

#include<linux/delay> 

//以下三个延时函数均是忙等待函数,因而在延迟过程中午饭运行其他任务。不发生休眠的。

void ndelay(unsigned long nsecs); //延时纳秒

void udelay(unsigned long usecs);//延时微秒

void mdelay(unsigned long msecs);//延时毫秒

12、不用忙等待的延时方式(将调用进程休眠给定时间)

#include<linux/delay.h>

void msleep(unsigened int millisecs);//休眠millisecs毫秒
不可中断的休眠millisecs毫秒

unsigned long msleep_interruptible(unsigned int millisecs);//可中断的休眠

void ssleep(unsigned int seconds);//休眠seconds秒。

 

内核定时器:

13、定时器用于控制某个函数(定时器处理函数)在未来的某个特定时间执行。内核定时器注册的处理函数只执行一次—不是循环执行的。

14、内核定时器被组织成双向链表,并使用struct
time_list 结构描述。

#include<linux/timer.h>

struct timer_list{

 
unsigned long expires;  //超时的jiffies

 
void (*function)(unsigned long);//超时处理函数

  
unsigned long data; //超时处理函数参数。

};

15、内核定时器操作函数

15.1初始化(初始化定时器队列结构)

   
void init_timer(struct timer_list * timer);

   
struct timer_list TIMER_INITIALIZER(_function,_expires,_data);

15.2添加定时器(启动定时器,开始倒计时)

   
void add_timer(struct time_list *tiemer0:

15.3删除定时器(在定时器超时前将它删除。定时器超时后,系统会自动地将它删除)

15.4内核定时器使用模板

   
static struct timer_list  key_timer;//定义内核定时器对象

   
static void key_timer_handle(unsigned ong data)//定时器处理函数

   
{

     
……

     
//定时器处理函数具体执行代码

     
……

     
// 定时器参数的更新,重启定时器

     
key_timer.expires = jiffies+ KEY_TIMER_DELAY;

   
  add_time(&key_time); 
 //赋新值,可以实现
循环

     
……

}

 

//设备驱动模块加载函数

static int__ init xxx_init(void)

{

 
……

 
//初始化内核定时器

  init_time(&key_timer);

 
key_timer.function=&key_timer_handle;

 
key_timer.data = (unsigned long)key_desc;

 
key_timer.expires = jiffies+KEY_TIMER_DELAY;

 

 
//添加内核定时器(这是第一次启动)

 
add_time(&key_time);  

……

}

 

Static void__exit xxx_exit(void)

{
              ……

  
//删除定时器

  
del_timer(&key_timer);

  
……

}

16、内核定时器与tasklet比较

16.1相同点:
在中断期间运行,时钟会在调度他们的同一个cpu上运行,软件中断的上下文,原子模式运行。(软件中断时打开硬件中断的同时执行某些异步任务的一种内核机制) 

 16.2不同的:不能要求TASKLECT在给定的时间执行。

 

声明:本文非原创,整理自申嵌 

时间: 2024-10-22 05:19:53

5-3 Linux内核计时、延时函数与内核定时器【转】的相关文章

linux驱动开发--copy_to_user 、copy_from_user函数实现内核空间数据与用户空间数据的相互访问

设备读操作 如果该操作为空,将使得read系统调用返回负EINVAL失败,正常返回实际读取的字节数 ssize_t (*read)(struct file *filp, char __user *buf, size_t  count, lofft *f_pos);filp:待操作的设备文件file结构体指针buf:待写入所读取数据的用户空间缓冲区指针count:待读取数据字节数f_pos:待读取数据文件位置,读取完成后根据实际读取字节数重新定位返回:成功实际读取的字节数,失败返回负值 设备写操作

Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7) 作者:tekkamanninja  转自:http://blog.chinaunix.net/uid-25909619-id-4938395.html     前面粗略分析start_kernel函数,此函数中基本上是对内存管理和各子系统的数据结构初始化.在内核初始化函数start_kernel执行到最后,就是调用rest_init函数,这个函数的主要使命就是创建并

Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7) 作者:tekkamanninja  转自:http://blog.chinaunix.net/uid-25909619-id-4938393.html 在分析start_kernel函数的时候,其中有构架相关的初始化函数setup_arch. 此函数根据构架而异,对于ARM构架的详细分析如下: void __init setup_arch(char **cmdli

Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7) 作者:tekkamanninja  转自:http://blog.chinaunix.net/uid-25909619-id-4938396.html     在基本分析完内核启动流程的之后,还有一个比较重要的初始化函数没有分析,那就是do_basic_setup.在内核init线程中调用了do_basic_setup,这个函数也做了很多内核和驱动的初始化

内核延时函数【转】

转自:http://www.mamicode.com/info-detail-514261.html 1) msleep:实现毫秒级的延时,该延时保证至少延时所设置的延时时间,不会提前超时返回,会让出CPU void msleep(unsigned int msecs) { unsigned long timeout = msecs_to_jiffies(msecs) + 1; while (timeout) timeout = schedule_timeout_uninterruptible(

Linux 系统裁剪笔记 4 (内核配置选项及删改)

在menuconfig中配置,可以对进行Linux内核配置选项及删改.本文介绍详细配置方法.第一部分:全部删除Code maturity level options ---> 代码成熟等级选项[]Prompt for development and/or incomplete code/drivers 默认情况下是选择的,这将会在设置界面中显示还在开发或者还没有完成的代码与驱动.不选.第二部分 :除以下选项,其它全部删除General setup-〉System V IPC (IPC:Inter

源代码-怎么样再linux下查看dump_stack()函数打印出来的信息?

问题描述 怎么样再linux下查看dump_stack()函数打印出来的信息? 本人Linux小白,刚学没多久,最近我想要研究下linux中打开文件操作的流程,于是我就在内核的filp open()函数的源代码中插入了dump_stack_()函数. 我重新编译内核之后,直接在终端调用cd 命令行,然后想在系统日志里面看看有没有 函数调用打印出来,但是翻来翻去好像什么都没有的样子. 但是我自己写一个简单的模块,里面有dump_stack_函数,在编译模块,再运行这个模块, 这样的话又可以在日志里

十天学Linux内核之第六天---调度和内核同步

原文:十天学Linux内核之第六天---调度和内核同步 心情大好,昨晚我们实验室老大和我们聊了好久,作为已经在实验室待了快两年的大三工科男来说,老师让我们不要成为那种技术狗,代码工,说多了都是泪啊,,不过我们的激情依旧不变,老师帮我们组好了队伍,着手参加明年的全国大赛,说起来我们学校历史上也就又一次拿国一的,去了一次人民大会堂领奖,可以说老大是对我们寄予厚望,以后我会专攻仪器仪表类的题目,激情不灭,梦想不息,不过最近一段时间还是会继续更新Linux内核,总之,继续加油~ Linux2.6版本中的

戴文的Linux内核专题:22 配置内核 (18)

戴文的Linux内核专题:22 配置内核 (18) 你好!准备好阅读下一篇文章了么?在本篇中,我们将会讨论辅助显示.辅助显示是一些小的LCD屏幕:大多数小于或等于128x64.接着,我们会讨论用户空间IO驱动,一些虚拟驱动,Hyper-V,开发中驱动,IOMMU,和其他一些内核特性. 第一个配置辅助显示的驱动是"KS0108 LCD Controller".KS0108 LCD Controller是由三星制造的图形控制器. 下面可以设置LCD并口地址(Parallel port wh