Linux 跨设备内存共享核心:dma-buf 全景演进与技术前沿

dma-buf(DMA Buffer)是 Linux 内核中用于跨设备/跨驱动内存共享 的核心子系统。近年来,随着 AI 训练、大模型推理以及超高性能网络(如 400G/800G 网络)的爆发,数据在 GPU、网卡(NIC)、存储设备(NVMe)之间的零拷贝(Zero-copy)传输成为了刚需。dma-buf 已经从最初一个简单的"媒体设备间共享内存"的小工具,演变成了如今支撑现代 AI 数据中心、GPU 服务器超高性能通信的底层基石。

一、 什么是 dma-buf?

在传统 Linux 中,每个设备驱动(如显卡驱动、网卡驱动)都独立管理自己的内存。如果要把 GPU 里的数据通过网卡发出去,通常需要:

  1. GPU 驱动将数据拷贝到系统内存(CPU 内存)。

  2. CPU 将数据从内核空间拷贝到用户空间。

  3. 网卡驱动再将系统内存中的数据拷贝到网卡硬件中。

这种多次拷贝严重浪费了 CPU 周期和内存带宽。

dma-buf 改变了这一切。它提供了一个抽象的内存文件描述符(fd):

  • Exporter(导出者) :分配内存的驱动(如 GPU 驱动),将这块内存包装成一个 dma-buf 并暴露一个文件描述符(fd)。

  • Importer(导入者):使用内存的驱动(如网卡驱动),通过这个 fd 获取内存的物理散射列表(Scatterlist),并将其映射到自己的 DMA 地址空间。

通过 dma-buf,网卡可以直接去读 GPU 的内存,实现硬件级别的零拷贝(P2P DMA)。

二、 dma-buf 在 RDMA 中的演进

RDMA(远程直接内存访问)天然追求极致的低延迟和高吞吐。为了让远程机器直接读写本地 GPU 内存(如 NVIDIA GPUDirect RDMA),RDMA 子系统(ib_core)对 dma-buf 的支持经历了几个重要阶段:

1. 早期阶段:用户态硬编码与私有内核 Peer-to-Peer

在 dma-buf 完善前,要实现 GPUDirect RDMA,需要 NVIDIA 驱动提供私有的内核符号(如 nvidia_p2p_get_pages),RDMA 驱动(如 Mellanox mlx5)去调用这些私有接口。这种方式没有统一的标准,无法进入 Linux 主线内核。

2. 标准化阶段:ib_umem_dmabuf 的引入 (约 2020-2021年)

Linux 内核引入了 ib_umem_dmabuf 机制。用户空间通过 GPU 驱动分配内存并获得 dma-buf fd,然后通过 RDMA 的 ibv_reg_dmabuf_mr(或通用的内存注册 ioctl)将这个 fd 注册为 RDMA 的内存区域(MR, Memory Region)。RDMA 核心层可以通过标准的 dma_buf_attach() 和 dma_buf_map_attachment() 接口获取 GPU 内存的 DMA 地址,不再依赖厂商私有驱动。

3. 动态内存管理与页面失效(Dynamic dma-buf)

早期的 RDMA 内存注册必须是常驻(Pinned)的,即内存不能被移动或释放。但 GPU 显存非常宝贵,GPU 驱动希望能动态回收或换出(Evict)显存。

内核引入了 dma_buf_move_notify 和 ODP(On-Demand Paging) 结合的机制。当 GPU 驱动需要移动这块显存时,会通过 dma-buf 的 peer 机制通知 RDMA 驱动让其硬件条目(TPT / MR)暂时失效,等 GPU 移动完后再重新建立映射,实现了高吞吐与灵活性的平衡。

三、 dma-buf 在 Netdev(传统网络子系统)中的演进

