内核定时器使用

内核定时器是内核用来控制在未来某个时间点(基于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. iOS开发实现登陆

    Assumption假设:iOS端加载Web页,然后用户输入用户名密码登陆,WebServer会把用户登陆信息记载在Cookie.那么iOS客户端如何取到Cookie中的登陆信息. 客户端监听 NSH ...

  2. C# trace debug TraceListener调试信息详解

    在C#编程中,可能要碰到把调试信息输出的问题,我们可以自己把信息显示在某个控件上,但是MS自己提供了一套机制帮助我们输出一些调试信息,这些信息有助于我们判断程序的走向,不用自己再去额外写调试代码了. ...

  3. iOS开发,新手入门指导

    在做了近两年wp,安卓开发之后,某一天突然决定投身iOS的开发之中. 因为一直用的mac,做wp开发的时候都用双系统,vs开久了,就会比较烫,这点让人不爽.后来更多地做安卓,直接mac下开发,很舒适的 ...

  4. 如何用angularjs制作一个完整的表格之四__自定义ng-model标签的属性使其支持input之外的html元素

    有的时候我们需要为非input类型的元素添加ng-model来实现双向的数据绑定,从而减少冗余代码,那么可以尝试一下的方式 例如:我页面中使用了contenteditable这个属性来实现用户可直接编 ...

  5. Eclipse 将Java项目转为Dynamic web project

    1.打开项目根目次下的.project 在<buildSpec>节点下是否存在 <buildCommand> <name>org.eclipse.wst.commo ...

  6. Sql 格式化工具

    SQL Pretty Printer:目前提供4种使用方式,桌面版本,SSMS(SQL Server Management Studio)插件,VS插件,和提供API接口. SQL Pretty Pr ...

  7. Python Django开始

    1.创建工程 C:\procedure\projects>django-admin startproject mysite 2.同步数据库 C:\procedure\projects\mysit ...

  8. ExtJs5_使用图标字体来美化按钮

    sencha 的例子中,有使用图标字体来美化按钮的例子,这个用起来又方便风格又统一,例如下图: 上面图标字体的使用方法也很简单,只要下载Font Awesome的css和图标文件,放到项目里就可以了. ...

  9. ViewPage和ActionBar打造滑动视图

    滑动效果非常流畅,可以将页签放置到ActionBar上,点击页签可以切换ViewPage,滑动ViewPage会同步更显Tabs. main.xml <?xml version="1. ...

  10. Cracking the coding interview--Q2.4

    Write code to partition a linked list around a value x, such that all nodes less than xcome before a ...