CPU、磁盘、RAM、以太网数据流

CPU, Disk, RAM, Ethernet Data Flow

试图了解现代消费者台式计算机上的数据流。

  1. 首先查看 SATA 端口。如果您想将一些字节加载到 RAM 中,CPU 将该请求发送到内存控制器,该内存控制器处理对 SATA 设备的请求,并且该数据是由内存控制器移动到 RAM 中的,或者 CPU 缓存或寄存器完全涉及数据?

  2. 我假设 OS 通常会阻塞线程,直到 I/O 请求完成。内存控制器是否发送中断让 OS 知道它可以再次将该线程安排到队列中?

  3. 以太网:因此,假设上述步骤已完成,其中文件的某些字节已加载到 RAM 中,内存是由内存控制器移动到以太网控制器还是 CPU参与持有这些数据?

  4. 如果你在localhost上使用socket呢?我们只是绕过内存控制器还是根本不涉及以太网控制器?

  5. SATA 到 SATA 存储传输在任何地方缓冲?

我知道问题很多,如果您能发表评论,我将不胜感激!我真的很想了解这里的基本原理。没有这些细节,我很难进入更高层次的抽象...

Looking first at a SATA port. If you want to load some bytes into RAM does the CPU send that request to the memory controller which handles the request to the SATA device and is that data than moved onto RAM by the memory controller or does the CPU cache or registers get involved with the data at all?

CPU运算过程中不参与。它是一个 AHCI 进行传输,它是一个 PCI DMA 设备。