传统网络子系统(netdev)使用 sk_buff(skb)来传输数据。skb 默认设计是基于系统内存(CPU 内存)和页(struct page)的。随着 GPU 成为计算核心,传统网络栈对 struct page 的深度依赖成为了瓶颈,因为 GPU 显存或某些特殊的设备内存(Device Memory)在内核中不一定有对应的 struct page 结构。这导致传统的 send / recv 无法直接操作 dma-buf。

为了解决这一痛点,在 2026 年 Linux 存储、文件系统、内存管理和 BPF 峰会(LSFMM+BPF) 上,开发者们针对如何让 dma-buf 更加高效地服务于网络和存储展开了深入探讨。以下是本次峰会关于该技术的最新动态:

2026 LSFMM+BPF 峰会现场直击:io_uring 与 dma-buf 的融合趋势

内核的 dma-buf 子系统提供了一种在驱动程序之间共享内存缓冲区的方法,通常用于支持高效的设备到设备(device-to-device)I/O。在 2026 年 Linux 存储、文件系统、内存管理和 BPF 峰会(LSFMM+BPF)上,Pavel Begunkov 在 Kanchan Joshi 的协助下,主持了存储与内存管理轨道的联合会议,共同探讨如何让 dma-buf 的使用更加高效,并使其能够用于由用户空间发起的读写操作。

Begunkov 首先提到了 Keith Busch 在 2022 年提交的一套补丁。该补丁指出,虽然 dma-buf 可以促进高效的 I/O 操作,但在这些操作发生之前,通常需要进行大量昂贵的准备工作。这些工作包括创建各种内部数据结构、建立 DMA 映射,以及可能对 I/O 内存管理单元(IOMMU)进行的高昂配置。当必须为每次操作都创建一个新的 dma-buf 时,这些工作就不得不重复进行,从而导致大部分效率优势荡然无存。Busch 的解决方案是允许将 dma-buf 注册到 io_uring 子系统中,类似于 io_uring 支持注册文件和缓冲区的方式。这将允许注册的 dma-buf 在 io_uring 内部被重复使用,从而将设置成本分摊到多次操作中。

虽然那套补丁从未合并到主线中,但人们对这一概念的兴趣依然不减。Begunkov 带来了自己的一套补丁系列来扩展 Busch 的工作。他在会上表示,他的目标是创建一个一致的基础设施,以允许在网络和存储子系统中使用 dma-buf。他选择将 io_uring 的注册缓冲区(registered buffers)作为用户空间 API,并为 dma-buf 引入了一种特殊的注册操作。用户空间将从支持 dma-buf 的子系统中获取它,然后将关联的文件描述符(file descriptor)注册到 io_uring 中;此后,它就可以用于 I/O 操作了。

这项工作有一些核心要求。尽管使用了 io_uring 作为 API,但该机制的内部实现不应该与 io_uring 强绑定;它最终应该能够扩展到文件系统及更广泛的领域。此外,它还必须支持由 dma-buf 提供者触发的映射失效(map invalidation)机制。其内部 API 围绕着一个新的 io_dmabuf_token 结构展开,该结构是实现 dma-buf 的驱动程序与 io_uring 之间的接口。具体的 I/O 请求则通过 io_dmabuf_map 结构进行追踪,并由 iomap 子系统提供支持,以提供一种驱动程序特定的方式来迭代遍历 I/O 请求。该补丁系列正在推进中,但尚未完全准备就绪。

他说,偶尔会有人提出一个问题:是否应该将 P2PDMA(Peer-to-Peer DMA)用于此目的?然而,有几个原因导致 P2PDMA 并不足够。首先,P2PDMA 无法使用用户空间可能已经拥有的 dma-buf,而这是一个刚性需求。新的 API 可以支持更低成本的数据中间转换、更好地优化 IOMMU 的使用,并提供映射失效支持;对此,台下的一位听众指出,P2PDMA 其实也支持映射失效。当然,不使用 P2PDMA 的缺点是需要一套全新的 API,而且目前这套 API 还局限于 io_uring。

