本文章摘自下面的网友:

http://blog.sina.com.cn/s/blog_6e5b342e0100m87d.html

一、内核中如何记录时间

任何程序都需要时间控制,其主要目的是:

  • 测量时间流逝和比较时间
  • 知道当前时间
  • 指定时间量的延时操作

为达到这个目的,应用程序使用日历时间(年月日时分秒)或者自1970年1月1日零时零分零秒到当前的秒数来度量时间的流逝,但内核中需要更加有精度的时间度量,因此内核使用时钟嘀嗒来记录时间。时钟中断发生后内核内部时间计数器增加1(即:增加1个时钟嘀嗒),系统引导时为0,当前值为自上次系统引导以来的时钟滴答数,程序可通过内核定义的全局变量jiffies_64或jiffies来访问。真实硬件上每秒的嘀嗒数从 50 到 1200 不等 ,x86上默认为1000,而s3c2440上默认为200,出于统一编程接口的考虑,内核定义了一个宏HZ,它表示1秒钟的嘀嗒数,可供程序使用。

Jiffies 和 jiffies_64是 unsigned long 类型只读变量,其用法如下:

  • #include <linux/jiffies.h>
  • unsigned long j, stamp_1, stamp_half, stamp_n;
  • j = jiffies;
  • stamp_1 = j + HZ;
  • stamp_half = j + HZ/2;
  • stamp_n = j + n * HZ / 1000;

注意:32-位 平台上当 HZ 是 1000 时, 计数器只是每 50 天溢出一次, 必要时你的代码应当准备处理这个事件

比较2个时间的大小,常用内核的如下宏定义:

  • #include <linux/jiffies.h>
  • int time_after(unsigned long a, unsigned long 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);

它们分别在时间a在时间b之后、之前、之后或相等、之前或相等的时候为真,反之为假

    • 求 2 个 jiffies 实例之间的差:

      • diff = (long)t2 - (long)t1;.
    • 你可以转换一个 jiffies 差为毫秒, 一般地通过:
      • msec = diff * 1000 / HZ;
    • jiffies与日历时间的转换函数:
      • #include <linux/time.h>
      • unsigned long timespec_to_jiffies(struct timespec *value);
      • void jiffies_to_timespec(unsigned long jiffies, struct timespec *value);
      • unsigned long timeval_to_jiffies(struct timeval *value);
      • void jiffies_to_timeval_r(unsigned long jiffies, struct *timeval)

二、内核定时器

1、概述

  • 无论何时你需要调度一个动作以后发生, 就可以使用内核定时器,如:当硬件无法发出中断时, 可通过使用内核定时器,以定期的间隔检查一个设备的状态。
  • 一个内核定时器是一个数据结构, 它指导内核在一个用户定义的时间,使用一个用户定义的参数,执行一个用户定义的函数
  • 由内核线程——软中断(ksoftirqd/0)调度执行

    一个cpu,一个ksoftirqd

    ksoftirqd属于atomic context

    ksoftirqd运行时,不禁用irq

2、定时器 API

#include <linux/timer.h>

struct timer_list {
unsigned long expires;
void (*function)(unsigned long);
unsigned long data;
其它字段
};
    静态初始化定时器结构:
    struct timer_list timerval = TIMER_INITIALIZER(_function,_expires,_data)
    动态初始化定时器结构:
    setup_timer(struct timer_list *timer,_function,_data);   //初始化function和data后,调用init_timer
    init_timer(struct timer_list *timer);
    timer.expires = jiffies+100;或者:timer.expires = jiffies + HZ/10(手动指定触发时间)
    将已经初始化的定时器加入系统定时器链表:
    void add_timer(struct timer_list *timer);
    注:定时器执行后,会自动退出系统定时器链表,如需再次执行,则需要更新expires后,再次加入系统定时器链表
    更新一个定时器的超时时间,同时加入系统链表
    int mod_timer(struct timer_list *timer,unsigned long expires)
    删除定时器,退出系统定时器链表
    int del_timer(struct timer_list *timer)
    三、如何在内核中实现延时
      设备驱动常常需要延后一段时间执行一个特定片段的代码, 以便允许硬件完成某个任务。延时一般区分为短延时和长延时
          1、短延时:当一个设备驱动需要等待硬件的反应时间, 涉及到的延时常常是最多几个毫秒 。此种延时就是短延时,一般采用忙等待。(忙等待我个人理解是此时处理器依然在本进程中)
          相关函数如下:
