摘要:
我们已经几次提及 shortprint 驱动; 现在是时候真正看看. 这个模块为并口实现一个非 常简单, 面向输出的驱动; 它是足够的, 但是, 来使能文件打印. 如果你选择来测试这个 驱动, 但是, 记住你必须传递给打印机一个文件以它理解的格式; 不是所有的打印机在给 一个任意数据的流时很好响应. 阅读全文
posted @ 2019-07-06 12:21
樊伟胜
阅读(472)
评论(0)
推荐(0)
摘要:
在系统中安装共享处理者不影响 /proc/stat, 它甚至不知道处理者. 但是, /proc/interrupts 稍稍变化. 所有同一个中断号的安装的处理者出现在 /proc/interrupts 的同一行. 下列输出( 从一 个 x86_64 系统)显示了共享中断处理是如何显示的: CPU0 阅读全文
posted @ 2019-07-06 12:20
樊伟胜
阅读(421)
评论(0)
推荐(0)
摘要:
如同前面建议的, 当内核收到一个中断, 所有的注册的处理者被调用. 一个共享的处理者 必须能够在它需要的处理的中断和其他设备产生的中断之间区分. 使用 shared=1 选项来加载 short 安装了下列处理者来代替缺省的: irqreturn_t short_sh_interrupt(int ir 阅读全文
posted @ 2019-07-06 12:19
樊伟胜
阅读(163)
评论(0)
推荐(0)
摘要:
共享中断通过 request_irq 来安装就像不共享的一样, 但是有 2 个不同: SA_SHIRQ 位必须在 flags 参数中指定, 当请求中断时. dev_id 参数必须是独特的. 任何模块地址空间的指针都行, 但是 dev_id 明确地 不能设置为 NULL. 内核保持着一个与中断相关联的 阅读全文
posted @ 2019-07-06 12:14
樊伟胜
阅读(116)
评论(0)
推荐(0)
摘要:
记住 tasklet 是一个特殊的函数, 可能被调度来运行, 在软中断上下文, 在一个系统决 定的安全时间中. 它们可能被调度运行多次, 但是 tasklet 调度不累积; ; tasklet 只 运行一次, 即便它在被投放前被重复请求. 没有 tasklet 会和它自己并行运行, 因为它 只运行一 阅读全文
posted @ 2019-07-06 12:13
樊伟胜
阅读(2611)
评论(0)
推荐(0)
摘要:
如果你需要禁止所有中断如何? 在 2.6 内核, 可能关闭在当前处理器上所有中断处理, 使用任一个下面 2 个函数(定义在 <asm/system.h>): 如果你需要禁止所有中断如何? 在 2.6 内核, 可能关闭在当前处理器上所有中断处理, 使用任一个下面 2 个函数(定义在 <asm/syst 阅读全文
posted @ 2019-07-06 12:11
樊伟胜
阅读(2202)
评论(0)
推荐(0)
摘要:
有时(但是很少!)一个驱动需要禁止一个特定中断线的中断递交. 内核提供了 3 个函数为 此目的, 所有都声明在 <asm/irq.h>. 这些函数是内核 API 的一部分, 因此我们描述它 们, 但是它们的使用在大部分驱动中不鼓励. 在其他的中, 你不能禁止共享的中断线, 并 且, 在现代的系统中, 阅读全文
posted @ 2019-07-06 12:10
樊伟胜
阅读(946)
评论(0)
推荐(0)
摘要:
老版本的 Linux 内核尽了很大努力来区分"快速"和"慢速"中断. 快速中断是那些能够很 快处理的, 而处理慢速中断要特别地长一些. 慢速中断可能十分苛求处理器, 并且它值得 在处理的时候重新使能中断. 否则, 需要快速注意的任务可能被延时太长. 在现代内核中, 快速和慢速中断的大部分不同已经消失 阅读全文
posted @ 2019-07-06 11:59
樊伟胜
阅读(658)
评论(0)
推荐(0)
摘要:
探测也可以在驱动自身实现没有太大麻烦. 它是一个少有的驱动必须实现它自己的探测, 但是看它是如何工作的能够给出对这个过程的内部认识. 为此目的, short 模块进行 do- it-yourself 的 IRQ 线探测, 如果它使用 probe=2 加载. 这个机制与前面描述的相同: 使能所有未使用 阅读全文
posted @ 2019-07-06 11:58
樊伟胜
阅读(140)
评论(0)
推荐(0)
摘要:
Linux 内核提供了一个低级设施来探测中断号. 它只为非共享中断, 但是大部分能够在共 享中断状态工作的硬件提供了更好的方法来尽量发现配置的中断号.这个设施包括 2 个函 数, 在<linux/interrupt.h> 中声明( 也描述了探测机制 ). unsigned long probe_ir 阅读全文
posted @ 2019-07-06 11:57
樊伟胜
阅读(243)
评论(0)
推荐(0)
摘要:
驱动在初始化时最有挑战性的问题中的一个是如何决定设备要使用哪个 IRQ 线. 驱动需 要信息来正确安装处理. 尽管程序员可用请求用户在加载时指定中断号, 这是个坏做法, 因为大部分时间用户不知道这个号, 要么因为他不配置跳线要么因为设备是无跳线的. 大 驱动在初始化时最有挑战性的问题中的一个是如何决 阅读全文
posted @ 2019-07-06 11:56
樊伟胜
阅读(435)
评论(0)
推荐(0)
摘要:
无论何时一个硬件中断到达处理器, 一个内部的计数器递增, 提供了一个方法来检查设备 是否如希望地工作. 报告的中断显示在 /proc/interrupts. 下面的快照取自一个双处理 器 Pentium 系统: root@montalcino:/bike/corbet/write/ldd3/src/ 阅读全文
posted @ 2019-07-06 11:55
樊伟胜
阅读(658)
评论(0)
推荐(0)
摘要:
如果你想实际地"看到"产生的中断, 向硬件设备写不足够; 一个软件处理必须在系统中配 置. 如果 Linux 内核还没有被告知来期待你的中断, 它简单地确认并忽略它. 中断线是一个宝贵且常常有限的资源, 特别当它们只有 15 或者 16 个时. 内核保持了中 断线的一个注册, 类似于 I/O 端口的 阅读全文
posted @ 2019-07-06 11:54
樊伟胜
阅读(444)
评论(0)
推荐(0)
摘要:
一个最著名的 I/O 内存区是在个人计算机上的 ISA 范围. 这是在 640 KB(0xA0000)和 1 MB(0x100000)之间的内存范围. 因此, 它正好出现于常规内存 RAM 中间. 这个位置可能 看起来有点奇怪; 它是一个在 1980 年代早期所作的决定的产物, 当时 640 KB 阅读全文
posted @ 2019-07-06 11:53
樊伟胜
阅读(491)
评论(0)
推荐(0)
摘要:
short 例子模块, 在存取 I/O 端口前介绍的, 也能用来存取 I/O 内存. 为此, 你必须告 诉它使用 I/O 内存在加载时; 还有, 你需要改变基地址来使它指向你的 I/O 区. 例如, 这是我们如何使用 short 来点亮调试 LED, 在一个 MIPS 开发板上: mips.root 阅读全文
posted @ 2019-07-06 11:52
樊伟胜
阅读(270)
评论(0)
推荐(0)
摘要:
在一些平台上, 你可能逃过作为一个指针使用 ioremap 的返回值的惩罚. 这样的使用不 是可移植的, 并且, 更加地, 内核开发者已经努力来消除任何这样的使用. 使用 I/O 内 存的正确方式是通过一系列为此而提供的函数(通过 <asm/io.h> 定义的). 从 I/O 内存读, 使用下列之一 阅读全文
posted @ 2019-07-06 11:51
樊伟胜
阅读(285)
评论(0)
推荐(0)
摘要:
我们介绍的驱动称为 short (Simple Hardware Operations and Raw Tests). 所有它做 的是读和写几个 8-位 端口, 从你在加载时选择的开始. 缺省地, 它使用分配给 PC 并口 的端口范围. 每个设备节点(有一个独特的次编号)存取一个不同的端口. sho 阅读全文
posted @ 2019-07-06 11:50
樊伟胜
阅读(486)
评论(0)
推荐(0)
摘要:
I/O 内存区必须在使用前分配. 分配内存区的接口是( 在 <linux/ioport.h> 定义): struct resource *request_mem_region(unsigned long start, unsigned long len, char *name); 这个函数分配一个 阅读全文
posted @ 2019-07-06 11:50
樊伟胜
阅读(531)
评论(0)
推荐(0)
摘要:
刚刚描述的这些函数主要打算被设备驱动使用, 但它们也可从用户空间使用, 至少在 PC- 类 的计算机. GNU C 库在 <sys/io.h> 中定义它们. 下列条件应当应用来对于 inb 及其 友在用户空间代码中使用: 程序必须使用 -O 选项编译来强制扩展内联函数. ioperm 和 iopl 阅读全文
posted @ 2019-07-06 11:49
樊伟胜
阅读(458)
评论(0)
推荐(0)
摘要:
因为我们期望大部分读者以所谓的"个人计算机"的形式使用一个 x86 平台, 我们觉得值 得解释一下 PC 并口如何设计的. 并口是在个人计算机上运行数字 I/O 例子代码的外设 接口选择. 尽管大部分读者可能有并口规范用, 为你的方便, 我们在这里总结一下它们. 并口, 在它的最小配置中 ( 我们浏 阅读全文
posted @ 2019-07-06 11:49
樊伟胜
阅读(614)
评论(0)
推荐(0)
摘要:
在驱动硬件请求了在它的活动中需要使用的 I/O 端口范围之后, 它必须读且/或写到这些 端口. 为此, 大部分硬件区别 8-位, 16-位, 和 32-位端口. 常常你无法混合它们, 象你 正常使用系统内存存取一样.[33]33 一个 C 程序, 因此, 必须调用不同的函数来存取不同大小的端口. 如 阅读全文
posted @ 2019-07-06 11:48
樊伟胜
阅读(585)
评论(0)
推荐(0)
摘要:
如同你可能希望的, 你不应当离开并开始抨击 I/O 端口而没有首先确认你对这些端口有 唯一的权限. 内核提供了一个注册接口以允许你的驱动来声明它需要的端口. 这个接口中 的核心的函数是 request_region: #include <linux/ioport.h> struct resource 阅读全文
posted @ 2019-07-06 11:43
樊伟胜
阅读(792)
评论(0)
推荐(0)
摘要:
不管硬件寄存器和内存之间的强相似性, 存取 I/O 寄存器的程序员必须小心避免被 CPU(或者编译器)优化所戏弄, 它可能修改希望的 I/O 行为. I/O 寄存器和 RAM 的主要不同是 I/O 操作有边际效果, 而内存操作没有: 一个内存写的 唯一效果是存储一个值到一个位置, 并且一个内存读返回 阅读全文
posted @ 2019-07-06 11:30
樊伟胜
阅读(973)
评论(0)
推荐(0)
摘要:
每个外设都是通过读写它的寄存器来控制. 大部分时间一个设备有几个寄存器, 并且在连 续地址存取它们, 或者在内存地址空间或者在 I/O 地址空间. 在硬件级别上, 内存区和 I/O 区域没有概念上的区别: 它们都是通过在地址总线和控制 总线上发出电信号来存取(即, 读写信号)[32]32并且读自或者 阅读全文
posted @ 2019-07-06 11:29
樊伟胜
阅读(300)
评论(0)
推荐(0)
摘要:
如果你真的需要一个大的物理上连续的缓冲, 最好的方法是在启动时请求内存来分配它. 在启动时分配是获得连续内存页而避开 get_free_pages 施加的对缓冲大小限制的唯一 方法, 不但最大允许大小还有限制的大小选择. 在启动时分配内存是一个"脏"技术, 因为 它绕开了所有的内存管理策略通过保留一 阅读全文
posted @ 2019-07-06 11:28
樊伟胜
阅读(172)
评论(0)
推荐(0)
摘要:
每-CPU 变量是一个有趣的 2.6 内核的特性. 当你创建一个每-CPU 变量, 系统中每个处理 器获得它自己的这个变量拷贝. 这个可能象一个想做的奇怪的事情, 但是它有自己的优点. 存取每-CPU 变量不需要(几乎)加锁, 因为每个处理器使用它自己的拷贝. 每-CPU 变量也 可存在于它们各自的 阅读全文
posted @ 2019-07-06 11:27
樊伟胜
阅读(379)
评论(0)
推荐(0)
摘要:
我们展示给你的下一个内存分配函数是 vmlloc, 它在虚拟内存空间分配一块连续的内存 区. 尽管这些页在物理内存中不连续 (使用一个单独的对 alloc_page 的调用来获得每个 页), 内核看它们作为一个一个连续的地址范围. vmalloc 返回 0 ( NULL 地址 ) 如果发 生一个错误 阅读全文
posted @ 2019-07-06 11:26
樊伟胜
阅读(423)
评论(0)
推荐(0)
摘要:
为完整起见, 我们介绍另一个内存分配的接口, 尽管我们不会准备使用它直到 15 章. 现 在, 能够说 struct page 是一个描述一个内存页的内部内核结构. 如同我们将见到的, 在内核中有许多地方有必要使用页结构; 它们是特别有用的, 在任何你可能处理高端内存 的情况下, 高端内存在内核空间 阅读全文
posted @ 2019-07-06 11:25
樊伟胜
阅读(1140)
评论(0)
推荐(0)
摘要:
为了真实地测试页分配, 我们已随其他例子代码发布了 scullp 模块. 它是一个简化的 scull, 就像前面介绍过的 scullc. scullp 分配的内存量子是整页或者页集合: scullp_order 变量缺省是 0, 但是可以在编 译或加载时改变. 下列代码行显示了它如何分配内存: /* 阅读全文
posted @ 2019-07-06 11:24
樊伟胜
阅读(277)
评论(0)
推荐(0)
摘要:
如果一个模块需要分配大块的内存, 它常常最好是使用一个面向页的技术. 请求整个页也 有其他的优点, 这个在 15 章介绍. 为分配页, 下列函数可用: get_zeroed_page(unsigned int flags); 返回一个指向新页的指针并且用零填充了该页. get_free_page(u 阅读全文
posted @ 2019-07-06 11:23
樊伟胜
阅读(569)
评论(0)
推荐(0)
摘要:
在内核中有不少地方内存分配不允许失败. 作为一个在这些情况下确保分配的方式, 内核 开发者创建了一个已知为内存池(或者是 "mempool" )的抽象. 一个内存池真实地只是一 类后备缓存, 它尽力一直保持一个空闲内存列表给紧急时使用. 一个内存池有一个类型 mempool_t ( 在 <linux 阅读全文
posted @ 2019-07-06 11:22
樊伟胜
阅读(917)
评论(0)
推荐(0)
摘要:
是时候给个例子了. scullc 是一个简化的 scull 模块的版本, 它只实现空设备 -- 永久 的内存区. 不象 scull, 它使用 kmalloc, scullc 使用内存缓存. 量子的大小可在编译 时和加载时修改, 但是不是在运行时 -- 这可能需要创建一个新内存区, 并且我们不想处 理 阅读全文
posted @ 2019-07-06 11:21
樊伟胜
阅读(267)
评论(0)
推荐(0)
摘要:
一个设备驱动常常以反复分配许多相同大小的对象而结束. 如果内核已经维护了一套相同 大小对象的内存池, 为什么不增加一些特殊的内存池给这些高容量的对象? 实际上, 内核 确实实现了一个设施来创建这类内存池, 它常常被称为一个后备缓存. 设备驱动常常不展 示这类的内存行为, 它们证明使用一个后备缓存是对 阅读全文
posted @ 2019-07-06 11:19
樊伟胜
阅读(240)
评论(0)
推荐(0)
摘要:
GFP_DMA 和 GFP_HIGHMEM 都有一个平台相关的角色, 尽管对所有平台它们的使用都 有效. Linux 内核知道最少 3 个内存区: DMA-能够 内存, 普通内存, 和高端内存. 尽管通常地 分配都发生于普通区, 设置这些刚刚提及的位的任一个请求从不同的区来分配内存. 这个 想法是, 阅读全文
posted @ 2019-07-06 11:17
樊伟胜
阅读(239)
评论(0)
推荐(0)
摘要:
记住 kmalloc 原型是: #include <linux/slab.h> void *kmalloc(size_t size, int flags); 给 kmalloc 的第一个参数是要分配的块的大小. 第 2 个参数, 分配标志, 非常有趣, 因 为它以几个方式控制 kmalloc 的行为 阅读全文
posted @ 2019-07-06 11:15
樊伟胜
阅读(1206)
评论(0)
推荐(0)
摘要:
#include <linux/wait.h> long wait_event_interruptible_timeout(wait_queue_head_t *q, condition, signed long timeout); 使当前进程在等待队列进入睡眠, 安装一个以 jiffies 表达的 阅读全文
posted @ 2019-07-06 11:14
樊伟胜
阅读(520)
评论(0)
推荐(0)
摘要:
#include <linux/param.h> HZ HZ 符号指定了每秒产生的时钟嘀哒的数目. #include <linux/jiffies.h> volatile unsigned long jiffies; u64 jiffies_64; jiffies_64 变量每个时钟嘀哒时被递增; 阅读全文
posted @ 2019-07-06 11:13
樊伟胜
阅读(144)
评论(0)
推荐(0)
摘要:
一个设备驱动, 在许多情况下, 不需要它自己的工作队列. 如果你只偶尔提交任务给队列, 简单地使用内核提供的共享的, 缺省的队列可能更有效. 如果你使用这个队列, 但是, 你 必须明白你将和别的在共享它. 从另一个方面说, 这意味着你不应当长时间独占队列(无 长睡眠), 并且可能要更长时间它们轮到处 阅读全文
posted @ 2019-07-06 11:12
樊伟胜
阅读(779)
评论(0)
推荐(0)
摘要:
工作队列是, 表面上看, 类似于 taskets; 它们允许内核代码来请求在将来某个时间调用 一个函数. 但是, 有几个显著的不同在这 2 个之间, 包括: tasklet 在软件中断上下文中运行的结果是所有的 tasklet 代码必须是原子的. 相反, 工作队列函数在一个特殊内核进程上下文运行; 阅读全文
posted @ 2019-07-06 11:11
樊伟胜
阅读(319)
评论(0)
推荐(0)
摘要:
tasklet 类似内核定时器在某些方面. 它们一直在中断时间运行, 它们一直运行在调度它 们的同一个 CPU 上, 并且它们接收一个 unsigned long 参数. 不象内核定时器, 但是, 你无法请求在一个指定的时间执行函数. 通过调度一个 tasklet, 你简单地请求它在以后 的一个由内 阅读全文
posted @ 2019-07-06 11:10
樊伟胜
阅读(357)
评论(0)
推荐(0)
摘要:
内核提供给驱动许多函数来声明, 注册, 以及去除内核定时器. 下列的引用展示了基本的 代码块: #include <linux/timer.h> struct timer_list { /* ... */ unsigned long expires; void (*function)(unsigne 阅读全文
posted @ 2019-07-06 11:09
樊伟胜
阅读(819)
评论(0)
推荐(0)
摘要:
为了使用它们, 尽管你不会需要知道内核定时器如何实现, 这个实现是有趣的, 并且值得 看一下它们的内部. 定时器的实现被设计来符合下列要求和假设: 定时器管理必须尽可能简化. 设计应当随着激活的定时器数目上升而很好地适应. 大部分定时器在几秒或最多几分钟内到时, 而带有长延时的定时器是相当少见. 一 阅读全文
posted @ 2019-07-06 11:09
樊伟胜
阅读(2146)
评论(0)
推荐(0)
摘要:
无论何时你需要调度一个动作以后发生, 而不阻塞当前进程直到到时, 内核定时器是给你 的工具. 这些定时器用来调度一个函数在将来一个特定的时间执行, 基于时钟嘀哒, 并且 可用作各类任务; 例如, 当硬件无法发出中断时, 查询一个设备通过在定期的间隔内检查 它的状态. 其他的内核定时器的典型应用是关闭 阅读全文
posted @ 2019-07-06 11:08
樊伟胜
阅读(906)
评论(0)
推荐(0)
摘要:
当一个设备驱动需要处理它的硬件的反应时间, 涉及到的延时常常是最多几个毫秒. 在这 个情况下, 依靠时钟嘀哒显然不对路. The kernel functions ndelay, udelay, and mdelay serve well for short delays, delaying exe 阅读全文
posted @ 2019-07-06 11:07
樊伟胜
阅读(344)
评论(0)
推荐(0)
摘要:
到目前为止所展示的次优化的延时循环通过查看 jiffy 计数器而不告诉任何人来工作. 但是最好的实现一个延时的方法, 如你可能猜想的, 常常是请求内核为你做. 有 2 种方 法来建立一个基于 jiffy 的超时, 依赖于是否你的驱动在等待其他的事件. 如果你的驱动使用一个等待队列来等待某些其他事件, 阅读全文
posted @ 2019-07-06 11:06
樊伟胜
阅读(491)
评论(0)
推荐(0)
摘要:
如我们已见到的, 忙等待强加了一个重负载给系统总体; 我们乐意找出一个更好的技术. 想到的第一个改变是明确地释放 CPU 当我们对其不感兴趣时. 这是通过调用调度函数而 实现地, 在 <linux/sched.h> 中声明: while (time_before(jiffies, j1)) { sc 阅读全文
posted @ 2019-07-06 11:05
樊伟胜
阅读(430)
评论(0)
推荐(0)
摘要:
设备驱动常常需要延后一段时间执行一个特定片段的代码, 常常允许硬件完成某个任务. 在这一节我们涉及许多不同的技术来获得延后. 每种情况的环境决定了使用哪种技术最好; 我们全都仔细检查它们, 并且指出每一个的长处和缺点. 一件要考虑的重要的事情是你需要的延时如何与时钟嘀哒比较, 考虑到 HZ 的跨各种 阅读全文
posted @ 2019-07-06 11:04
樊伟胜
阅读(587)
评论(0)
推荐(0)
摘要:
内核代码能一直获取一个当前时间的表示, 通过查看 jifies 的值. 常常地, 这个值只代 表从最后一次启动以来的时间, 这个事实对驱动来说无关, 因为它的生命周期受限于系统 的 uptime. 如所示, 驱动可以使用 jiffies 的当前值来计算事件之间的时间间隔(例如, 在输入驱动中从单击中 阅读全文
posted @ 2019-07-06 11:03
樊伟胜
阅读(431)
评论(0)
推荐(0)
摘要:
如果你需要测量非常短时间间隔, 或者你需要非常高精度, 你可以借助平台依赖的资源, 一个要精度不要移植性的选择. 在现代处理器中, 对于经验性能数字的迫切需求被大部分 CPU 设计中内在的指令定时不 确定性所阻碍, 这是由于缓存内存, 指令调度, 以及分支预测引起. 作为回应, CPU 制造 商引入 阅读全文
posted @ 2019-07-06 11:02
樊伟胜
阅读(385)
评论(0)
推荐(0)
摘要:
这个计数器和来读取它的实用函数位于 <linux/jiffies.h>, 尽管你会常常只是包含 <linux/sched.h>, 它会自动地将 jiffies.h 拉进来. 不用说, jiffies 和 jiffies_64 必须当作只读的. 无论何时你的代码需要记住当前的 jiffies 值, 可 阅读全文
posted @ 2019-07-06 11:01
樊伟胜
阅读(1137)
评论(0)
推荐(0)
摘要:
管理存取控制的另一个技术是创建设备的不同的私有拷贝, 根据打开它的进程. 明显地, 这只当设备没有绑定到一个硬件实体时有可能; scull 是一个这样的"软件"设备 的例子. /dev/tty 的内部使用类似的技术来给它的进程一个不同的 /dev 入口点呈现的 视图. 当设备的拷贝被软件驱动创建, 阅读全文
posted @ 2019-07-06 10:52
樊伟胜
阅读(266)
评论(0)
推荐(0)
摘要:
当设备不可存取, 返回一个错误常常是最合理的方法, 但是有些情况用户可能更愿意等待 设备. 例如, 如果一个数据通讯通道既用于规律地预期地传送报告(使用 crontab), 也用于根据 用户的需要偶尔地使用, 对于被安排的操作最好是稍微延迟, 而不是只是因为通道当前忙 而失败. 当设备不可存取, 返 阅读全文
posted @ 2019-07-06 10:47
樊伟胜
阅读(838)
评论(0)
推荐(0)
摘要:
单打开设备之外的下一步是使一个用户在多个进程中打开一个设备, 但是一次只允许一个 用户打开设备. 这个解决方案使得容易测试设备, 因为用户一次可从几个进程读写, 但是 假定这个用户负责维护在多次存取中的数据完整性. 这通过在 open 方法中添加检查来实 现; 这样的检查在通常的许可检查后进行, 并 阅读全文
posted @ 2019-07-06 10:46
樊伟胜
阅读(218)
评论(0)
推荐(0)
摘要:
llseek 方法实现了 lseek 和 llseek 系统调用. 我们已经说了如果 llseek 方法从设备 的操作中缺失, 内核中的缺省的实现进行移位通过修改 filp->f_pos, 这是文件中的当前 读写位置. 请注意对于 lseek 系统调用要正确工作, 读和写方法必须配合, 通过使用和 阅读全文
posted @ 2019-07-06 10:45
樊伟胜
阅读(672)
评论(0)
推荐(0)
摘要:
提供存取控制的强力方式是只允许一个设备一次被一个进程打开(单次打开). 这个技术最 好是避免因为它限制了用户的灵活性. 一个用户可能想运行不同的进程在一个设备上, 一 个读状态信息而另一个写数据. 在某些情况下, 用户通过一个外壳脚本运行几个简单的程 序可做很多事情, 只要它们可并发存取设备. 换句 阅读全文
posted @ 2019-07-06 10:45
樊伟胜
阅读(414)
评论(0)
推荐(0)
摘要:
poll 和 select 系统调用的真正实现是相当地简单, 对那些感兴趣于它如何工作的人; epoll 更加复杂一点但是建立在同样的机制上. 无论何时用户应用程序调用 poll, select, 或者 epoll_ctl,[24]24 内核调用这个系统调用所引用的所有文件的 poll 方法, 传递 阅读全文
posted @ 2019-07-06 10:43
樊伟胜
阅读(1724)
评论(0)
推荐(0)
摘要:
使用非阻塞 I/O 的应用程序常常使用 poll, select, 和 epoll 系统调用. poll, select 和 epoll 本质上有相同的功能: 每个允许一个进程来决定它是否可读或者写一个 或多个文件而不阻塞. 这些调用也可阻塞进程直到任何一个给定集合的文件描述符可用来 读或写. 因此 阅读全文
posted @ 2019-07-06 10:42
樊伟胜
阅读(898)
评论(0)
推荐(0)
摘要:
我们已经见到了 scullpipe 驱动如何实现阻塞 I/O. 如果你想试一试, 这个驱动的源码 可在剩下的本书例子中找到. 阻塞 I/O 的动作可通过打开 2 个窗口见到. 第一个可运行 一个命令诸如 cat /dev/scullpipe. 如果你接着, 在另一个窗口拷贝文件到 /dev/scul 阅读全文
posted @ 2019-07-06 10:41
樊伟胜
阅读(244)
评论(0)
推荐(0)
摘要:
我们已经见到当一个进程调用 wake_up 在等待队列上, 所有的在这个队列上等待的进程 被置为可运行的. 在许多情况下, 这是正确的做法. 但是, 在别的情况下, 可能提前知道 只有一个被唤醒的进程将成功获得需要的资源, 并且其余的将简单地再次睡眠. 每个这样 的进程, 但是, 必须获得处理器, 阅读全文
posted @ 2019-07-06 10:40
樊伟胜
阅读(623)
评论(0)
推荐(0)
摘要:
我们已展现的唤醒进程的样子比内核中真正发生的要简单. 当进程被唤醒时产生的真正动 作是被位于等待队列入口项的一个函数控制的. 缺省的唤醒函数[22]22设置进程为可运行的 状态, 并且可能地进行一个上下文切换到有更高优先级进程. 设备驱动应当从不需要提供 一个不同的唤醒函数; 如果你例外, 关于如何 阅读全文
posted @ 2019-07-06 10:40
樊伟胜
阅读(2576)
评论(0)
推荐(0)
摘要:
在 Linux 内核的之前的版本, 正式的睡眠要求程序员手动处理所有上面的步骤. 它是一 个繁琐的过程, 包含相当多的易出错的样板式的代码. 程序员如果愿意还是可能用那种方 式手动睡眠; <linux/sched.h> 包含了所有需要的定义, 以及围绕例子的内核源码. 但是, 有一个更容易的方式. 阅读全文
posted @ 2019-07-06 10:39
樊伟胜
阅读(482)
评论(0)
推荐(0)
摘要:
如果我们深入 <linux/wait.h>, 你见到在 wait_queue_head_t 类型后面的数据结构是非 常简单的; 它包含一个自旋锁和一个链表. 这个链表是一个等待队列入口, 它被声明做 wait_queue_t. 这个结构包含关于睡眠进程的信息和它想怎样被唤醒. 使一个进程睡眠的第一步 阅读全文
posted @ 2019-07-06 10:38
樊伟胜
阅读(978)
评论(0)
推荐(0)
摘要:
最后, 我们看一个实现了阻塞 I/O 的真实驱动方法的例子. 这个例子来自 scullpipe 驱 动; 它是 scull 的一个特殊形式, 实现了一个象管道的设备. 在驱动中, 一个阻塞在读调用上的进程被唤醒, 当数据到达时; 常常地硬件发出一个中断 来指示这样一个事件, 并且驱动唤醒等待的进程作 阅读全文
posted @ 2019-07-06 10:36
樊伟胜
阅读(759)
评论(0)
推荐(0)
摘要:
在我们看全功能的 read 和 write 方法的实现之前, 我们触及的最后一点是决定何时使 进程睡眠. 有时实现正确的 unix 语义要求一个操作不阻塞, 即便它不能完全地进行下去. 有时还有调用进程通知你他不想阻塞, 不管它的 I/O 是否继续. 明确的非阻塞 I/O 由 filp->f_fla 阅读全文
posted @ 2019-07-06 10:31
樊伟胜
阅读(2693)
评论(0)
推荐(0)
摘要:
当一个进程睡眠, 它这样做以期望某些条件在以后会成真. 如我们之前注意到的, 任何睡 眠的进程必须在它再次醒来时检查来确保它在等待的条件真正为真. Linux 内核中睡眠的 最简单方式是一个宏定义, 称为 wait_event(有几个变体); 它结合了处理睡眠的细节和 进程在等待的条件的检查. wa 阅读全文
posted @ 2019-07-06 10:30
樊伟胜
阅读(606)
评论(0)
推荐(0)
摘要:
对于一个进程"睡眠"意味着什么? 当一个进程被置为睡眠, 它被标识为处于一个特殊的状 态并且从调度器的运行队列中去除. 直到发生某些事情改变了那个状态, 这个进程将不被 在任何 CPU 上调度, 并且, 因此, 将不会运行. 一个睡着的进程已被搁置到系统的一边, 等待以后发生事件. 对于一个 Lin 阅读全文
posted @ 2019-07-06 10:29
樊伟胜
阅读(2750)
评论(0)
推荐(0)
摘要:
有时控制设备最好是通过写控制序列到设备自身来实现. 例如, 这个技术用在控制台驱动 中, 这里所谓的 escape 序列被用来移动光标, 改变缺省的颜色, 或者进行其他的配置任 务. 这样实现设备控制的好处是用户可仅仅通过写数据控制设备, 不必使用(或者有时候 写)只为配置设备而建立的程序. 当设备 阅读全文
posted @ 2019-07-06 10:28
樊伟胜
阅读(273)
评论(0)
推荐(0)
摘要:
ioctl 的 scull 实现只传递设备的配置参数, 并且象下面这样容易: switch(cmd) { case SCULL_IOCRESET: scull_quantum = SCULL_QUANTUM; scull_qset = SCULL_QSET; break; case SCULL_IO 阅读全文
posted @ 2019-07-06 10:26
樊伟胜
阅读(663)
评论(0)
推荐(0)
摘要:
在看 scull 驱动的 ioctl 代码之前, 我们需要涉及的另一点是如何使用这个额外的参数. 如果它是一个整数, 就容易: 它可以直接使用. 如果它是一个指针, 但是, 必须小心些. 当用一个指针引用用户空间, 我们必须确保用户地址是有效的. 试图存取一个没验证过的 用户提供的指针可能导致不正确 阅读全文
posted @ 2019-07-06 10:25
樊伟胜
阅读(1884)
评论(0)
推荐(0)
摘要:
尽管 ioctl 系统调用最常用来作用于设备, 内核能识别几个命令. 注意这些命令, 当用 到你的设备时, 在你自己的文件操作被调用之前被解码. 因此, 如果你选择相同的号给一 个你的 ioctl 命令, 你不会看到任何的给那个命令的请求, 并且应用程序获得某些不期望 的东西, 因为在 ioctl 阅读全文
posted @ 2019-07-06 10:24
樊伟胜
阅读(469)
评论(0)
推荐(0)
摘要:
在为 ioctl 编写代码之前, 你需要选择对应命令的数字. 许多程序员的第一个本能的反 应是选择一组小数从0或1 开始, 并且从此开始向上. 但是, 有充分的理由不这样做. ioctl 命令数字应当在这个系统是唯一的, 为了阻止向错误的设备发出正确的命令而引起 的错误. 这样的不匹配不会不可能发生 阅读全文
posted @ 2019-07-06 10:12
樊伟胜
阅读(1517)
评论(0)
推荐(0)
摘要:
大部分驱动需要 -- 除了读写设备的能力 -- 通过设备驱动进行各种硬件控制的能力. 大 部分设备可进行超出简单的数据传输之外的操作; 用户空间必须常常能够请求, 例如, 设 备锁上它的门, 弹出它的介质, 报告错误信息, 改变波特率, 或者自我销毁. 这些操作常 常通过 ioctl 方法来支持, 阅读全文
posted @ 2019-07-06 10:11
樊伟胜
阅读(1570)
评论(0)
推荐(0)
摘要:
atomic_t 类型在进行整数算术时是不错的. 但是, 它无法工作的好, 当你需要以原子方 式操作单个位时. 为此, 内核提供了一套函数来原子地修改或测试单个位. 因为整个操作 在单步内发生, 没有中断(或者其他处理器)能干扰. 原子位操作非常快, 因为它们使用单个机器指令来进行操作, 而在任何时 阅读全文
posted @ 2019-07-06 10:10
樊伟胜
阅读(849)
评论(0)
推荐(0)
摘要:
内核包含了一对新机制打算来提供快速地, 无锁地存取一个共享资源. seqlock 在这 种情况下工作, 要保护的资源小, 简单, 并且常常被存取, 并且很少写存取但是必须要快. 基本上, 它们通过允许读者释放对资源的存取, 但是要求这些读者来检查与写者的冲突而 工作, 并且当发生这样的冲突时, 重试 阅读全文
posted @ 2019-07-06 10:10
樊伟胜
阅读(308)
评论(0)
推荐(0)
摘要:
有时, 一个共享资源是一个简单的整数值. 假设你的驱动维护一个共享变量 n_op, 它告 知有多少设备操作目前未完成. 正常地, 即便一个简单的操作例如: n_op++; 可能需要加锁. 某些处理器可能以原子的方式进行那种递减, 但是你不能依赖它. 但是一 个完整的加锁体制对于一个简单的整数值看来过 阅读全文
posted @ 2019-07-06 10:09
樊伟胜
阅读(995)
评论(0)
推荐(0)
摘要:
内核提供了一个自旋锁的读者/写者形式, 直接模仿我们在本章前面见到的读者/写者旗标. 这些锁允许任何数目的读者同时进入临界区, 但是写者必须是排他的存取. 读者写者锁有 一个类型 rwlock_t, 在 <linux/spinlokc.h> 中定义. 它们可以以 2 种方式被声明和被 初始化: rw 阅读全文
posted @ 2019-07-06 10:08
樊伟胜
阅读(252)
评论(0)
推荐(0)
摘要:
我们已经看到 2 个函数, spin_lock 和 spin_unlock, 可以操作自旋锁. 有其他几个函 数, 然而, 有类似的名子和用途. 我们现在会展示全套. 这个讨论将带我们到一个我们无 法在几章内适当涵盖的地方; 自旋锁 API 的完整理解需要对中断处理和相关概念的理解. 实际上有 4 阅读全文
posted @ 2019-07-06 10:01
樊伟胜
阅读(525)
评论(0)
推荐(0)
摘要:
自旋锁原语要求的包含文件是 <linux/spinlock.h>. 一个实际的锁有类型 spinlock_t. 象任何其他数据结构, 一个 自旋锁必须初始化. 这个初始化可以在编译时完成, 如下: 自旋锁原语要求的包含文件是 <linux/spinlock.h>. 一个实际的锁有类型 spinloc 阅读全文
posted @ 2019-07-06 09:48
樊伟胜
阅读(395)
评论(0)
推荐(0)
摘要:
内核编程的一个普通模式包括在当前线程之外初始化某个动作, 接着等待这个动作结束. 这个动作可能是创建一个新内核线程或者用户空间进程, 对一个存在着的进程的请求, 或 者一些基于硬件的动作. 在这些情况中, 很有诱惑去使用一个旗标来同步 2 个任务, 使 用这样的代码: struct semaphor 阅读全文
posted @ 2019-07-06 09:47
樊伟胜
阅读(448)
评论(0)
推荐(0)
摘要:
旗标为所有调用者进行互斥, 不管每个线程可能想做什么. 然而, 很多任务分为 2 种清 楚的类型: 只需要读取被保护的数据结构的类型, 和必须做改变的类型. 允许多个并发读 者常常是可能的, 只要没有人试图做任何改变. 这样做能够显著提高性能; 只读的任务可 以并行进行它们的工作而不必等待其他读者退 阅读全文
posted @ 2019-07-06 09:46
樊伟胜
阅读(351)
评论(0)
推荐(0)
摘要:
旗标机制给予 scull 一个工具, 可以在存取 scull_dev 数据结构时用来避免竞争情况. 但是正确使用这个工具是我们的责任. 正确使用加锁原语的关键是严密地指定要保护哪个 资源并且确认每个对这些资源的存取都使用了正确的加锁方法. 在我们的例子驱动中, 感 兴趣的所有东西都包含在 scull 阅读全文
posted @ 2019-07-06 09:36
樊伟胜
阅读(210)
评论(0)
推荐(0)
摘要:
Linux 内核提供了一个遵守上面语义的旗标实现, 尽管术语有些不同. 为使用旗标, 内核 代码必须包含 <asm/semaphore.h>. 相关的类型是 struct semaphore; 实际旗标可以用 几种方法来声明和初始化. 一种是直接创建一个旗标, 接着使用 sema_init 来设定它 阅读全文
posted @ 2019-07-06 09:35
樊伟胜
阅读(579)
评论(0)
推荐(0)
摘要:
让我们快速看一段 scull 内存管理代码. 在写逻辑的深处, scull 必须决定它请求的内 存是否已经分配. 处理这个任务的代码是: if (!dptr->data[s_pos]) { dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL); if (! 阅读全文
posted @ 2019-07-06 09:33
樊伟胜
阅读(178)
评论(0)
推荐(0)
摘要:
用户模式 Linux (UML) 是一个有趣的概念. 它被构建为一个分开的 Linux 内核移植, 有 它自己的 arch/um 子目录. 它不在一个新的硬件类型上运行, 但是; 相反, 它运行在一 个由 Linux 系统调用接口实现的虚拟机上. 如此, UML 使用 Linux 内核来运行, 作为 阅读全文
posted @ 2019-07-06 09:32
樊伟胜
阅读(300)
评论(0)
推荐(0)
摘要:
目前为止我们看到的 2 个交互式调试方法( 使用 gdb 于 /proc/kcore 和 kdb) 都缺乏 应用程序开发者已经熟悉的那种环境. 如果有一个真正的内核调试器支持改变变量, 断点 等特色, 不是很好? 确实, 有这样一个解决方案 2 个分开的补丁在流通中, 它允许 gdb, 具 备完全功 阅读全文
posted @ 2019-07-06 09:29
樊伟胜
阅读(378)
评论(0)
推荐(0)
摘要:
许多读者可能奇怪为什么内核没有建立更多高级的调试特性在里面.答案, 非常简单, 是 Linus 不相信交互式的调试器. 他担心它们会导致不好的修改, 这些修改给问题打了补丁 而不是找到问题的真正原因. 因此, 没有内嵌的调试器. 其他内核开发者, 但是, 见到了交互式调试工具的一个临时使用. 一个这 阅读全文
posted @ 2019-07-06 09:28
樊伟胜
阅读(1488)
评论(0)
推荐(0)
摘要:
gdb 对于看系统内部是非常有用. 在这个级别精通调试器的使用要求对 gdb 命令有信心, 需要理解目标平台的汇编代码, 以及对应源码和优化的汇编码的能力. gdb 对于看系统内部是非常有用. 在这个级别精通调试器的使用要求对 gdb 命令有信心, 需要理解目标平台的汇编代码, 以及对应源码和优化的 阅读全文
posted @ 2019-07-06 09:17
樊伟胜
阅读(547)
评论(0)
推荐(0)
摘要:
尽管内核代码的大部分 bug 以 oops 消息结束, 有时候它们可能完全挂起系统. 如果系 统挂起, 没有消息打印. 例如, 如果代码进入一个无限循环, 内核停止调度,[15]15 并且系 统不会响应任何动作, 包括魔术 Ctrl-Alt-Del 组合键. 你有 2 个选择来处理系统挂起- - 或 阅读全文
posted @ 2019-07-06 09:14
樊伟胜
阅读(1241)
评论(0)
推荐(0)
摘要:
大部分 bug 以解引用 NULL 指针或者使用其他不正确指针值来表现自己的. 此类 bug 通 常的输出是一个 oops 消息. 处理器使用的任何地址几乎都是一个虚拟地址, 通过一个复杂的页表结构映射为物理地址 (例外是内存管理子系统自己使用的物理地址). 当解引用一个无效的指针, 分页机制无法 阅读全文
posted @ 2019-07-06 09:13
樊伟胜
阅读(678)
评论(0)
推荐(0)
摘要:
有时小问题可以通过观察用户空间的应用程序的行为来追踪. 监视程序也有助于建立对驱 动正确工作的信心. 例如, 我们能够对 scull 感到有信心, 在看了它的读实现如何响应 不同数量数据的读请求之后. 73 有时小问题可以通过观察用户空间的应用程序的行为来追踪. 监视程序也有助于建立对驱 动正确工作 阅读全文
posted @ 2019-07-06 09:12
樊伟胜
阅读(372)
评论(0)
推荐(0)
摘要:
ioctl, 我们在第 1 章展示给你如何使用, 是一个系统调用, 作用于一个文件描述符; 它 接收一个确定要进行的命令的数字和(可选地)另一个参数, 常常是一个指针. 作为一个使 用 /proc 文件系统的替代, 你可以实现几个用来调试用的 ioctl 命令. 这些命令可以从 驱动拷贝相关的数据结 阅读全文
posted @ 2019-07-06 08:53
樊伟胜
阅读(1729)
评论(0)
推荐(0)
摘要:
如我们上面提到的, 在 /proc 下的大文件的实现有点麻烦. 一直以来, /proc 方法因为 当输出数量变大时的错误实现变得声名狼藉. 作为一种清理 /proc 代码以及使内核开发 者活得轻松些的方法, 添加了 seq_file 接口. 这个接口提供了简单的一套函数来实现大 内核虚拟文件. se 阅读全文
posted @ 2019-07-06 08:52
樊伟胜
阅读(573)
评论(0)
推荐(0)
摘要:
一旦你有一个定义好的 read_proc 函数, 你应当连接它到 /proc 层次中的一个入口项. 使用一个 creat_proc_read_entry 调用: struct proc_dir_entry *create_proc_read_entry(const char *name,mode_t 阅读全文
posted @ 2019-07-06 08:48
樊伟胜
阅读(1673)
评论(0)
推荐(0)
摘要:
所有使用 /proc 的模块应当包含 <linux/proc_fs.h> 来定义正确的函数. 要创建一个只读 /proc 文件, 你的驱动必须实现一个函数来在文件被读时产生数据. 当 某个进程读文件时(使用 read 系统调用), 这个请求通过这个函数到达你的模块. 我们先 看看这个函数并在本章后面 阅读全文
posted @ 2019-07-06 08:45
樊伟胜
阅读(549)
评论(0)
推荐(0)
摘要:
/proc 文件系统是一个特殊的软件创建的文件系统, 内核用来输出消息到外界. /proc 下 的每个文件都绑到一个内核函数上, 当文件被读的时候即时产生文件内容. 我们已经见到 一些这样的文件起作用; 例如, /proc/modules, 常常返回当前已加载的模块列表. /proc 在 Linux 阅读全文
posted @ 2019-07-06 08:44
樊伟胜
阅读(453)
评论(0)
推荐(0)
摘要:
偶尔地, 当从一个驱动打印消息, 你会想打印与感兴趣的硬件相关联的设备号. 打印主次 编号不是特别难, 但是, 为一致性考虑, 内核提供了一些实用的宏定义( 在 <linux/kdev_t.h> 中定义)用于这个目的: int print_dev_t(char *buffer, dev_t dev) 阅读全文
posted @ 2019-07-06 08:39
樊伟胜
阅读(272)
评论(0)
推荐(0)
摘要:
前面一节描述了 printk 是任何工作的以及怎样使用它. 没有谈到的是它的缺点. 大量使用 printk 能够显著地拖慢系统, 即便你降低 cosole_loglevel 来避免加载控制 台设备, 因为 syslogd 会不停地同步它的输出文件; 因此, 要打印的每一行都引起一次 磁盘操作. 从 阅读全文
posted @ 2019-07-06 08:39
樊伟胜
阅读(155)
评论(0)
推荐(0)
摘要:
在驱动开发的早期, printk 非常有助于调试和测试新代码. 当你正式发行驱动时, 换句 话说, 你应当去掉, 或者至少关闭, 这些打印语句. 不幸的是, 你很可能会发现, 就在你 认为你不再需要这些消息并去掉它们时, 你要在驱动中实现一个新特性(或者有人发现了 一个 bug), 你想要至少再打开 阅读全文
posted @ 2019-07-06 08:38
樊伟胜
阅读(498)
评论(0)
推荐(0)
摘要:
如果你不小心, 你会发现自己用 printk 产生了上千条消息, 压倒了控制台并且, 可能地, 使系统日志文件溢出. 当使用一个慢速控制台设备(例如, 一个串口), 过量的消息速率也 能拖慢系统或者只是使它不反应了. 非常难于着手于系统出错的地方, 当控制台不停地输 出数据. 因此, 你应当非常注意 阅读全文
posted @ 2019-07-06 08:38
樊伟胜
阅读(258)
评论(0)
推荐(0)
摘要:
printk 函数将消息写入一个 LOG_BUF_LEN 字节长的环形缓存, 长度值从 4 KB 到 1 MB, 由配置内核时选择. 这个函数接着唤醒任何在等待消息的进程, 就是说, 任何在系统 调用中睡眠或者在读取 /proc/kmsg 的进程. 这 2 个日志引擎的接口几乎是等同的, 但 是注意 阅读全文
posted @ 2019-07-06 08:33
樊伟胜
阅读(195)
评论(0)
推荐(0)

浙公网安备 33010602011771号