Begunkov 介绍到,其应用场景包括需要利用普通主机内存来优化 IOMMU 使用的应用程序。许多网络存储解决方案也可以从中受益,因为它可以方便地在网络接口和文件系统之间移动数据。显然,还有一家公司希望将该特性用于其 GPU 基础设施。Joshi 补充说,NVMe 子系统可以利用该特性来实现透传(pass-through)支持等功能。未来的计划还包括为更多的块设备驱动程序、SCSI 子系统以及文件系统添加支持。

一项 IOMMU 预映射(pre-mapping)基准测试显示,其性能提升最高可达 8.8倍。值得注意的是,预映射完全消除了在"延迟(lazy)"或"严格(strict)"模式下使用 IOMMU 所带来的性能惩罚------这两种模式都会在映射更改时执行一定量的 TLB 失效操作,以强制实现设备隔离。换句话说,为了获得满负荷的性能,人们不再需要使用在某些人看来安全性较低的 IOMMU 透传(pass-through)模式了。

然而,Jason Gunthorpe 感到疑惑:为什么透传模式还不够?预映射带来的额外复杂性又是如何体现其合理性的?Begunkov 回答说,摆脱透传模式的动力主要源于对安全性的考量。Gunthorpe 则认为,更好的解决方案是在操作完成后不保留 IOMMU 的映射。Christoph Hellwig 提到,一些数据中心开始强制要求使用 IOMMU,而且 IOMMU 所做内存合并(memory coalescing)对性能大有裨益,因此需要提供兼顾高性能的完整 IOMMU 支持;Gunthorpe 承认这些确实是很强有力的理由。Matthew Wilcox 建议,对缓冲区进行映射的时机正好可以用来对底层内存进行碎片整理,从而从根本上消除对内存合并的需求。

David Howells 担心,对 dma-buf 的误用(无论是无意的还是蓄意的)可能会因为耗尽所有可用的 IOMMU 插槽(slots)而引发问题,并询问该功能是否需要特定的权限(privilege)才能使用。Begunkov 赞同这确实可能成为一个问题,并表示将需要某种能力检查(capability check)机制。

Christian Brauner 对该功能仍在使用 scatterlists(散列表)这一事实提出了异议,因为开发者们最终希望淘汰这一内部 API;Hellwig 回答说,dma-buf 目前仍然需要 scatterlists,因此眼下还无法避免。随后,现场针对如何移除 dma-buf 对 scatterlists 的依赖进行了一些发散式的讨论,但 Hellwig 表示,不应该为了等待这项清理工作而搁置 Begunkov 的成果。随着会议时间耗尽,大家还讨论了未来如何支持文件系统访问的问题,不过目前尚未看到相关的补丁。

四、 尖端演进:dma-buf/devmem 与 netkit 的结合

到了 2024-2026 年,为了应对容器化 AI 基础设施(如 Kubernetes 中的 GPU 容器)对网络极致性能的追求,Linux 内核网络栈发生了一次重大变革,核心就是 devmem TCP 和 netkit 的结合。

1. TCP Devmem (dma-buf 基于 TCP 的非零拷贝/零拷贝接收)

由 Google 工程师主导的 TCP Devmem 补丁集并入主线。它允许网络协议栈直接将数据接收到通过 dma-buf 分配的设备内存(Device Memory,如 GPU 显存)中。

  • 工作原理 :硬件网卡必须支持标头拆分(Header Split) 。网卡收到一个 TCP 报文时,把 TCP 标头(Header)放到系统内存中(供 CPU 协议栈处理),而把载荷(Payload,即真实的 AI 数据)直接通过 DMA 放到预先映射的 GPU dma-buf 块中。

  • 这样,网络流在经过内核 TCP 协议栈时,CPU 只处理几百字节的 Header,而 G 级别的 Payload 实现了从网卡到 GPU 的纯硬件零拷贝接收 。用户态通过一个新的 recvmsg() 标志(MSG_SOCK_DEVMEM)来获取包含 dma-buf 偏移量的元数据。