来自英特尔的 AHCI (https://www.intel.ca/content/www/ca/en/io/serial-ata/serial-ata-ahci-spec-rev1-3-1.html) is used for SATA disks. Meanwhile, for more modern NVME disks an NVME PCI controller is used (https://wiki.osdev.org/NVMe)。

OS 基本上会写入 RAM 以写入 AHCI 的寄存器。这样,它将能够告诉 AHCI 做一些事情并告诉它在特定位置写入 RAM(可能在缓冲区 provided/allocated 中,用户模式进程请求磁盘上的数据)。该操作是 DMA,因此 CPU 并未真正涉及。

在 C++ 中,您可能使用 fstream 或直接 API 调用 OS 提供的库中的 OS 来请求数据。例如,在 Windows 上,您可以使用 WriteFile() 函数 (https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile) 写入某些文件。

在下面,库是一个进行系统调用的瘦包装器(标准 C++ 库也是如此)。您可以在 浏览我的回答,了解有关系统调用及其在现代系统上如何工作的更多信息。

内存控制器并没有真正涉及到。它涉及写入 AHCI 的寄存器,但 Brendan 的回答可能对这件事更有用,因为我真的不知道。

I assume the OS typically blocks the thread until an I/O request is completed. Does the memory controller send an interrupt to let the OS know it can schedule that thread into the queue again?

是的,OS 阻塞线程,是的,AHCI 在命令完成时触发 MSI 中断。

OS会将进程放入等待IO的进程队列中。 AHCI 确实使用 PCI 设备的 MSI 功能触发中断。 MSI 功能是一种特殊功能,它允许绕过现代 x86 处理器的 IO APIC,并直接将处理器间中断发送到本地 APIC。本地 APIC 然后在 IDT 中查找向量触发并使处理器跳转到关联的处理程序。

它是区分触发中断的设备的处理程序编号,这使得 OS 可以轻松地为该中断编号(驱动程序)上的设备放置适当的处理程序。

OS 有一个驱动程序模型,它考虑了将在现代计算机上使用的不同类型的设备。驱动程序模型通常采用虚拟文件系统的形式。虚拟文件系统基本上将每个设备作为文件呈现给 OS 的上层。上层基本上对文件进行打开、读取、写入和ioctl 调用。在下面,driver会做一些复杂的事情,比如写AHCI的寄存器触发read/write个周期,然后把进程放到其他队列等待IO。

从用户模式(比如调用 fstream 的 open 方法),它实际上是一个系统调用。系统调用处理程序将检查所有权限并确保在返回文件句柄之前请求的一切正常。

Ethernet: So assuming the above steps are complete where some bytes of a file have been loaded onto RAM does the memory get moved to ethernet controller by the memory controller or does the CPU get involved holding any of this data?

以太网控制器也是一个 PCI DMA 设备。它直接在RAM中读写。

以太网控制器是一个 PCI DMA 设备。我从来没有写过以太网驱动程序,但我可以告诉它只是直接在 RAM 中读写。现在对于网络通信,您将拥有类似于文件但不属于虚拟文件系统的套接字。网卡(包括以太网控制器)不作为文件呈现给上层。相反,您使用套接字与控制器通信。套接字未在 C++ 标准库中实现,但作为 OS 提供的必须从 C++ 或 C 中使用的库存在于所有广泛的平台上。套接字本身也是系统调用。

What if you use socket with localhost? Do we just do a round about with the memory controller or do we involve the ethernet controller at all?

可能不涉及以太网控制器。

如果您使用带有本地主机的套接字,OS 将简单地将数据发送到环回接口。维基百科的文章在这里很直接(https://en.wikipedia.org/wiki/Localhost):

In computer networking, localhost is a hostname that refers to the current computer used to access it. It is used to access the network services that are running on the host via the loopback network interface. Using the loopback interface bypasses any local network interface hardware.

SATA to SATA storage transfer buffered anywhere?

从第一个设备传输到RAM,然后再传输到第二个设备。

在我之前为 AHCI 提供的 link 中指出:

This specification defines the functional behavior and software interface of the Advanced Host Controller Interface (ACHI), which is a hardware mechanism that allows software to communicate with Serial ATA devices. AHCI is a PCI class device that acts as a data movement engine between system memory and Serial ATA devices.

AHCI 不适用于从 SATA 移动到 SATA。它用于从 SATA 移动到 RAM 或从 RAM 移动到 SATA。因此,SATA 到 SATA 操作涉及将数据放入 RAM,然后将数据从 RAM 移动到另一个 SATA 设备。

内存控制器本身不创建请求(它没有 DMA 或总线控制功能)。

内存控制器主要是将请求路由到正确的位置。例如,如果 CPU(或设备)要求从物理地址 0x12345678 读取 4 个字节,则内存控制器使用物理地址来确定是将该请求路由到 PCI 总线,还是不同的 NUMA node/different 内存控制器(例如使用快速路径或超传输或全路径链接到其他 chips/memory 控制器),或其本地连接的 RAM 芯片。如果内存控制器将请求转发到其本地连接的 RAM 芯片;然后内存控制器还处理“哪个内存通道”和时序部分;并且还可以处理加密和 ECC(checking/correcting 错误,并将它们报告给 OS)。

大多数设备都支持自己控制总线。

因为大多数操作系统使用“在虚拟内存中连续”的分页通常并不意味着“在物理内存中连续”。因为设备只处理物理地址,而且大多数传输在物理内存中不是连续的;大多数设备都支持使用“范围列表”。例如,如果磁盘控制器驱动程序想要从磁盘读取 8 KiB,那么驱动程序可能会告诉磁盘控制器“从物理地址 0x11111800 获取前 2 KiB,然后从物理地址 0x22222000 获取下一个 4 KiB,然后是最后 2来自物理地址 0x33333000 的 KiB”;并且磁盘控制器将按照此列表将 8 KiB 传输的片段传输到所需的地址。

因为设备使用物理地址并且几乎所有软件(包括内核、驱动程序)通常主要使用虚拟地址,某些东西(内核)必须将虚拟地址(例如从“read()”函数调用)转换为设备 driver/s(可能)需要的“范围列表”。当使用 IOMMU(用于安全或虚拟化)时,此转换可能包括配置 IOMMU 以适应传输;在这种情况下,最好将它们视为 IOMMU 可能 convert/translate 物理地址的“设备地址”(设备使用“设备地址”而不是实际物理地址)。

针对某些(相对少见,多为高端服务器)案例; motherboard/chipset 还可能包含某种 DMA 引擎(例如“Intel QuickData 技术”)。取决于 DMA 引擎;这可能能够将数据直接注入 CPU 缓存(而不是像大多数设备一样只能传输 to/from RAM),并且可能能够处理直接“从一个设备到另一个设备”的传输(而不是必须使用 RAM 作为缓冲区)。然而;一般来说(因为设备驱动程序需要在没有“相对稀有”的 DMA 引擎时工作)motherboard/chipset 提供的任何 DMA 引擎很可能不会被 OS 或驱动程序很好地支持(并且可能根本不支持或不使用 DMA 引擎)。