linux驱动之定时器的介绍和内核时间的学习
本文章摘自下面的网友:
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驱动之定时器的介绍和内核时间的学习的更多相关文章
- Linux驱动之定时器在按键去抖中的应用
机械按键在按下的过程中会出现抖动的情况,如下图,这样就会导致本来按下一次按键的过程会出现多次中断,导致判断出错.在按键驱动程序中我们可以这么做: 在按键驱动程序中我们可以这么做来取消按键抖动的影响:当 ...
- linux驱动之定时器的使用
被文章摘自一下几位网友.非常感谢他们. http://blog.sina.com.cn/s/blog_57330c3401011cq3.html Linux的内核中定义了一个定时器的结构: #incl ...
- Linux驱动之GPIO子系统和pinctrl子系统
前期知识 1.如何编写一个简单的Linux驱动(一)--驱动的基本框架 2.如何编写一个简单的Linux驱动(二)--设备操作集file_operations 3.如何编写一个简单的Lin ...
- Linux驱动技术(七) _内核定时器与延迟工作
内核定时器 软件上的定时器最终要依靠硬件时钟来实现,简单的说,内核会在时钟中断发生后检测各个注册到内核的定时器是否到期,如果到期,就回调相应的注册函数,将其作为中断底半部来执行.实际上,时钟中断处理程 ...
- linux设备驱动程序该添加哪些头文件以及驱动常用头文件介绍(转)
原文链接:http://blog.chinaunix.net/uid-22609852-id-3506475.html 驱动常用头文件介绍 #include <linux/***.h> 是 ...
- Linux驱动开发必看详解神秘内核(完全转载)
Linux驱动开发必看详解神秘内核 完全转载-链接:http://blog.chinaunix.net/uid-21356596-id-1827434.html IT168 技术文档]在开始步入L ...
- linux 驱动学习笔记01--Linux 内核的编译
由于用的学习材料是<linux设备驱动开发详解(第二版)>,所以linux驱动学习笔记大部分文字描述来自于这本书,学习笔记系列用于自己学习理解的一种查阅和复习方式. #make confi ...
- linux驱动简单介绍
linux驱动简单介绍 驱动基本介绍 驱动.顾名思义就是“驱使硬件设备行动”.设备驱动与底层硬件之间打交道,按照硬件设备的具体操作方式来读写设备寄存器,最终完成一系列操作. 设备 驱动充当了应用程序 ...
- Linux驱动之内核自带的S3C2440的LCD驱动分析
先来看一下应用程序是怎么操作屏幕的:Linux是工作在保护模式下,所以用户态进程是无法象DOS那样使用显卡BIOS里提供的中断调用来实现直接写屏,Linux抽象出FrameBuffer这个设备来供用户 ...
随机推荐
- python socket函数详解
关于socket函数,每个的意义和基本功能都知道,但每次使用都会去百度,参数到底是什么,返回值代表什么意义,就是说用的少,也记得不够精确.每次都查半天,经常烦恼于此.索性都弄得清楚.通透,并记录下来, ...
- Android:自定义BaseActivity基类
使用BaseActivity可以封装一些重复代码例如设置标题栏颜色,封装一些工具类... 主要功能: 封装Toast 新建一个BaseActivity继承自Activity package com.o ...
- c++输出左右对齐设置
#include<iostream> int main(){ using std::cout; cout.setf(std::ios::left); int w = cout.width( ...
- Jmeter 中 CSV 如何参数化测试数据并实现自动断言
当我们使用Jmeter工具进行接口测试,可利用CSV Data Set Config配置元件,对测试数据进行参数化,循环读取csv文档中每一行测试用例数据,来实现接口自动化.此种情况下,很多测试工程师 ...
- PHP gregoriantojd() 函数
------------恢复内容开始------------ 实例 把格利高里历法的日期转换为儒略日计数,然后再转换回格利高里历法的日期: <?php$jd=gregoriantojd(6,20 ...
- PHP mysqli_set_charset() 函数
设置默认客户端字符集: <?php 高佣联盟 www.cgewang.com // 假定数据库用户名:root,密码:123456,数据库:RUNOOB $con=mysqli_connect( ...
- 《Python与量化投资:从基础到实战》PDF高清完整版-PDF|网盘下载附提取码
本书主要讲解如何利用Python进行量化投资,包括对数据的获取.整理.分析挖掘.信号构建.策略构建.回测.策略分析等.本书也是利用Python进行数据分析的指南,有大量的关于数据处理分析的应用,并将重 ...
- 教你不编程快速解析 JSON 数据
JSON 是一种轻量级的,不受语言约束的数据存储格式,大部分编程语言都可以解析它,并且对编程人员也十分友好.我们在进行通讯/数据交互时,非常经常用到 JSON 格式. 但是,我们在进行数据存储的时候, ...
- day17.json模块、时间模块、zipfile模块、tarfile模块
一.json模块 """ 所有的编程语言都能够识别的数据格式叫做json,是字符串 能够通过json序列化成字符串与如下类型: (int float bool str l ...
- 从零写一个Asp.net core手脚架 (异常处理)
既然是手脚架,那么肯定得明白,手脚架是有限资源的一个整合,我们尽可能完善它,并保留可扩展性才是最终目的,尽可能减少硬编码,让业务不满足的情况下,可以自行修改 我们把解决方案取名Asp.netCoreT ...