内核定时器使用

内核定时器是内核用来控制在未来某个时间点(基于jiffies)调度执行某个函数的一种机制,其实现位于 <Linux/timer.h> 和 kernel/timer.c 文件中。

被调度的函数肯定是异步执行的,它类似于一种“软件中断”,而且是处于非进程的上下文中,所以调度函数必须遵守以下规则:

1) 没有 current 指针、不允许访问用户空间。因为没有进程上下文,相关代码和被中断的进程没有任何联系。

2) 不能执行休眠(或可能引起休眠的函数)和调度。

3) 任何被访问的数据结构都应该针对并发访问进行保护,以防止竞争条件。

内核定时器的调度函数运行过一次后就不会再被运行了(相当于自动注销),但可以通过在被调度的函数中重新调度自己来周期运行。

在SMP系统中,调度函数总是在注册它的同一CPU上运行,以尽可能获得缓存的局域性。

内核定时器的数据结构

struct timer_list {
struct list_head entry;
unsigned long expires;
void (*function)(unsigned long);
unsigned long data;
struct tvec_base *base;
/* ... */
};

其中 expires 字段表示期望定时器执行的 jiffies 值,到达该 jiffies 值时,将调用 function 函数,并传递 data 作为参数。当一个定时器被注册到内核之后,entry 字段用来连接该定时器到一个内核链表中。base 字段是内核内部实现所用的。

需要注意的是 expires 的值是32位的,因为内核定时器并不适用于长的未来时间点。

初始化

在使用 struct timer_list 之前,需要初始化该数据结构,确保所有的字段都被正确地设置。初始化有两种方法。

方法一:

DEFINE_TIMER(timer_name, function_name, expires_value, data);

该宏会定义一个名叫 timer_name 内核定时器,并初始化其 function, expires, name 和 base 字段。

方法二:

struct timer_list mytimer;
void init_timer(struct timer_list *timer);

上述init_timer函数将初始化struct timer_list的 entry的next 为 NULL ,并未base指针赋值

tm->expires = ;
tm->function = ;
tm->data = ;

setup_timer(&mytimer, (*function)(unsigned long), unsigned long data); 方法也可以用于初始化定时器并赋值其成员,源代码为:

static inline void setup_timer(struct timer_list * timer, void (*function)(unsigned long), unsigned long data)
{
  timer->function = function;
  timer->data = data;
  init_timer(timer);
}

注意,无论用哪种方法初始化,其本质都只是给字段赋值,所以只要在运行 add_timer() 之前,expires, function 和 data 字段都可以直接再修改。

关于上面这些宏和函数的定义,参见 include/linux/timer.h。

注册

定时器要生效,还必须被连接到内核专门的链表中,这可以通过  add_timer(struct timer_list *timer)  来实现。

重新注册(修改)

要修改一个定时器的调度时间,可以通过调用  mod_timer(struct timer_list *timer, unsigned long expires) 。mod_timer() 会重新注册定时器到内核,而不管定时器函数是否被运行过。

注销

注销一个定时器,可以通过  del_timer(struct timer_list *timer)  或  del_timer_sync(struct timer_list *timer) 。

其中 del_timer_sync 是用在 SMP 系统上的(在非SMP系统上,它等于del_timer),当要被注销的定时器函数正在另一个 cpu 上运行时,del_timer_sync() 会等待其运行完,所以这个函数会休眠。另外还应避免它和被调度的函数争用同一个锁。对于一个已经被运行过且没有重新注册自己的定时器而言,注销函数其实也没什么事可做。

int timer_pending(const struct timer_list *timer);

这个函数用来判断一个定时器是否被添加到了内核链表中以等待被调度运行。注意,当一个定时器函数即将要被运行前,内核会把相应的定时器从内核链表中删除(相当于注销)。

使用范例

/* 实现每隔一秒向内核log中打印一条信息 */
#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 callback(unsigned long arg)
{
struct timeval tv;
char *strp = (char*)arg; printk("%s: %lu, %s\n", __func__, jiffies, strp); do_gettimeofday(&tv);
printk("%s: %ld, %ld\n", __func__,
tv.tv_sec - oldtv.tv_sec, //与上次中断间隔 s
tv.tv_usec- oldtv.tv_usec); //与上次中断间隔 ms oldtv = tv;
tm.expires = jiffies+*HZ;
add_timer(&tm); //重新开始计时
} static int __init demo_init(void)
{
printk(KERN_INFO "%s : %s : %d - ok.\n", __FILE__, __func__, __LINE__); init_timer(&tm); //初始化内核定时器 do_gettimeofday(&oldtv); //获取当前时间
tm.function= callback; //指定定时时间到后的回调函数
tm.data = (unsigned long)"hello world"; //回调函数的参数
tm.expires = jiffies+*HZ; //定时时间
add_timer(&tm); //注册定时器 return ;
} static void __exit demo_exit(void)
{
printk(KERN_INFO "%s : %s : %d - ok.\n", __FILE__, __func__, __LINE__);
del_timer(&tm); //注销定时器
} module_init(demo_init);
module_exit(demo_exit); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Farsight");
MODULE_DESCRIPTION("Demo for kernel module");

