📄 ch07.html
字号:
<html xmlns:cf="http://docbook.sourceforge.net/xmlns/chunkfast/1.0"><head><meta http-equiv="Content-Type" content="text/html; charset=gb2312"><title>第 7 章 时间, 延时, 和延后工作-Linux设备驱动第三版(中文版)- - </title><meta name="description" content="驱动开发- - " /><meta name="keywords" content="Linux设备驱动,中文版,第三版,ldd,linux device driver,驱动开发,电子版,程序设计,软件开发, " /><meta name="author" content=" www.21cstar.com QQ:610061171" /> <meta name="verify-v1" content="5asbXwkS/Vv5OdJbK3Ix0X8osxBUX9hutPyUxoubhes=" /><link rel="stylesheet" href="docbook.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.69.0"><link rel="start" href="index.html" title="Linux 设备驱动 Edition 3"><link rel="up" href="index.html" title="Linux 设备驱动 Edition 3"><link rel="prev" href="ch06s07.html" title="6.7. 快速参考"><link rel="next" href="ch07s02.html" title="7.2. 获知当前时间"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">第 7 章 时间, 延时, 和延后工作</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch06s07.html">上一页</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="ch07s02.html">下一页</a></td></tr></table><hr></div><div class="chapter" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title"><a name="TimeDelayandDeferredWork.chap"></a>第 7 章 时间, 延时, 和延后工作</h2></div></div></div><div class="toc"><p><b>目录</b></p><dl><dt><span class="sect1"><a href="ch07.html#MeasuringTimeLapes.sect">7.1. 测量时间流失</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch07.html#UsingthejiffesCounter.sect">7.1.1. 使用 jiffies 计数器</a></span></dt><dt><span class="sect2"><a href="ch07.html#PorcessorSpecificRegisters.sect">7.1.2. 处理器特定的寄存器</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch07s02.html">7.2. 获知当前时间</a></span></dt><dt><span class="sect1"><a href="ch07s03.html">7.3. 延后执行</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch07s03.html#LongDelays.sect">7.3.1. 长延时</a></span></dt><dt><span class="sect2"><a href="ch07s03.html#ShortDelays.sect">7.3.2. 短延时</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch07s04.html">7.4. 内核定时器</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch07s04.html#TheTimerAPI.sect">7.4.1. 定时器 API</a></span></dt><dt><span class="sect2"><a href="ch07s04.html#TheImplementaionofKernelTimers.sect">7.4.2. 内核定时器的实现</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch07s05.html">7.5. Tasklets 机制</a></span></dt><dt><span class="sect1"><a href="ch07s06.html">7.6. 工作队列</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch07s06.html#TheSharedQueue.sect">7.6.1. 共享队列</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch07s07.html">7.7. 快速参考</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch07s07.html#Timekeeping.sect">7.7.1. 时间管理</a></span></dt><dt><span class="sect2"><a href="ch07s07.html#Delays.sect">7.7.2. 延迟</a></span></dt><dt><span class="sect2"><a href="ch07s07.html#KernelTimers.sect">7.7.3. 内核定时器</a></span></dt><dt><span class="sect2"><a href="ch07s07.html#Taskletsqr.sect">7.7.4. Tasklets 机制</a></span></dt><dt><span class="sect2"><a href="ch07s07.html#workqueueskr.sect">7.7.5. 工作队列</a></span></dt></dl></dd></dl></div><p>到此, 我们知道了如何编写一个全特性字符模块的基本知识. 真实世界的驱动, 然而, 需要做比实现控制一个设备的操作更多的事情; 它们不得不处理诸如定时, 内存管理, 硬件存取, 等更多. 幸运的是, 内核输出了许多设施来简化驱动编写者的任务. 在下几章中, 我们将描述一些你可使用的内核资源. 这一章引路, 通过描述定时问题是如何阐述. 处理时间问题包括下列任务, 按照复杂度上升的顺序:</p><div class="itemizedlist"><ul type="disc"><li><p>测量时间流失和比较时间</p></li><li><p>知道当前时间</p></li><li><p>指定时间量的延时操作</p></li><li><p>调度异步函数在之后的时间发生</p></li></ul></div><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="MeasuringTimeLapes.sect"></a>7.1. 测量时间流失</h2></div></div></div><p>内核通过定时器中断来跟踪时间的流动. 中断在第 10 章详细描述.</p><p>定时器中断由系统定时硬件以规律地间隔产生; 这个间隔在启动时由内核根据 HZ 值来编程, HZ 是一个体系依赖的值, 在 <linux/param.h>中定义或者它所包含的一个子平台文件中. 在发布的内核源码中的缺省值在真实硬件上从 50 到 1200 嘀哒每秒, 在软件模拟器中往下到 24. 大部分平台运行在 100 或者 1000 中断每秒; 流行的 x86 PC 缺省是 1000, 尽管它在以前版本上(向上直到并且包括 2.4)常常是 100. 作为一个通用的规则, 即便如果你知道 HZ 的值, 在编程时你应当从不依赖这个特定值.</p><p>可能改变 HZ 的值, 对于那些要系统有一个不同的时钟中断频率的人. 如果你在头文件中改变 HZ 的值, 你需要使用新的值重编译内核和所有的模块. 如果你愿意付出额外的时间中断的代价来获得你的目标, 你可能想提升 HZ 来得到你的异步任务的更细粒度的精度. 实际上, 提升 HZ 到 1000 在使用 2.4 或 2.2 内核版本的 x86 工业系统中是相当普遍的. 但是, 对于当前版本, 最好的方法是保持 HZ 的缺省值, 由于我们完全信任内核开发者, 他们肯定已经选择了最好的值. 另外, 一些内部计算当前实现为只为从 12 到 1535 范围的 HZ (见 <linux/timex.h> 和 RFC-1589).</p><p>每次发生一个时钟中断, 一个内核计数器的值递增. 这个计数器在系统启动时初始化为 0, 因此它代表从最后一次启动以来的时钟嘀哒的数目. 这个计数器是一个 64-位 变量( 即便在 32-位的体系上)并且称为 jiffies_64. 但是, 驱动编写者正常地存取 jiffies 变量, 一个 unsigned long, 或者和 jiffies_64 是同一个或者它的低有效位. 使用 jiffies 常常是首选, 因为它更快, 并且再所有的体系上存取 64-位的 jiffies_64 值不必要是原子的. </p><p>除了低精度的内核管理的 jiffy 机制, 一些 CPU 平台特有一个高精度的软件可读的计数器. 尽管它的实际使用有些在各个平台不同, 它有时是一个非常有力的工具.</p><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="UsingthejiffesCounter.sect"></a>7.1.1. 使用 jiffies 计数器</h3></div></div></div><p>这个计数器和来读取它的实用函数位于 <linux/jiffies.h>, 尽管你会常常只是包含 <linux/sched.h>, 它会自动地将 jiffies.h 拉进来. 不用说, jiffies 和 jiffies_64 必须当作只读的.</p><p>无论何时你的代码需要记住当前的 jiffies 值, 可以简单地存取这个 unsigned long 变量, 它被声明做 volatile 来告知编译器不要优化内存读. 你需要读取当前的计数器, 无论何时你的代码需要计算一个将来的时间戳, 如下面例子所示:</p><pre class="programlisting">#include <linux/jiffies.h>unsigned long j, stamp_1, stamp_half, stamp_n;j = jiffies; /* read the current value */stamp_1 = j + HZ; /* 1 second in the future */stamp_half = j + HZ/2; /* half a second */stamp_n = j + n * HZ / 1000; /* n milliseconds */</pre><p>这个代码对于 jiffies 回绕没有问题, 只要不同的值以正确的方式进行比较. 尽管在 32-位 平台上当 HZ 是 1000 时, 计数器只是每 50 天回绕一次, 你的代码应当准备面对这个事件. 为比较你的被缓存的值( 象上面的 stamp_1 ) 和当前值, 你应当使用下面一个宏定义:</p>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -