之前我们学会了如何编写一个字符设备,并对其中的一些重要操作进行了说明。对于一个完整的设备而已,可能还有许多工作要做。

本节我们将要说一下内核中是如何对时间问题进行操作的。

本节主要涉及到以下内容:

  • 内核中的时间描述;
  • 如何获取当前时间;
  • 如何进行延时操作;

内核中的时间描述

在内核中,系统定时硬件以周期性的间隔产生中断,内核通过这个中断来跟踪时间流。

在内核中,上面的中断间隔是由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设备驱动程序》(十二)——时间操作(一)相关推荐

  1. Linux 设备驱动程序(二)

    系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核(一) 深入理解 Linux 内核(二) Linux 设备驱动程序(一) Linux 设备驱动程序(二) Linux 设备驱动程序( ...

  2. Linux设备驱动程序(二)——建立和运行模块

    文章目录 前言 一.设置测试系统 二.Hello World 模块 1.代码详解 2.执行效果 三.内核模块相比于应用程序 1.用户空间和内核空间 2.内核的并发 3.当前进程 4.几个别的细节 四. ...

  3. linux设备模型十二(热拔插hotplug)

    我们知道,一个最简单的字符设备驱动应该是下面这个形式. #include <linux/fs.h> #include <linux/init.h> /* 定义一个open函数 ...

  4. Linux 设备驱动程序(三)

    系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核(一) 深入理解 Linux 内核(二) Linux 设备驱动程序(一) Linux 设备驱动程序(二) Linux 设备驱动程序( ...

  5. c#获取当前时间 毫秒_《Linux设备驱动程序》(十二)——时间操作(一)

    之前我们学会了如何编写一个字符设备,并对其中的一些重要操作进行了说明.对于一个完整的设备而已,可能还有许多工作要做. 本节我们将要说一下内核中是如何对时间问题进行操作的. 本节主要涉及到以下内容: 内 ...

  6. c++ helloworld_《Linux设备驱动程序》(二)——Hello World

    Hello World是许多人开始写代码的时候第一个编写的例子,这里也延续传统,从Hello World开始. Hello World! 本节主要学会以下内容: 了解模块相关的内容:模块概念.模块编译 ...

  7. linux 内核 ide,Linux设备驱动程序学习(7)-内核的数据类型

    Linux设备驱动程序学习(7)-内核的数据类型 由于前面的学习中有用到 第十一章 内核数据结构类型 的知识,所以我先看了.要点如下: 将linux 移植到新的体系结构时,开发者遇到的若干问题都与不正 ...

  8. Linux设备驱动程序-并发和竞态

    并发及其管理 1.并发(concurrency)指的是多个执行单元同时.并行被执行,而并发的执行单元对共享资源(硬件资源和软件上的全局变量.静态变量等)的访问则很容易导致竞态(race conditi ...

  9. 嵌入式Linux设备驱动程序:用户空间中的设备驱动程序

    嵌入式Linux设备驱动程序:用户空间中的设备驱动程序 Embedded Linux device drivers: Device drivers in user space Interfacing ...

最新文章

  1. 【MongoDB学习笔记21】MongoDB的复合索引
  2. oracle 时间集合,oracle 日期函数集合(集中版本)第2/2页
  3. oracle kepserve,Kepware.KEPServer\KEPServerEX_V5操作简介含opc quick client 连接测试
  4. redis rdb aof区别_Redis的持久化机制:RDB和AOF
  5. OpenCV_Corner Detect with FastFeatureDetector(基于FAST的角点检测) 及 SUSAN算子
  6. 在ASP.NET MVC中使用“RadioButtonList”和“CheckBoxList”
  7. tplink查看上网记录_Tplink路由器PPPOE拨号不能上网日志查看原因
  8. 数据结构 c语言 试卷,数据结构(C语言)试卷(1)
  9. 利用谷歌浏览器翻译外文文献 操作记录
  10. ARM最强CPU/GPU来了!A75、G72首发:性能爆炸
  11. Json与XML在线互转工具
  12. 小老弟!听说你在搞Android 10.0 适配,看这篇就妥了!
  13. 樱花樱花想见你:关于不一样的爱
  14. Hibernate_基于Annotation的使用_OneToOne_Demo(不维护关系那方(mappedBy=person))
  15. 计算机大学生毕设网页设计: 大学生体育运动网页设计模板代码 校园排球网页作业成品 学校排球网页制作模板 学生简单体育运动网站设计成品
  16. 精益生产方式有哪些?精益生产方式的核心是什么?
  17. 想成为富人,你得攒资产
  18. 行业软件下载-平面设计、视频编辑、建模渲染、文字识别、思维导图...
  19. 菜鸟站长之家给大家讲讲WordPress百度云cdn加速教程方法,并且免费版支持 HTTPS了
  20. linux rcp与scp的区别,好用的SCP RCP

热门文章

  1. 炫酷实用 7款jQuery/HTML5图片应用
  2. JSP 中的几种注释
  3. mcafee安装是出现1920错误
  4. Hadoop教程(三)HDFS文件系统Shell命令
  5. Spring验证示例 - Spring MVC Form Validator
  6. 【服务端渲染】之 Vue SSR
  7. 【Python】Python库之图形艺术
  8. 【C语言】scanf()输入浮点型数据
  9. 比特飞使用的是什么主题
  10. C#LeetCode刷题之#231-2的幂(Power of Two)