MMIO
I/O 作为 CPU 和外设交流的一个渠道,主要分为两种,一种是 Port I/O,一种是 MMIO(Memory mapping I/O)。
Port-mapped I/O
一种 I/O 编址方式是端口映射 I/O(port-mapped I/O), CPU 使用专门的 I/O 指令对设备进行访问, 并把设备的地址称作端口号。在执行其中的一条指令时,CPU 使用地址总线选择所请求的 I/O 端口,使用数据总线在 CPU 寄存器和端口之间传送数据。
目的:
系统设计者的主要目的是提供对 I/O 编程的统一方法,但又不牺牲性能。为了达到这个目的,每个设备的 I/O 端口都被组织成一组专用寄存器。CPU 把要发给设备的命令写入控制寄存器(control register),并从状态寄存器(status register)中读出表示设备内部状态的值。CPU 还可以通过读取输入寄存器(input register)的内容从设备取得数据,也可以通过向输出寄存器(output register)中写入字节而把数据输出到设备。
举例:
以 x86 为例,x86 提供了 in 和 out 指令用于访问设备,其中 in 指令用于将设备寄存器中的数据传输到 CPU 寄存器中, out 指令用于将 CPU 寄存器中的数据传送到设备寄存器中. 一个例子是使用 out 指令给串口发送命令字:
|
|
上述代码把数据 0x41 传送到 0x3f8 号端口所对应的设备寄存器中. CPU 执行上述代码后, 会将 0x41 这个数据传送到串口的一个寄存器中, 串口接收之后, 发现是要输出一个字符 A; 但对 CPU 来说, 它并不关心设备会怎么处理 0x41 这个数据, 只会老老实实地把 0x41 传送到 0x3f8 号端口。事实上, 设备的 API 及其行为都会在相应的文档里面有清晰的定义, 在 PA 中我们无需了解这些细节, 只需要知道, 驱动开发者可以通过 RTFM, 来编写相应程序来访问设备即可。
Memory mapping I/O
MMIO(Memory mapping I/O)即内存映射 I/O,它是 PCI 规范的一部分,I/O 设备被放置在内存空间而不是 I/O 空间。从处理器的角度看,内存映射 I/O 后系统设备访问起来和内存一样。这样访问 AGP/PCI-E 显卡上的帧缓存,BIOS,PCI 设备就可以使用读写内存一样的汇编指令完成,简化了程序设计的难度和接口的复杂性。
目的:
早期的 PC 中,所有的 IO 设备(除了存储设备之外的设备)的内部存储或者寄存器都只能通过 IO 地址空间进行访问(Intel 干的好事)。但是这种方式局限性很大,而且效率低,于是乎,软件开发者和硬件厂商都不能忍了……然后一种新的东西就出来了——MMIO。MMIO,即 Memory Mapped IO,也就是说把这些 IO 设备中的内部存储和寄存器都映射到统一的存储地址空间(Memory Address Space)中。但是,为了兼容一些之前开发的软件,PCIe 仍然支持 IO 地址空间,只是建议在新开发的软件中采用 MMIO。
内存映射 I/O 这种编址方式非常巧妙, 它是通过不同的物理内存地址给设备编址的. 这种编址方式将一部分物理内存的访问"重定向"到 I/O 地址空间中, CPU 尝试访问这部分物理内存的时候, 实际上最终是访问了相应的 I/O 设备, CPU 却浑然不知.
这样以后, CPU 就可以通过普通的访存指令来访问设备. 这也是内存映射 I/O 得天独厚的好处: 物理内存的地址空间和 CPU 的位宽都会不断增长, 内存映射 I/O 从来不需要担心 I/O 地址空间耗尽的问题.
从原理上来说, 内存映射 I/O 唯一的缺点就是, CPU 无法通过正常渠道直接访问那些被映射到 I/O 地址空间的物理内存了. 但随着计算机的发展, 内存映射 I/O 的唯一缺点已经越来越不明显了: 现代计算机都已经是 64 位计算机, 物理地址线都有 48 根, 这意味着物理地址空间有 256TB 这么大, 从里面划出 3MB 的地址空间给显存, 根本就是不痛不痒。
对 x86 来说, 内存映射 I/O 的一个例子是 NEMU 中的物理地址区间[0xa1000000, 0xa1800000). 这段物理地址区间被映射到 VGA 内部的显存, 读写这段物理地址区间就相当于对读写 VGA 显存的数据. 例如:
|
|
会将显存中一个屏幕大小的数据清零, 即往整个屏幕写入黑色像素, 作用相当于清屏. 可以看到, 内存映射 I/O 的编程模型和普通的编程完全一样: 程序员可以直接把 I/O 设备当做内存来访问. 这一特性也是深受驱动开发者的喜爱.
-
No backlinks found.