2. netkit:容器网络的下一代超车道

在云计算和 K8s 中,容器之间(或者容器与宿主机之间)通信通常使用 veth(Virtual Ethernet)设备。然而 veth 性能较差。netkit 是近两年引入的一种全新的内核虚拟网络设备(由 Meta 推进),旨在彻底取代 veth 用于容器网络。它与 eBPF 深度绑定,放弃了传统 veth 繁琐的排队重定向机制,直接在内核中进行极其高效的同宿主机容器间数据包传导。

3. 三者合一:dma-buf/devmem + netkit 的终极演进

当我们将 dma-buf/devmem 与 netkit 结合时,就诞生了当前 AI 容器网络的终极形态:容器间 GPU-to-GPU 的本地网络零拷贝。

  • 场景:在同一台物理机(宿主机)上运行了两个 GPU 容器(例如 K8s 调度的大模型分布式训练节点),Container A 的 GPU 需要发送数据给 Container B 的 GPU。

  • 演进后的路径:

    1. 两个容器的 GPU 内存都通过 dma-buf 暴露并映射。

    2. 当 Container A 发送数据时,利用 TCP Devmem 机制,数据直接驻留在 GPU 内存中。

    3. 数据包进入 netkit 设备,netkit 内部的 eBPF 程序直接识别出目标是同台机器的 Container B。

    4. netkit 绕过所有物理网卡驱动和复杂的排队逻辑,直接将该数据包的引用(Payload 依然在 dma-buf 中)重定向到 Container B 的网络接收队列。

    5. Container B 通过 Devmem 机制直接从自己的 GPU 内存或通过 P2P 访问对方的 GPU 内存读取数据。整个过程完全免除了 CPU 内存拷贝,将 AI 集群内部虚拟网络的损耗降到了接近于零的绝对极限。

五、 技术总结与演进脉络

阶段 / 领域 核心解决问题 核心技术 / 关键组件
基础 dma-buf 解决跨驱动(如 V4L2 与 GPU)内存共享 dma-buf fd, attach/map 抽象接口
RDMA 演进 解决高带宽跨集群 GPU 零拷贝通信(如 InfiniBand/RoCE) ib_umem_dmabuf, move_notify 动态页面失效机制
Netdev 演进 解决传统网络协议栈无法直接处理无 page 结构设备内存的问题 io_uring 预映射(Pre-mapping), 规避 IOMMU 频繁切换惩罚
devmem + netkit 解决 AI 容器化时代 宿主机内及跨机 TCP 网络的 GPU 极致零拷贝 TCP Devmem (Header Split) + netkit (eBPF 驱动虚拟网卡)
相关推荐
讯捷蓝达2 小时前
戴尔R720xd服务器卡死在 “Configuring Memory...Done“,换了内存还是卡?问题根本不在内存
运维·服务器
且从容.3 小时前
ubuntu安装flink
linux·ubuntu·flink
数字新视界4 小时前
动环监控信创适配在智能机房管理中的应用价值与未来展望
大数据·服务器·数据库·动环监控系统·动力与环境监控系统
爱和冰阔落4 小时前
【Linux网络】TCP 服务器如何并发处理多个客户端?从 fork、僵尸进程到双重 fork
linux·运维·服务器·网络
程序员沉梦听雨5 小时前
WSL2的安装与使用
linux·wsl
微小的鱼5 小时前
Linux的渲染与显示系统链路
android·java·linux
lisanmengmeng5 小时前
alertmanger 标签 rules 打上分组标签
linux·运维·服务器
Android系统攻城狮6 小时前
Linux FFmpeg深度解析之av_write_frame调用流程与实战(三十三)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频
上火的金鱼妹6 小时前
k8s生产实际问题排查(网关,数据库读写等)
运维·服务器
孙克旭_6 小时前
k8s基础组件与创建资源
运维·docker·容器·kubernetes