内存映射 I/O

内存映射 I/O

有人能解释一下端口映射和内存映射之间的区别吗?两者兼有能达到什么目的?为什么要进行端口映射?端口映射的结构与内存映射有何不同?许多架构同时使用两者有什么原因吗?此外,端口在这种意义上是什么意思?因为端口在不同的上下文中可能有不同的含义。

例如:端口转发、端口作为通信端点、“端口映射”。

假设我将 OUT 写入端口 400h(虚构;仅举例)(如在 x86-64 等中)。

如果不在内存中,我要写入什么或写入哪里?“端口”如何映射?从这个意义上讲它是什么?

答案1

内存映射 I/O 和端口映射 I/O 是两种互补的 I/O 方法。

内存映射 I/O

在内存映射系统中,访问 I/O 设备就像访问内存的一部分一样。Load并且Store执行命令来读取和写入 I/O 设备,就像它们用于内存一样(端口映射有专门的 I/O 命令)。这意味着 I/O 设备使用与内存相同的地址总线,这意味着 CPU 可以引用内存或者根据地址值分配 I/O 设备。这种方法要求地址空间隔离:即为 I/O 保留的地址不应提供给物理内存。

下面是一个简单的基本计算机系统. 在当代制度中,情况要复杂得多。

在此处输入图片描述


端口映射 I/O

根据维基百科

端口映射 I/O 通常使用一类专门用于执行 I/O 的 CPU 指令。这在 Intel 微处理器上很常见,即 IN 和 OUT 指令。这些指令可以读取和写入一到四个字节(outb、outw、outl)到 I/O 设备。I/O 设备具有与通用内存不同的地址空间,这可以通过 CPU 物理接口上的额外“I/O”引脚或专用于 I/O 的整个总线来实现。由于 I/O 的地址空间与主内存的地址空间是隔离的,因此有时将其称为隔离 I/O。


至于优点和缺点:由于外围设备比内存慢,共享数据和地址总线可能会减慢内存访问速度。另一方面,通过内存映射系统提供的 I/O 简单性,CPU 需要更少的内部逻辑,这有助于实现更快、更便宜、更低功耗的 CPU。逻辑类似于 RISC 系统:降低复杂性,获得更专用和更强大的系统,这对于嵌入式系统等来说非常方便。

相反(同样来自 Wiki):

端口映射的 I/O 指令通常非常有限,通常仅提供 CPU 寄存器和 I/O 端口之间的简单加载和存储操作,因此,例如,要将常量添加到端口映射的设备寄存器需要三条指令:将端口读到 CPU 寄存器,将常量添加到 CPU 寄存器,然后将结果写回端口。

我强烈建议您阅读该维基文章以获取更多信息。


回答您的一个问题:

如果不在内存中,我应该写入什么或在哪里?

您正在通过数据总线写入 I/O 接口的寄存器,稍后(准备就绪时)将数据发送到实际的 I/O 设备。下面是示例 I/O 设备接口的图像。

在此处输入图片描述

答案2

在内存映射 I/O(简称 MMIO)中,设备通过实际上用于访问内存的指令进行访问。每个设备都会获得特定的内存地址。但是,当您尝试读取或写入这部分内存时,某个设备(可能是北桥)只会将其发送到相关设备。即使计算机没有足够的内存用于这样的地址(因为 MMIO 地址通常很高),也没有关系,因为物理内存(即您在主板上看到的内存,称为 RAM)甚至不相关。如果您有足够的 RAM 用于该地址,那么它要么映射到更高的非 I/O 地址,要么只是丢失,意味着您无法在那里读取或写入。

端口映射 I/O(简称 PMIO)非常不同。您使用不同的指令来读取和写入端口。有一个端口地址空间,就像内存地址空间一样,其中的地址要么是实际与设备通信的 I/O 地址,要么就是无效的。PMIO 本质上可以被认为是一个 MMIO,具有一个仅用于 I/O 的单独内存地址空间。

答案3

