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_notifyODP(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-bufio_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-bufscatterlists 的依赖进行了一些发散式的讨论,但 Hellwig 表示,不应该为了等待这项清理工作而搁置 Begunkov 的成果。随着会议时间耗尽,大家还讨论了未来如何支持文件系统访问的问题,不过目前尚未看到相关的补丁。

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

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

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/devmemnetkit 结合时,就诞生了当前 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 驱动虚拟网卡)
相关推荐
实心儿儿38 分钟前
Linux —— 线程控制(1)
linux·运维·服务器
筠筠喵呜喵1 小时前
Linux软件开发性能优化
linux·c++·性能优化
Bruce_kaizy1 小时前
c++ linux环境编程——文件io介绍以及open 、write 、read 三剑客深度详解
linux·服务器·c++·ubuntu·操作系统·文件io
亦良Cool2 小时前
VMware虚拟机ubuntu瘦身,解决虚拟机越用越大
linux·运维·ubuntu
星辰&与海3 小时前
KVM + QEMU虚拟化方案
linux·运维
宋浮檀s3 小时前
应急响应——恶意流量&攻击行为识别
linux·运维·网络·网络安全·应急响应
REDcker3 小时前
Linux OverlayFS详解
java·linux·运维
Royzst4 小时前
xml知识点
java·服务器·前端
TechWJ4 小时前
数据库在公司内网,出差路上想查数据怎么办?
服务器·数据库·mariadb
zizle_lin4 小时前
WSL的系统安装和部分环境配置(按需操作)
运维