eq linux_《Linux设备驱动程序》(十二)——时间操作(一)
之前我们学会了如何编写一个字符设备,并对其中的一些重要操作进行了说明。对于一个完整的设备而已,可能还有许多工作要做。
本节我们将要说一下内核中是如何对时间问题进行操作的。
本节主要涉及到以下内容:
- 内核中的时间描述;
- 如何获取当前时间;
- 如何进行延时操作;
内核中的时间描述
在内核中,系统定时硬件以周期性的间隔产生中断,内核通过这个中断来跟踪时间流。
在内核中,上面的中断间隔是由HZ常数来决定的,该常数是与体系结构相关的,定义在中(或者在其中的某个子文件中)。HZ的含义是一秒内产生的中断数。如果需要,用户可以修改这个变量,修改完后需要重新编译整个内核以及模块,不过,不建议用户对该常数进行修改。
内核中使用一个变量来作为中断计数器,系统启动时,该计数器清零;上面的中断发生时,计数器的值加1,因此这个计数器记录了系统启动以来的中断数(也称为时钟滴答数)。这个变量称为jiffies_64,是一个64位的变量(即使在32位系统中也是64位的),而我们在编写驱动过程中,常用的变量名称为jiffies,该变量是一个unsigned long型变量,其值要么就是jiffies_64,要么就是jiffies_64的低32位。以上计数器变量及其相关操作定义在文件中。
通过以上jiffies变量和HZ常数,就可以知道或定义一些时间:
#include unsigned long j = jiffies;unsigned long stamp_1 = j + HZ; /* 未来1秒 */unsigned long stamp_half = j + HZ / 2; /* 未来半秒 */unsigned long stamp_n = j + n * HZ / 1000; /* 未来n毫秒 */
上面对于jiffies可以直接读取,但对于jiffies_64变量读取是非原子的,是不可靠的,如果需要使用jiffies_64变量,我们可以使用以下辅助函数:
#include u64 get_jiffies_64(void);
如果两个unsigned long变量表示获取到的jiffies时间,则我们可以通过比较其大小来判断时间先后(较大时间靠后),但我们还需要考虑时间过长而溢出问题(概率很低),因此,我们最好使用内核提供的宏来进行比较:
#include int time_after(unsigned long a, unsigned log b); /* 判断a时间是否比b时间靠后 */int time_before(unsigned long a, unsigned log b); /* 判断a时间是否比b时间靠前 */int time_after_eq(unsigned long a, unsigned log b); /* 判断a时间是否比b时间靠后或相等 */int time_before_eq(unsigned long a, unsigned log b); /* 判断a时间是否比b时间靠前或相等 */
上面说了内核空间的时间描述。
在用户空间中,用于描述时间的变量是struct timeval(较老,包含秒和毫秒)和struct timespec(较新,包含秒和纳秒)。
如果需要对两个空间的时间描述,可以使用内核提供的函数进行转换:
#include 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(unsgined long jiffies, struct timeval *value);
上面的函数看名字和变量就知道其含义和使用方法,这里就不再描述了。
获取当前时间
通过读取jiffies变量,我们就可以获取当前时间(系统启动后经历的时间)。
但有时候我们也需要处理绝对时间戳,因此,内核中导出了以下两个函数来获取绝对时间:
#include void do_gettimeofday(struct timeval *tv);struct timespec current_kernel_time(void);
延迟执行
设备驱动程序中经常在执行某个操作后需要等待一段时间,让硬件做后某些任务后再继续执行。
对于时间较长的延时(大于一个滴答时钟),可以使用等待队列的方式实现,等待队列的使用可以查看《Linux设备驱动程序》(九)——休眠与唤醒 :
wait_queue_head_t wait;init_waitqueue_head(&wait);wait_event_interrupt_timeout(wait, 0, delay);
上面的condition设为0,因为我们并不是在等待某个特定的事件;delay是超时的时间(为jiffies数量,而不是绝对时间)。因此,上面的代码会进入休眠,等待指定的jiffies数量后继续执行。
为了使用超时功能而定义了等待队里头,这是多余的,因为我们并不需要他。为了避免定义多余的变量,内核提供了以下方法实现延时:
#include set_current_state(TASK_INTERRUPTIBLE);schedule_timeout(delay);
通过set_current_state来设置当前进程的状态(如果是不可中断的设置为TASK_UNINTERRUPTIBLE),这样调度器超时后将其设置为TASK_RUNNING,该进程才会继续执行;如果没有设置进程状态,则进程状态一直都是TASK_RUNNING,此时后面执行schedule_timeout时,其效果等效于schedule,延时不会起作用。
对于短延迟,内核提供了以下几个函数来完成:
#include void ndelay(unsigned long nscs); /* 延迟指定的纳秒 */void udelay(unsigned long usecs); /* 延迟指定的微秒 */void mdelay(unsigned long msecs); /* 延迟指定的毫秒 */
这些函数的实现是与具体架构相关的,所有的架构都实现了udelay,其他函数可能没有定义,会在udelay的基础上提供默认的未定义的其他函数。
需要知道的是,以上延迟并不是说精确延时指定的时间,而是至少延迟指定的时间,可能会更长。
虽然输入的参数均为unsigned long类型的,但一般性的规则是只用在其指定的量级上,即上千纳秒应该使用udelay,而上千微秒则应使用mdelay,而不是输入一个很大的值。
以上的延迟都是忙等待函数,因此在延迟期间不能执行其他任务。
对于毫秒级以上的延迟,内核还提供了一种非忙等待的实现:
#include void msleep(unsigned int millisecs); /* 延时等地指定毫秒 */unsigned long msleep_interruptible(unsgined int millisecs); /* 返回剩余毫秒数,一般为0 */void ssleep(unsgined int seconds); /* 延时等待指定秒 */
以上介绍了内核时间的概念、如何获取内核时间以及在当前线程中的延时操作。下一节继续说如何进行异步延时操作。
eq linux_《Linux设备驱动程序》(十二)——时间操作(一)相关推荐
- Linux 设备驱动程序(二)
系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核(一) 深入理解 Linux 内核(二) Linux 设备驱动程序(一) Linux 设备驱动程序(二) Linux 设备驱动程序( ...
- Linux设备驱动程序(二)——建立和运行模块
文章目录 前言 一.设置测试系统 二.Hello World 模块 1.代码详解 2.执行效果 三.内核模块相比于应用程序 1.用户空间和内核空间 2.内核的并发 3.当前进程 4.几个别的细节 四. ...
- linux设备模型十二(热拔插hotplug)
我们知道,一个最简单的字符设备驱动应该是下面这个形式. #include <linux/fs.h> #include <linux/init.h> /* 定义一个open函数 ...
- Linux 设备驱动程序(三)
系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核(一) 深入理解 Linux 内核(二) Linux 设备驱动程序(一) Linux 设备驱动程序(二) Linux 设备驱动程序( ...
- c#获取当前时间 毫秒_《Linux设备驱动程序》(十二)——时间操作(一)
之前我们学会了如何编写一个字符设备,并对其中的一些重要操作进行了说明.对于一个完整的设备而已,可能还有许多工作要做. 本节我们将要说一下内核中是如何对时间问题进行操作的. 本节主要涉及到以下内容: 内 ...
- c++ helloworld_《Linux设备驱动程序》(二)——Hello World
Hello World是许多人开始写代码的时候第一个编写的例子,这里也延续传统,从Hello World开始. Hello World! 本节主要学会以下内容: 了解模块相关的内容:模块概念.模块编译 ...
- linux 内核 ide,Linux设备驱动程序学习(7)-内核的数据类型
Linux设备驱动程序学习(7)-内核的数据类型 由于前面的学习中有用到 第十一章 内核数据结构类型 的知识,所以我先看了.要点如下: 将linux 移植到新的体系结构时,开发者遇到的若干问题都与不正 ...
- Linux设备驱动程序-并发和竞态
并发及其管理 1.并发(concurrency)指的是多个执行单元同时.并行被执行,而并发的执行单元对共享资源(硬件资源和软件上的全局变量.静态变量等)的访问则很容易导致竞态(race conditi ...
- 嵌入式Linux设备驱动程序:用户空间中的设备驱动程序
嵌入式Linux设备驱动程序:用户空间中的设备驱动程序 Embedded Linux device drivers: Device drivers in user space Interfacing ...
最新文章
- 【MongoDB学习笔记21】MongoDB的复合索引
- oracle 时间集合,oracle 日期函数集合(集中版本)第2/2页
- oracle kepserve,Kepware.KEPServer\KEPServerEX_V5操作简介含opc quick client 连接测试
- redis rdb aof区别_Redis的持久化机制:RDB和AOF
- OpenCV_Corner Detect with FastFeatureDetector(基于FAST的角点检测) 及 SUSAN算子
- 在ASP.NET MVC中使用“RadioButtonList”和“CheckBoxList”
- tplink查看上网记录_Tplink路由器PPPOE拨号不能上网日志查看原因
- 数据结构 c语言 试卷,数据结构(C语言)试卷(1)
- 利用谷歌浏览器翻译外文文献 操作记录
- ARM最强CPU/GPU来了!A75、G72首发:性能爆炸
- Json与XML在线互转工具
- 小老弟!听说你在搞Android 10.0 适配,看这篇就妥了!
- 樱花樱花想见你:关于不一样的爱
- Hibernate_基于Annotation的使用_OneToOne_Demo(不维护关系那方(mappedBy=person))
- 计算机大学生毕设网页设计: 大学生体育运动网页设计模板代码 校园排球网页作业成品 学校排球网页制作模板 学生简单体育运动网站设计成品
- 精益生产方式有哪些?精益生产方式的核心是什么?
- 想成为富人,你得攒资产
- 行业软件下载-平面设计、视频编辑、建模渲染、文字识别、思维导图...
- 菜鸟站长之家给大家讲讲WordPress百度云cdn加速教程方法,并且免费版支持 HTTPS了
- linux rcp与scp的区别,好用的SCP RCP