有了“I/O 信号”和“内存映射”等名称,一切都变得比实际复杂得多,因此给人的印象是它包含的内容更多,并且涵盖了一个高级主题。现在的趋势是人们将其视为新事物。但事实并非如此。即使是 19 世纪 30 年代的巴贝奇驱动打印机,也需要 I/O 信号,尽管是通过轴和齿轮完成的。例如,在 2000 年前的亚历山大英雄的机器中,或者在希腊时代的剧院中,他们总是从一组不同的绳索中拉出一根绳子来控制灯光或布景,每根绳索就像一条输入和输出线,就这么简单,地址是“哪条线”,即我们选择哪个东西、内存或设备,数据是您要传递到该内存或设备或从该内存或设备读回的信息。

尽管大型计算机占据了建筑物中的机柜,早在 40 年代就使用了 64 位之类的东西,因此很久以前就以相同的方式处理 I/O 映射,例如 Konrad Zuse 和他的房间大小的计算机在 30 年代使用了大约 20 位十进制的浮点数,并且必须驱动他的打印机和各种灯泡指示灯以及开关等。但微型微处理器的情况则不同,它们直到 60 年代才被设想出来,并直到 1971 年才制造出来。所有这些技术在 80 年代使用 8 位逻辑,在 70 年代用于 4 位微处理器,在 60 年代用于 2 位,在 90 年代用于 16 位,当时每个人都开始拥有一台计算机,因此因为它就在他们面前,他们第一次开始讨论这个 I/O 和内存映射主题,它似乎是互联网出现后出现的新事物;然后我们在 00 年代有了 32 位,在 10 年代有了 64 位计算机,这导致了关于内存数据线的无休止的讨论。为了回答你的问题,我将谈论电子爱好者 30-40 年前购买的芯片,就像我当时买的一样,因为后来,事情变得如此先进,我无法用后来的芯片构建,但现在的原理是一样的,门只是隐藏在更大的黑盒芯片内,这些芯片包含其他引脚,可以处理更多并行进行的操作(例如,启用许多八进制锁存器,同时成行启用许多芯片),数据和地址总线有更多的线,这是唯一的区别。

好吧,我对所有新语言或现在现代个人计算机的情况不太了解,但我可以告诉你过去我用芯片制造计算机时的情况。

所有的 I/O 映射和内存映射的含义简单来说就是,如果你为了庆祝某个目的串起了一堆灯泡,并且每个灯泡都有电线,并将这些灯泡称为内存位置,(即灯泡代表 RAM 中的内存,要么开要么关,如果你选择位置 0,你将获得电线 0,位置 1 获得电线 1,位置 2 获得电线 2 等等)如果你添加了更多电线,例如一根电线是一个铃铛,那么那个特定位置就不是内存,而是一个设备,你可以使用 OUT 命令将其输出到该设备以使其响铃。但从计算机的角度来看,它被视为内存位置,因为它同样作为通往 MPU 的电线进入。如果添加了另一根电线,即你从外部操作的开关,那么这就是一个 I/O 设备,它将是一条 IN 指令到 PC。所以这称为 I/O 映射 I/O。