#include <linux/delay.h>
void ndelay(unsigned long nsecs);
void udelay(unsigned long usecs);
void mdelay(unsigned long msecs);

2、长延时:

如果需要延后较长时间,就可以采用长延时。长延时可分为忙等待和让出CPU两种方式。

1)、忙等待:

  unsigned long j1 = jiffies + 2*HZ;
      while (time_before(jiffies, j1))

cpu_relax();

  cpu_relex 的调用使用了一个特定于体系的方式,你此时没有用处理器做事情,比较浪费处理器的资源

2)、让出处理器

  unsigned long j1 = jiffies + 3600*HZ;

  while (time_before(jiffies, j))

  {

    set_current_state(TASK_INTERRUPTIBLE);

    schedule_timeout(30*HZ);

  }

3)此外,如果你的驱动使用一个等待队列来等待某些其他事件,但是你也想确保它在一个确定时间段内运行能够运行,而不是永久等待,那么可以使用超时

  

#include <linux/wait.h>
long wait_event_timeout(wait_queue_head_t q, condition, long timeout);
long wait_event_interruptible_timeout(wait_queue_head_t q, condition, long timeout);
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/jiffies.h> struct timer_list mytimer; static void myfunc(unsigned long data)
{
printk("%s/n", (char *)data);
mod_timer(&mytimer, jiffies + *HZ);
} static int __init mytimer_init(void)
{
setup_timer(&mytimer, myfunc, (unsigned long)"Hello, world!");
mytimer.expires = jiffies + HZ;
add_timer(&mytimer); return ;
} static void __exit mytimer_exit(void)
{
del_timer(&mytimer);
} module_init(mytimer_init);
module_exit(mytimer_exit);

后面会编写一个linux模块化驱动编程的实例和延时。

linux驱动之定时器的介绍和内核时间的学习的更多相关文章

  1. Linux驱动之定时器在按键去抖中的应用

    机械按键在按下的过程中会出现抖动的情况,如下图,这样就会导致本来按下一次按键的过程会出现多次中断,导致判断出错.在按键驱动程序中我们可以这么做: 在按键驱动程序中我们可以这么做来取消按键抖动的影响:当 ...

  2. linux驱动之定时器的使用

    被文章摘自一下几位网友.非常感谢他们. http://blog.sina.com.cn/s/blog_57330c3401011cq3.html Linux的内核中定义了一个定时器的结构: #incl ...

  3. Linux驱动之GPIO子系统和pinctrl子系统

    前期知识   1.如何编写一个简单的Linux驱动(一)--驱动的基本框架   2.如何编写一个简单的Linux驱动(二)--设备操作集file_operations   3.如何编写一个简单的Lin ...

  4. Linux驱动技术(七) _内核定时器与延迟工作

    内核定时器 软件上的定时器最终要依靠硬件时钟来实现,简单的说,内核会在时钟中断发生后检测各个注册到内核的定时器是否到期,如果到期,就回调相应的注册函数,将其作为中断底半部来执行.实际上,时钟中断处理程 ...

  5. linux设备驱动程序该添加哪些头文件以及驱动常用头文件介绍(转)

    原文链接:http://blog.chinaunix.net/uid-22609852-id-3506475.html 驱动常用头文件介绍 #include <linux/***.h> 是 ...

  6. Linux驱动开发必看详解神秘内核(完全转载)

    Linux驱动开发必看详解神秘内核 完全转载-链接:http://blog.chinaunix.net/uid-21356596-id-1827434.html   IT168 技术文档]在开始步入L ...

  7. linux 驱动学习笔记01--Linux 内核的编译

    由于用的学习材料是<linux设备驱动开发详解(第二版)>,所以linux驱动学习笔记大部分文字描述来自于这本书,学习笔记系列用于自己学习理解的一种查阅和复习方式. #make confi ...

  8. linux驱动简单介绍

     linux驱动简单介绍 驱动基本介绍 驱动.顾名思义就是“驱使硬件设备行动”.设备驱动与底层硬件之间打交道,按照硬件设备的具体操作方式来读写设备寄存器,最终完成一系列操作. 设备 驱动充当了应用程序 ...

  9. Linux驱动之内核自带的S3C2440的LCD驱动分析

    先来看一下应用程序是怎么操作屏幕的:Linux是工作在保护模式下,所以用户态进程是无法象DOS那样使用显卡BIOS里提供的中断调用来实现直接写屏,Linux抽象出FrameBuffer这个设备来供用户 ...