一些和时间相关的内容

linux/jiffies.h

计数值:
jiffies
u64 get_jiffies_64(void)

asm/param.h
每秒触发中断的次数
HZ

---------------------------------------------

时间值
秒数=(jiffies(new) - jiffies(old))/HZ
jiffies(new) = jiffies(old) + 秒*HZ

---------------------------------------------
linux/delay.h
延时函数
void ssleep(unsigned int seconds);
void msleep(unsigned int msecs);

---------------------------------------------
时间函数
linux/time.h
void do_gettimeofday(struct timeval *tv)

Linux设备驱动——内核定时器的更多相关文章

  1. Linux设备驱动中的阻塞和非阻塞I/O <转载>

    Green 博客园 首页 新随笔 联系 订阅 管理 Linux设备驱动中的阻塞和非阻塞I/O   [基本概念] 1.阻塞 阻塞操作是指在执行设备操作时,托不能获得资源,则挂起进程直到满足操作所需的条件 ...

  2. 【Linux开发】linux设备驱动归纳总结(七):2.内核定时器

    linux设备驱动归纳总结(七):2.内核定时器 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  3. 《Linux设备驱动开发具体解释(第3版)》(即《Linux设备驱动开发具体解释:基于最新的Linux 4.0内核》)网购链接

    <Linux设备驱动开发具体解释:基于最新的Linux 4.0内核> china-pub   spm=a1z10.3-b.w4011-10017777404.30.kvceXB&i ...

  4. Linux设备驱动工程师之路——内核链表的使用【转】

    本文转载自:http://blog.csdn.net/forever_key/article/details/6798685 Linux设备驱动工程师之路——内核链表的使用 K-Style 转载请注明 ...

  5. linux设备驱动归纳总结(七):1.时间管理与内核延时【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-100005.html linux设备驱动归纳总结(七):1.时间管理与内核延时 xxxxxxxxxxx ...

  6. linux设备驱动归纳总结(一)内核的相关基础概念【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-59413.html linux设备驱动归纳总结(一):内核的相关基础概念 xxxxxxxxxxxxxx ...

  7. 转:Linux设备驱动开发(1):内核基础概念

    一.linux设备驱动的作用 内核:用于管理软硬件资源,并提供运行环境.如分配4G虚拟空间等. linux设备驱动:是连接硬件和内核之间的桥梁. linux系统按个人理解可按下划分: 应用层:包括PO ...

  8. (转载)小白的linux设备驱动归纳总结(一):内核的相关基础概念---学习总结

    1. 学习总结 小白的博客讲的linux内核驱动这一块的东西比较基础,因此想通过学习他的博客,搭配着看书的方式来学习linux内核和驱动.我会依次更新在学习小白的博客的过程的感悟和体会. 2.1 内核 ...

  9. 【Linux开发】linux设备驱动归纳总结(七):1.时间管理与内核延时

    linux设备驱动归纳总结(七):1.时间管理与内核延时 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

随机推荐

  1. Java面试——基础

    1,作用域,Java只有public,protect,private,默认是default相当于friendly 作用域 当前类          同一package 子类            其它 ...

  2. 网络编程Socket之UDP

    服务器端实现步骤: 1. 创建 DatagramSocket,指定端口号 2. 创建 DatagramPacket 3. 接收客户端发送的数据信息 4. 读取数据 package cn.jmu.edu ...

  3. 基于GBT28181:SIP协议组件开发-----------第二篇SIP组件开发原理

    原创文章,引用请保证原文完整性,尊重作者劳动,原文地址http://www.cnblogs.com/qq1269122125/p/3937590.html,qq:1269122125. 上一节中讲的S ...

  4. (转载)div最小宽度和自适应的实现方法

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  5. 无法嵌入互操作类型“Microsoft.Office.Interop.Word.ApplicationClass”。请改用适用的接口。

    引用里找到Microsoft.Office.Interop.Word右键属性 在嵌入互操作类型里,选上False就行了.

  6. set_time_limit() 控制页面运行时间

    当你的页面有大量数据时,建议使用set_time_limit()来控制运行时间,默认是30s,所以需要你将执行时间加长点,如 set_time_limit(300)  ,其中将秒数设为0 ,表示持续运 ...

  7. 适应手机端的jQuery图片滑块动画

    一款比较特别的jQuery图片滑块插件,它不仅在PC浏览器上可以使用,而且更适合在手机端的网页中使用.这款jQuery插件不仅可以定义图片切换的方向,而且可以即时切换图片切换的动画方式,可以有平移.翻 ...

  8. highcharts实例教程一:结合php与mysql生成折线图

    Highcharts是一款纯javascript和html5编写的图表库,不仅几乎能兼容所有pc浏览器,而且对ios和android手机端的兼容 性也不错,它能够很简单便捷的在Web网站或Web应用中 ...

  9. django group_by

    from django.db.models import Count Members.objects.values('designation').annotate(dcount=Count('desi ...

  10. Cmakelist.txt

    在LINUX下面编程需要注意的是,需要自己配置链接库,比如 TARGET_LINK_LIBRARIES(),需要把自己要加的库加进去