现在在计算机上,总线上的线路代表地址线或数据线,但它们是二进制的,即使用 2 根线路,您可以得到 00 01 10 11,即 4 种组合 2^2,因此使用 8 根线路有 2^8=256 种可能性,使用 20 根线路有 2^20=1048576 种可能性,使用 30 根线路有 2^30=1073741824(1 GB)种可能性。这就是为什么它被称为 MAPPED,而不仅仅是说 I/O 和内存,他们说 I/O 映射和内存映射,因为您正在将线路映射为组合 y 二进制编码。因此,如果说你有 2 根电线、4 种组合,它们就不能直接连接到灯泡上(更不用说 MPU 的微小电压所需的电流放大,以及防止反馈电流),而是 2 根电线必须经过解码器(我们曾经使用 138 将 3 根线解码成 8 根线,使用 164 将 4 根二进制线解码成 16 根线。)一旦通过解码器,这 2 根线(例如 A0 和 A1(地址 0 和地址 1(线))就变成 4 根线(开或关),用于你正在驱动的特定灯泡(在计算机上的情况下为内存),但在某些情况下,这些位置会选择一些输入/输出设备,而是说“使用我”,即像内存一样,一旦找到,数据就会以一种或另一种方式传递(使用巧妙的三态逻辑每次都切断途中的电压)在数据总线线路 D0..7 或 D0..31 上,或者无论计算机上的数据大小如何(您有 2 位、4 位, 8 位、16 位、32 位、64 位、128 位、256 位、计算机,无论您构建的是哪种计算机)。因此,数据会自然地从数据线传入或传出到内存或 I/O 设备(如果它是内存映射的),但这不应与 IN/OUT 指令混淆,这个 IN 和 OUT 表示来自其他一些 I/O 内存块,MPU 内部专门为 I/O 分配的特殊 I/O 内存块,即(非内存映射),这种 I/O 空间在某些微处理器上并不总能获得,例如,我认为 6502 上没有它,但我们在 z80 上有它。更具艺术性的芯片只使用内存映射,例如在游戏机等中,更明智但无趣(留在书中)的芯片也用于 I/O 空间。内存映射 I/O 速度极快,因为它结合了内存寻址(对于 RAM 来说速度超快),因此图形类型的计算机仅使用内存映射进行 I/O 即可获得速度。I/O 映射 I/O 分配给慢速端口(例如 rs232 或并行端口),并使用 IN OUT 命令。

现在,如果您不添加两根电线,而是实际更换了原本连接灯泡的两根电线,并取下其中一些灯泡,用其他东西替换它们,例如,一个灯泡上装一个铃铛,另一个灯泡上装一个开关,那么现在它们不再分别通过 IN 和 OUT 指令引用(选择),而是通过访问选择这些电线(原本是灯泡)的特定内存位置来引用它们。所以这是内存映射 I/O。

内存映射 I/O 意味着通常通向内存 (RAM) 的实际地址总线也连接到其他解码器 (逻辑解码器),并且当它感测到地址信号的特定二进制组合时,它会产生高输出 (例如,如果您有大量与与非门,并且您说,如果这个与非那个等等,使用引脚 A0..A20 或您的地址总线的任何大小),则此高信号启用锁存器 (对于特定设备,如串行端口、并行端口),然后此锁存器将数据总线上的数据传递到 I/O 设备。这用于写入 I/O 设备。读取以相反的方式进行,I/O 设备将数据传回,如果我没记错的话,它会将完全相同的地址代码组合发送到地址线上。

我推测,今天它一定以同样的方式工作,只是数据和地址线会更多。

您实际上是将 I/O 连接到地址线。因此,I/O 被有效地映射到内存空间,就像它是内存一样。但另一个锁存器会禁止地址引脚同时访问 RAM,这样您就不会在同一线路上获得两个地址或数据源的电压,否则会损坏芯片。

40 年前,我们在 z80 芯片上就有了 IN 和 OUT 指令。这是针对芯片以不同方式处理 I/O 本身的特殊情况,即它不是内存映射的。(即,使用内存映射,您只需读取或写入内存位置,但使用 IN 和 OUT 您已经告诉 CPU 这是一个 I/O 信号而不是内存)。因此,对于 IN/OUT 指令,它有自己的 I/O 地址空间(这是 RAM 内存的额外空间),这个 I/O Ram 似乎有一组相同的地址,只是您是通过连接到这些 I/O 地址的解码器直接访问设备的,并且您不是从标准地址引脚访问 I/O 设备,这是针对 IN/OUT 指令的。

当您输入和输出一个字符串时,我不知道 x86,但大概这意味着您正在数据总线上发送或接收数据(使用所有数据引脚 D0..D15 或任何大小的数据总线)以该特定 I/O 设备可能的最大数据速率多次串联(也许要做到这一点,它使用某种握手信号,您必须查找它。)因此,D0..63 线上的数据(或旧电脑上的 D0..31 或 80 年代末 90 年代初电脑上的 D0..15,或 80 年代和 80 年代前的电脑上的 D0..7,是串联的一个接一个,而不是只输入和输出一次。即 INSTR 和 OUTSTR 只是以某个定义的数据速率多次输入和输出。例如,如果您正在访问互联网,您每次都希望输入和输出大量信息,所以您将使用数据字节的输入和输出,在这种情况下,最好将其作为字母和数字的 ASCII 代码字符串传递。这些命令与在循环中使用 IN 和 OUT 指令完全相同,其中计数是字符串长度。

如果您正在访问 PC 扬声器,则您只需使用 OUT 一次传递一条数据。

如果您正在从并行端口读取数据,则需要执行 IN,并使用端口 I/O 地址的代码。写入它,例如通过电子信号驱动旧打印机或机器人,则需要使用 OUT 命令。并行端口和串行端口(旧 RS232)是典型的端口。RS232 是串行数据,只允许一位输入或输出,因此如果您正在从 rs232 读取数据,则只有 1 位字节是相关的,输出也是如此。rs232 的波特率最大约为 17kHz,但这些过去经常用于驱动电子设备,在过去,我曾经构建 rs232 电路,例如读取电压或驱动 PIC 微控制器。每个端口都命名为 COM1 COM2 COM3 COM4,并且它们具有 I/O 地址。我不确定,但它们类似于例如 3F8h 378h(h=十六进制地址)

我不确定现代端口,但如果您正在写入 USB,这很可能是内存映射 I/O,以实现更快的速度。

PS/2 键盘端口,我认为它使用 IN 指令从键盘读取数据。它取代了旧的 RS232,但我认为规格略有不同。

磁盘驱动器通常是内存映射的,现在大概也是如此,也就是说,您不能使用 IN/OUT 指令来驱动磁盘驱动器,因为这样会太慢。但是端口无论如何都很慢,所以这无关紧要,例如,就所需数据速率而言,打印机的速度很慢,而硬盘所需的数据速率却非常高,例如 200 兆字节/秒。扬声器只需要声音频率乘以 10 或 20,比如说 20kHz 就足以满足蜂鸣器的需求,因此它是 I/O。速度慢的东西使用 I/O,即 IN/OUT 指令。因此,USB 现在可能已是内存映射的,您必须检查一下。

更好的理解方式是这样的。在 80 年代的旧电脑上,有时你想控制自己制作的某个设备,但没有输出端口的规格(因为在那个年代,制造商隐藏了这一点,以便某些公司(例如操纵杆和卡带公司)可以通过一些商业交易在市场上领先)。你所要做的就是打开电脑,然后把电线焊接到地址总线上的某些点上,例如,你将三根电线焊接到电路中安全距离的某些点上(以免芯片因发热而损坏),这些点通过电路板布局连接到微处理器上的 A15 A7 和 A1 引脚。而且您通常还必须连接一条 MREQ 线(一条内存请求线和/或 RD/WR 线,以产生更整洁的信号,并将其添加到与或非逻辑中,但如果您聪明的话,您可以只使用地址线来完成)然后您连接这三条线 + 这个额外的 Ready 类型信号(例如 MREQ RD 或 WR 线以提供一些有效的低电平或高电平(这里可能需要一个额外的非门)来表示数据现在已准备好)通过一个 4 输入与门,它通过一个 200 欧姆的电阻器向 LED 提供输出,您有自己的内存映射到 LED 灯的高速 I/O,您可以通过 SR 锁存器或 D 型锁存器将其锁存以将其存储在某些电路板外部的 1 位存储器中。这里 15 是 32K 线,7 是 64 线,1 是 2 线(二进制以 2 的幂计算,因此 A1 是 2^1、A7 是 2^7 而 A15 是 2^15),因此如果您在十六进制中使用旧 MPU 上的 LDA 或 STA 或 LD 寻址位置 32768+64+2=32834 = F041,您将输出到这个 led,如果电阻大约为 100 欧姆,它就会亮起来。因此您已经完成了内存映射 I/O,它很简单,您今天可以通过将其焊接到 mpu 地址线上来完成。但是由于电路的精密性,您现在不会这样做。但您也可以连接数据线 D0..7(在过去)或者现在说 d0..31(用于旧 486 PC 上的 32 位)。然后,如果您通过执行将值 8 加载到累加器(现在为 mov ax,8)或将该累加器值存储到地址位置(mov F041h,ax accumulator)来在机器代码中寻址该位置,您甚至今天就会看到 LED 亮起。请注意,示例中的 8 是数据总线上的内容,在这种特定情况下,我们不会传递数据,我们只是启用特定设备(LED 亮起,如果我们选择了该 I/O 设备,这里只是一个 LED),因此在这个例子中,MOV ax,8 指令的数字并不重要,它可能是例如 mov ax,243,当我们执行 mov F041h 时,我们仍然会在 F041h 行上启用 LED,因为我们使用的是相同的地址。您会看到,有地址线和数据线。因此,当您在 COM1 中寻址 3F8 或任何地址时,I/O 内存映射只是向端口发送信号,例如ps/2,并且与门检查线路上是否有 1110000100,即 11 是 3 1000 是 F 而 0100 是 8,请参见二进制到十六进制的转换。如果在那些有 1 的位位置出现高电压,则端​​口(例如 rs232 或 ps/2)设置为活动状态,即已启用,这将通过 CE 芯片启用信号或 CS 芯片选择简单启用锁存器。

在锁存器上,它是 E 启用引脚或 OE 有效低输出启用。即,在上面描述的例子中,我们使用地址来选择(通过解码)我们要使用哪个 I/O 设备(即,在示例中,如果选择了该 I/O 设备,LED 就会亮起。所以这是启用线。然后,一旦选择了 I/O 设备,数据就会从数据总线(以前是 D0..7,或者现在 64 位计算机的 D0..63)通过八进制锁存器 373 传递,这些是 D 型触发器电路,可将数据存储在触发器内。在高电平有效时钟沿下,数据通过并存储。这个时钟沿来自数据信号上的“DATA RDY”信号,它有各种名称,我不知道现在叫什么名字。所以对于 64 位,我们有 8 个八进制锁存器。它们使用双向锁存器来控制数据,无论是单向还是三态,这样当不使用 I/O 设备时,数据线处于高阻态。所以因此,您使用地址线上的组合来选择 I/O 设备,这是数字,例如 OUT 3F8h, 7 中的 3f8h,而数据(此处的示例为 7)是在数据线上传递的内容,在 OUT 命令中,数据传递到数据锁存器,然后传递到 I/O 设备。如果您有 IN,您将执行一个命令,例如 IN 3f8h,800h,(我希望如此,但我不知道 x86 汇编程序的语法),我的意思是,对于 IN,您正在从数据线输入数据(在选择地址之后,例如此处的 3f7h,它选择该 I/O 设备),该数据来自 I/O 设备,通过数据锁存器中的 D 型触发器(数据总线线路的每个位一个),并输入到 MPU 微处理单元上的 D0..7 或(现代 PC 上的 D0..63)引脚。在这个例子中,我输入了 IN 3f8h, 800h,以表明数据一旦进入,就会被存储到地址 800h 中。我认为 x86 的语法有所不同,您可能需要执行 IN 3f8h, ah 或类似操作,即先将数据输入寄存器,然后执行 MOV 800h, ah,即将数据移动到 RAM 中的内存位置(如果您想存储它),或者对 ah 等执行其他操作。ah 是一个示例寄存器,它可以是任意的,al、bh、bl 等等,但请检查语法,每个汇编系统都略有不同,我不是 x86 专家。再次,我使用 3f8h 作为示例 I/O 地址,有数百个甚至数千个这样的地址,例如 378h... 有关完整列表,请参阅 IBM PC 的 I/O 内存映射。

而当您访问内存(RAM,例如 70 年代的 64 字节静态 RAM 和动态 RAM、80 年代的 8K SRAM 和 DRAM、90 年代每行都有几兆字节的 SIMMS(单列直插式内存模块),现在是包含 DIMM 的 DDR 模块、双列直插式内存模块,我没有检查过,但最新的模块在每个小芯片上无疑都有几千兆字节)时,如果它不是 I/O 地址(很少有地址是 I/O 地址,如今内存在地址空间中的可能性是现代 PC 上 I/O 的数百万倍甚至更高),您仍然使用相同的读写数据指令来访问内存,但您并没有驱动一些寻找这些位的外部逻辑电路,而是那些地址和数据引脚直接连接到 RAM 芯片。

在机器代码中,I/O 和内存寻址看起来相同,好像它们都是内存访问,但在实际电子电路中物理发生的情况完全不同。

相关内容