内存映射 IO - IO 设备如何知道值已更改?

Posted

技术标签:

【中文标题】内存映射 IO - IO 设备如何知道值已更改?【英文标题】:Memory mapped IO - how does IO device know value has changed? 【发布时间】:2018-03-26 20:50:28 【问题描述】:

IO 设备如何知道与它相关的内存中的值在memory mapped IO 中发生了变化?

例如,假设内存地址 0 专门用于保存 VGA 设备的背景颜色。 VGA 设备如何知道我们何时更改 memory[0] 中的值? VGA 设备是否不断轮询内存位置?或者 CPU 是否会在更改值时以某种方式通知设备(如果是,如何通知?)?

一个示例架构是 MIPS。鉴于MIPS instruction set 没有inout 指令,我不明白它如何与示例中的VGA 设备进行通信(更改时)。另一个例子是 ARM 架构。

【问题讨论】:

【参考方案1】:

在内存映射 I/O 中,对设备的内存区域执行内存读/写将导致 CPU 与设备执行事务以获取/存储该值 - 直接通过 CPU 的内存总线,或者通过辅助总线(例如 ARM 系统上的 AHB/APB)。这个内存事务直接通知设备一个值正在改变;无需单独通知。

您假设内存映射 I/O 由普通 RAM 映射。 事实并非如此。事实上,这些设备的行为方式可能与真实内存完全不同!例如,典型的 UART 或 SPI 设备实现可能具有单个数据寄存器,可以写入该寄存器以传输数据,或读取该寄存器以检索接收到的数据。同样,中断寄存器具有“读取时清除”或“写入 1 以清除”语义的情况并不少见。

值得一提的是:在实践中,许多帧缓冲图形实现确实表现得与普通内存一样。不同之处在于内存存储在双端口 RAM(或时分复用总线)中,视频 RAMDAC 不断读取该内存以将其内容传输到连接的显示器。

【讨论】:

【参考方案2】:

指定为内存映射 I/O (MMIO) 的物理地址空间区域未映射到主内存(系统内存);它被映射到作为 I/O 设备物理一部分的 I/O 寄存器。

为了确定如何处理内存访问(读或写),处理器首先检查目标内存地址所属区域的类型。在任何 MIPS 处理器中,至少有两种类型:Uncached 和 Cached。 MMIO 区域始终未缓存。 Uncached 内存访问请求直接发送到主内存控制器,而不检查或影响任何缓存。但是,I/O Uncached 内存访问请求被发送到 I/O 控制器,最终该请求将到达目标 I/O 设备。

现在,CPU 和 I/O 设备如何相互通信完全由 I/O 设备本身指定。因此,I/O 设备将有一个规范,讨论有多少个 I/O 寄存器以及应该如何使用它们。 I/O 寄存器可用于保存状态标志、控制标志、要由 CPU 读取或写入的数据,或它们的某种组合。请注意,由于 I/O 寄存器在物理上是 I/O 设备的一部分,因此可以设计 I/O 设备,以便它可以检测到其任何寄存器何时被读取或写入,并在以下情况下采取相应的行动必填。

I/O 设备可以向 CPU 发送中断,以通知它某些数据可用,或者它可能出于任何原因需要注意。 CPU 还可以通过检查一些状态标志来频繁地轮询 I/O 设备,然后采取相应的措施。

【讨论】:

相关:superuser.com/questions/1226197/x86-address-space-controller 对现代 x86 系统如何在 DRAM 和 MMIO 之间分类加载/存储地址进行了一些高级描述,并且使用集成的北桥/内存控制器,该逻辑是正确的CPU,以及来自 TLB 条目的不可缓存/回写/直写/不可缓存与写入组合的内存属性。 @PeterCordes 显然,英特尔处理器中所谓的“系统代理”在 MIPS Technologies 处理器中称为“系统接口”。我在 MIPS 架构规范文档中读到,在使用 TLB 进行地址转换期间,CPU 可以确定内存访问的类型(未缓存或缓存)。我认为当它未缓存时,它会直接发送到系统接口,系统接口将检查它是 I/O 还是内存请求。当我写这个答案时,我不确定 MIPS 处理器中的“系统代理”是什么,我懒得去检查。 在每个 TLB 条目中使用一个位来记住该页面是否可缓存是有意义的;否则你必须稍后在其他地方查找它。我猜想如果 x86 MTRR 可以覆盖 PTE 中的页面属性,它们会被页面遍历器检查。并且可能更改 MTRR 会使 TLB 条目无效。因此,TLB 是可缓存与否的一站式商店。我想知道现代 x86 上的 page-walker 是否将 MMIO 与 DRAM 放入 TLB 条目中?可能不是,这可能更接近内存控制器,就像你对 MIPS 说的那样。 @PeterCordes MIPS 中没有 MTRR 寄存器,因此为此目的在每个 TLB 条目中使用一个位是有意义的。在 x86 处理器上,我认为有专用的 MTRR 寄存器,它们指定的类型不会缓存在 TLB 中。缓存它们会增加区域开销。所以我怀疑这一点,除非访问 TLB 比 MTRR 快。但是这两种设计选择都是可能的,因为它们都没有违反 x86 规范。 MTRR 和页表条目中指定的内存类型不会覆盖每个覆盖;它们以某种方式合并。为什么页表遍历器会检查它们?... ...这不是它的责任。对 MTRR 进行更改与任何高速缓存都不一致,它们也与其他处理器的 MTRR 不一致。这样做是操作系统的责任。此外,Bios 通常会设置 MTTR,因此它们很少更改。我也同意你的观点,MMIO 与 DRAM 的区别可能是在地址转换之后完成的。

以上是关于内存映射 IO - IO 设备如何知道值已更改?的主要内容,如果未能解决你的问题,请参考以下文章

存储映射IO

如何在 Erlang 中进行内存映射 IO?

初步了解设备IO方式和ReactOS MDL实现

[架构之路-47]:目标系统 - 系统软件 - Linux OS硬件设备驱动 - CPU内存管理单元MMUDMA与IO内存管理单元IOMMU

内存映射

如何判断系统负载,包括cpu,内存,io设备等