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

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

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

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

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

3. Linux内核使用struct timer_list来描述一个定时器

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

(1)expires:表示期望定时器执行的 jiffies 值,到达该 jiffies 值时,将调用 function 函数,并传递 data 作为参数

(2)entry:连接该定时器到一个内核链表中

(3)base:内核内部使用

4. 定时器使用流程

(1)定义和初始化定时器

  ① 方法一:

DEFINE_TIMER(timer_name, function_name, expires_value, data);

  DEFINE_TIMER为宏定义,源码如下

#define DEFINE_TIMER(_name, _function, _expires, _data)        \
struct timer_list _name = \
TIMER_INITIALIZER(_function, _expires, _data)

  ② 方法二

struct timer_list my_timer;
init_timer(&my_timer);
my_timer.expires = jiffies + delay; 
my_timer.data = 0;     
my_timer.function = my_function; 

  注:以上宏和函数定义于include/linux/timer.h。

(3)注册定时器:add_timer

add_timer(&my_timer);

(4)启动定时器:mod_timer

int mod_timer(struct timer_list *timer, unsigned long expires)

  注:该函数负责修改内核定时器timer的超时字段expires。该函数可以修改激活和没有激活的内核定时器的超时时间,并把它们都设置为激活状态;返回值为0表示修改的内核定时器在修改之前处于未激活状态,返回值为1表示修改的内核定时器在修改之前处于已激活状态。

(5)删除定时器:一但定时到期,内核会自动删除定时器节点。但有时需要显示的调用函数del_timer()来删除定时器节点

int del_timer(struct timer_list *timer)

5. Linux内核定时器组织架构

(1)与softirq、工作队列两种中断下半部的处理方法类似,每一个内核定时器节点与系统中的处理器通过一个每处理器变量联系起来。内核在文件 kernel/timer.c中使用下面的语句分配了一个名称为tvec_bases、类型为tvec_base_t的每处理器变量。

static DEFINE_PER_CPU(struct tvec_base *, tvec_bases) = &boot_tvec_bases;

(2)数据结结struct tevc_base用来记录系统中每一个处理器上待处理内核定时器节点的相关信息

struct tvec_base {
spinlock_t lock;
struct timer_list *running_timer;
unsigned long timer_jiffies;
struct tvec_root tv1;
struct tvec tv2;
struct tvec tv3;
struct tvec tv4;
struct tvec tv5;
} ____cacheline_aligned;

①lock:用于保护每处理器变量tvec_bases的本地拷贝

②running_timer:记录正在本地处理器上进行超时处理的内核定时器

③timer_jiffies:记录该数据结构中所包含的定时器中最早超时时间,根据该变量可以计算出超时定时器节点所在链表的表头。

④tv1,tv2,tv3,tv4,tv5:

#define TVN_BITS (CONFIG_BASE_SMALL ? 4 : 6)
#define TVR_BITS (CONFIG_BASE_SMALL ? 6 : 8)
#define TVN_SIZE (1 << TVN_BITS)
#define TVR_SIZE (1 << TVR_BITS)
#define TVN_MASK (TVN_SIZE - 1)
#define TVR_MASK (TVR_SIZE - 1) struct tvec {
struct list_head vec[TVN_SIZE];
}; struct tvec_root {
struct list_head vec[TVR_SIZE];
};

(3)内核定时器架构示意图:

5. 简单示例

#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <asm/atomic.h> MODULE_LICENSE("GPL"); static struct timer_list timer; static void do_work(struct work_struct* work);
static DECLARE_WORK(test_work, do_work);
static DECLARE_WORK(test_work1, do_work);
static struct workqueue_struct *test_workqueue; // 定时器超时函数
void test_timer_fn(unsigned long arg)
{
if (queue_work(test_workqueue, &test_work)== ) {
printk("Timer (0) add work queue failed\n");
}
} // work func
static void do_work(struct work_struct* work)
{
printk("====do_work start====\n");
printk("====do_work end====\n");
} // init
int wq_init(void)
{
// work queue
test_workqueue = create_singlethread_workqueue("test-wq"); // timer1
init_timer(&timer);
timer.function= test_timer_fn;
add_timer(&timer); //设置超时时间,启动定时器
mod_timer(&timer, jiffies + HZ / ); // 设置超时时间 1\100s return ;
} void wq_exit(void)
{
del_timer(&timer); destroy_workqueue(test_workqueue); printk("wq exit success\n");
} module_init(wq_init);
module_exit(wq_exit);