随机推荐

  1. PHPSTORM常用插件

    Translation 最好用的翻译插件 .env files support 可以在env函数使用是提示.env文件中所有的key值的自动完成功能 PHP composer.json support ...

  2. Java复习总结(二)Java SE 面试题

    Java SE基础知识 目录 Java SE 1. 请你谈谈Java中是如何支持正则表达式操作的? 2. 请你简单描述一下正则表达式及其用途. 3. 请你比较一下Java和JavaSciprt? 4. ...

  3. PDO::quote

    PDO::quote — 为SQL语句中的字符串添加引号.(PHP 5 >= 5.1.0, PECL pdo >= 0.2.1) 说明 语法 public string PDO::quot ...

  4. git原理及如何选择分支模式

    一.git 原理介绍 1.git的四个工作区域 Git有四个工作区域:工作目录(Working Directory).暂存区(Stage/Index).资源库(Repository或Git Direc ...

  5. luogu P2510 [HAOI2008]下落的圆盘

    LINK:下落的圆盘 计算几何.n个圆在平面上编号大的圆将编号小的圆覆盖求最后所有没有被覆盖的圆的边缘的总长度. 在做这道题之前有几个前置知识. 极坐标系:在平面内 由极点 极轴 和 极径组成的坐标系 ...

  6. 可笑,你竟然不知道 Java 如何生成 UUID

    先看再点赞,给自己一点思考的时间,微信搜索[沉默王二]关注这个靠才华苟且的程序员.本文 GitHub github.com/itwanger 已收录,里面还有一线大厂整理的面试题,以及我的系列文章. ...

  7. 关于SqlServer那些事1(回归基础)

    即将实习,回归基础总结,希望可以再好好打磨一下基础的一些东西 关于如何在重新修改表结构时该变其权限设置 步骤: 点击工具 进入选项 设计器 取消勾选阻止保存要求重新创建表的更改 关于创建创建数据库以及 ...

  8. this.getClass().getResource(String) 路径问题

    this.getClass().getResource(String) 路径问题    这里的默认当前路径是该类所在目录: this.getClass() 说明了一切!! 找到类,就找文件:很显然和类 ...

  9. 【HNOI2012】永无乡 题解(并查集+线段树合并)

    题目链接 给定一张含$n$个点$m$条边的无向图,每个点有一个重要指数$a_i$.有两种操作:1.在$x$和$y$之间连一条边:2.求$x$所在连通块中重要程度第$k$小的点. ----------- ...

  10. 31-关键字:final

    final:最终的 1.可以用来修饰:类.方法.变量 2.具体的: 2.1 final 用来修饰一个类:此类不能被其他类所继承. * 比如:String类.System类.StringBuffer类 ...