Linux驱动中断与时间篇——低分辨率定时器

简介: Linux驱动中断与时间篇——低分辨率定时器

内核延时函数接口

延时的函数有delaysleep两种类型:

delay接口

void ndelay(unsigned long nsecs);//纳秒延时
void udelay(unsigned long usecs);//微妙延时
void mdelay(unsigned long msecs);//毫秒延时

sleep接口

void msleep(unsigned int msecs);//毫秒级延时
long msleep_interruptible(unsigned int msecs);//毫秒级延时,可被信号打断
void ssleep(unsigned int seconds);//秒级延时

delay和sleep的区别

delay型延时:忙等待,占用CPU资源,延迟过程无法进行其他任务。

sleep型延时:休眠,不占用CPU资源,其它模块此时可以使用CPU资源。

低分辨率定时器

jiffies和HZ

jiffies:全局变量,表示系统启动以来产生的节拍数。每产生一次中断,jiffies自动加一。

HZ:赫兹,也叫节拍率,表示每秒种产生多少次中断

例如:HZ200,代表每秒产生200次中断,那2秒钟jiffies的值就应该是400。因此系统的运行时间可以用jiffies/HZ表示。

一秒钟:jiffies + HZ表示一秒钟

原因:内核中统计时间是通过jiffies,因此要比较时间或者定时也是通过jiffies。

例如程序运行一秒钟,内核如何知道运行了一秒?答案是运行一秒后的jiffies值和运行前的jiffies值进行比较,如果相差为一个HZ,则代表一秒钟。jiffies+HZ其实就是一秒后jiffies的值,所以jiffies+HZ可以间接表示一秒钟。

定时2秒:jiffies + 2*HZ。以此类推

获取当前的jiffies值,可以用get_jiffies_64()函数。

将时间转为对应的jiffies值,可以用msecs_to_jiffies()等函数,例如msecs_to_jiffies(1000)代表1秒,函数返回值其实就是HZ

相关接口

#include<linux/timer.h>
struct timer_list { 
    struct list_head list;  
    unsigned long expires;  //定时器到期时间,传入的是jiffies值
    unsigned long data;  //作为参数被传入定时器处理函数
    void (*function)(unsigned long);
};
void init_timer(struct timer_list * timer);//初始化定时器
void add_timer(struct timer_list * timer);//添加一个定时器
int mod_timer(struct timer_list *timer, unsigned long expires);//修改定时器的定时时间expires
int del_timer(struct timer_list * timer);//删除定时器
unsigned int jiffies_to_msecs (const unsigned long j);//将jiffies转为对应的毫秒值
unsigned int jiffies_to_usecs (const unsigned long j);//将jiffies转为对应的微秒值
unsigned long msecs_to_jiffies (const unsigned int m);//将毫秒值转为对应的jiffies
unsigned long usecs_to_jiffies (const unsigned int u);//将微秒值转为对应的jiffies

定时器使用示例

使用步骤:

1、调用init_timer初始化一个定时器,给struct timer_list各成员赋值。

2、调用add_timer将定时器添加到内核定时器链表,时间到后回调函数自动调用,用mod_timer修改expires的值可实现循环定时。

3、不需要定时器时,调用del_timer删除。

单次定时

加载驱动一秒钟后,打印出“timer handler, data:520”:

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>//jiffies在此头文件中定义
#include <linux/timer.h>//struct timer_list
struct timer_list timer;
static void timer_handler (unsigned long arg)
{
 printk("timer handler, data:%d\n", arg);
}
static int __init my_init(void)
{
 printk("%s enter\n", __func__);
 init_timer(&timer);
 timer.expires = get_jiffies_64() + msecs_to_jiffies(1000);//定时1秒
 timer.function = timer_handler;
 timer.data = 520;
 add_timer(&timer);
    return 0;
}
static void __exit my_exit(void)
{
 printk("%s enter\n", __func__);
 del_timer(&timer);
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");

循环定时

实现循环定时就是在定时时间到了之后,调用mod_timer函数再次修改定时时间

每隔一秒钟打印“timer handler, data:520

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>//jiffies在此头文件中定义
#include <linux/timer.h>//struct timer_list
struct timer_list timer;
static void timer_handler (unsigned long arg)
{
 printk("timer handler, data:%d\n", arg);
 mod_timer(&timer, get_jiffies_64() + msecs_to_jiffies (1000));
}
static int __init my_init(void)
{
 init_timer(&timer);
 timer.expires = get_jiffies_64() + msecs_to_jiffies (1000);//定时1秒
 timer.function = timer_handler;
 timer.data = 520;
 add_timer(&timer);
    return 0;
}
static void __exit my_exit(void)
{
 del_timer(&timer);
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");

end

往期推荐

入职Linux驱动工程师后,我才知道的真相......

机遇:我是如何走向Linux驱动的...

当我用几道题考了一遍做Linux驱动的同事......

“不是所有的驱动岗,都值得你去”

Linux驱动面试高频考点

相关文章
|
1月前
|
存储 JSON Linux
|
1月前
|
Oracle 关系型数据库 Linux
讲解linux下的Qt如何编译oracle的驱动库libqsqloci.so
通过这一连串的步骤,可以专业且有效地在Linux下为Qt编译Oracle驱动库 `libqsqloci.so`,使得Qt应用能够通过OCI与Oracle数据库进行交互。这些步骤适用于具备一定Linux和Qt经验的开发者,并且能够为需要使用Qt开发数据库应用的专业人士提供指导。
50 1
讲解linux下的Qt如何编译oracle的驱动库libqsqloci.so
|
1月前
|
安全 小程序 Linux
Linux中信号是什么?Ctrl + c后到底为什么会中断程序?
信号在进程的学习中是一个非常好用的存在,它是软件层次上对中断机制的一种模拟,是异步通信方式,同时也可以用来检测用户空间到底发生了什么情况,然后系统知道后就可以做出相应的对策。
|
1月前
|
缓存 网络协议 算法
【Linux系统编程】深入剖析:四大IO模型机制与应用(阻塞、非阻塞、多路复用、信号驱动IO 全解读)
在Linux环境下,主要存在四种IO模型,它们分别是阻塞IO(Blocking IO)、非阻塞IO(Non-blocking IO)、IO多路复用(I/O Multiplexing)和异步IO(Asynchronous IO)。下面我将逐一介绍这些模型的定义:
|
2月前
|
Linux
【GEC6818开发板】Linux驱动中printk无法在终端输出显示
【GEC6818开发板】Linux驱动中printk无法在终端输出显示
|
2月前
|
Linux 程序员 芯片
【Linux驱动】普通字符设备驱动程序框架
【Linux驱动】普通字符设备驱动程序框架
|
1月前
|
Linux 开发者
Linux底层驱动社区饮水机系统详解
在Linux驱动开发中,入门时通常会关注驱动程序的三大核心步骤:入口函数、出口函数和声明许可证。这些步骤构成了驱动程序的基本结构,是驱动与内核交互的基础。下面是对这三个步骤的简要说明:
|
2月前
|
Linux
【Linux驱动学习(1)】USB与input子系统,linux统一设备模型,枚举,USB描述符深入剖析
【Linux驱动学习(1)】USB与input子系统,linux统一设备模型,枚举,USB描述符深入剖析

热门文章

最新文章