Linux设备驱动开发基础--内核定时器的更多相关文章

  1. Linux设备驱动开发基础--阻塞型设备驱动

    1. 当一个设备无法立刻满足用户的读写请求时(例如调用read时,设备没有数据提供),驱动程序应当(缺省的)阻塞进程,使它进入等待(睡眠)状态,知道请求可以得到满足. 2. Linux内核等待队列:在 ...

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

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

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

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

  4. Hasen的linux设备驱动开发学习之旅--时钟

    /** * Author:hasen * 參考 :<linux设备驱动开发具体解释> * 简单介绍:android小菜鸟的linux * 设备驱动开发学习之旅 * 主题:时钟 * Date ...

  5. 《Linux设备驱动开发具体解释(第3版)》进展同步更新

    本博实时更新<Linux设备驱动开发具体解释(第3版)>的最新进展. 2015.2.26 差点儿完毕初稿. 本书已经rebase到开发中的Linux 4.0内核,案例多数基于多核CORTE ...

  6. Linux 设备驱动开发 —— platform设备驱动应用实例解析

    前面我们已经学习了platform设备的理论知识Linux 设备驱动开发 —— platform 设备驱动 ,下面将通过一个实例来深入我们的学习. 一.platform 驱动的工作过程 platfor ...

  7. Linux设备驱动开发环境的搭建(转)

    经过两周的摸索,终于对Linux设备驱动开发有了个初步的认识,下面对Linux设备驱动开发环境的搭建做个小结,以方便自己以后查询,同时也能给同道的初学者一点帮助. 刚接触Linux设备驱动时,初学者往 ...

  8. Linux设备驱动开发详解

    Linux设备驱动开发详解 http://download.csdn.net/detail/wuyouzi067/9581380

  9. 《Linux设备驱动开发详解(第2版)》配套视频登录51cto教育频道

    http://edu.51cto.com/course/course_id-379-page-1.html http://edu.51cto.com/course/course_id-379-page ...

随机推荐

  1. IntelliJ IDEA——利用maven插件构建web工程

  2. logback.xml 实例

    <?xml version="1.0" encoding="UTF-8"?> <configuration debug="false ...

  3. wc.exe C++实现

    目录 Github项目地址 PSP表格 解题思路 设计实现过程 测试运行 项目小结 Github项目地址 wc-project PSP表格 PSP2.1 Personal Software Proce ...

  4. React学习笔记3

    React的生命周期   生命周期分为三个阶段   1.mounted(初始化的时候) 当我们看见页面元素从JSX变成了DOM节点时,React组件已经被载入(mounted)到页面中了   2.up ...

  5. Arduino I2C + 数字式环境光传感器BH1750FVI

    BH1750FVI是日本罗姆(ROHM)半导体生产的数字式环境光传感IC.其主要特性有: I2C数字接口,支持速率最大400Kbps 输出量为光照度(Illuminance) 测量范围1~65535 ...

  6. Spring中的用到的设计模式

    应该说设计模式是我们在写代码时候的一种被承认的较好的模式.好的设计模式就像是给代码造了一个很好的骨架,在这个骨架里,你可以知道心在哪里,肺在哪里,因为大多数人都认识这样的骨架,就有了很好的传播性.这是 ...

  7. [.net 多线程]Task

    C# 异步编程Task整理(一) c# .Net并行和多线程编程之Task学习记录! .NET 实现并行的几种方式(一) Dispatcher介绍 [C#学习笔记]使用C#中的Dispatcher 用 ...

  8. linux chmod对文件权限的操作

    在Unix和Linux的各种操作系统下,每个文件(文件夹也被看作是文件)都按读.写.运行设定权限. 例如我用ls -l命令列文件表时,得到如下输出: -rw-r--r-- 1 apple users ...

  9. 基于 Token 的身份验证:JSON Web Token(JWT)

    1.传统身份验证和JWT的身份验证 传统身份验证:       HTTP 是一种没有状态的协议,也就是它并不知道是谁是访问应用.这里我们把用户看成是客户端,客户端使用用户名还有密码通过了身份验证,不过 ...

  10. 洛谷P2766 最长不下降子序列问题(最大流)

    传送门 第一问直接$dp$解决,求出$len$ 然后用$f[i]$表示以$i$为结尾的最长不下降子序列长度,把每一个点拆成$A_i,B_i$两个点,然后从$A_i$向$B_i$连容量为$1$的边 然后 ...