2. VideoBuf2 核心概念:vb2 核心数据结构、vb2_queue、vb2_buffer、内存模型、操作回调

VideoBuf2 (vb2) 是 V4L2 框架中负责视频缓冲区管理的核心子系统。理解其核心数据结构、队列模型、内存模型以及操作回调机制,是进行 DMA 缓冲区分配与调优的基础。本章将深入剖析这些核心概念,帮助您建立扎实的底层认知。

2.1 vb2 核心数据结构全景

vb2 框架围绕几个关键数据结构构建,它们共同协作,完成从用户空间请求到内核 DMA 缓冲区分配、管理和回收的完整生命周期。下表列出了最核心的数据结构及其职责:

数据结构核心作用所属层次
vb2_queue管理整个视频流(如一个摄像头设备节点)的缓冲区队列驱动层 / 核心层
vb2_buffer代表单个缓冲区实例,包含状态、索引、操作回调核心层
vb2_v4l2_bufferV4L2 扩展结构,包含 v4l2_buffer 元数据V4L2 接口层
vb2_plane描述缓冲区内的单个平面(用于非连续格式如 NV12)核心层
vb2_mem_ops内存操作回调集合,定义分配、释放、映射等接口内存模型层
vb2_ops驱动操作回调集合,定义队列设置、缓冲区准备等接口驱动层

关键理解: vb2_queue 是“管理者”,vb2_buffer 是“被管理者”,vb2_mem_ops 和 vb2_ops 是“策略接口”。驱动开发者主要实现 vb2_ops,而内存分配策略由 vb2_mem_ops 封装。

2.2 vb2_queue:缓冲区队列的中央控制器

vb2_queue 是每个视频流实例的核心结构体。它负责跟踪所有缓冲区状态、管理空闲和已入队列表、协调内存分配,并作为驱动与 V4L2 核心交互的桥梁。

vb2_queue 核心结构示意 struct vb2_queue bufs[] 缓冲区指针数组 queued_list 入队列表 done_list 完成列表 memory 内存模型类型 type 缓冲区类型 (V4L2_BUF_TYPE_*) io_modes 支持的 IO 模式 ops (struct vb2_ops *) mem_ops (struct vb2_mem_ops *) 说明:vb2_queue 通过 bufs[] 管理所有缓冲区,通过 queued_list / done_list 实现双缓冲队列机制 ops 和 mem_ops 是驱动定制行为的入口点

调优要点: vb2_queue 的 min_buffers_needed 字段决定了驱动启动 DMA 所需的最小缓冲区数量。设置过小可能导致 DMA 饥饿,设置过大则浪费内存。对于高分辨率视频,建议根据帧大小和延迟要求动态计算此值。

2.3 vb2_buffer:缓冲区实例的生命周期

vb2_buffer 是单个缓冲区的抽象,它不直接包含数据,而是通过 planes 数组指向实际的内存区域。其状态机转换是理解 vb2 工作流程的关键。

2.3.1 缓冲区状态机

vb2_buffer 状态转换图 VB2_BUF_STATE_DEQUEUED VB2_BUF_STATE_QUEUED VB2_BUF_STATE_DONE VB2_BUF_STATE_ERROR QBUF DMA 完成 DQBU 错误 REQUEUE

2.3.2 关键字段解析

struct vb2_buffer {
    struct vb2_queue      *vb2_queue;    // 所属队列
    unsigned int           index;         // 缓冲区索引 (0..nbuffers-1)
    unsigned int           type;          // 缓冲区类型
    unsigned int           memory;        // 内存模型 (VB2_MEMORY_MMAP/USERPTR/DMABUF)
    unsigned int           num_planes;    // 平面数量
    struct vb2_plane       planes[VB2_MAX_PLANES]; // 平面数组
    enum vb2_buffer_state  state;         // 当前状态
    // ... 其他字段
};

常见误区: vb2_buffer 的 planes 数组大小固定为 VB2_MAX_PLANES(通常为 4)。对于多平面格式(如 NV12 的 2 平面),务必正确设置 num_planes 和每个 plane 的 m.fd、length、bytesused 等字段,否则会导致 DMA 操作越界或数据损坏。

2.4 内存模型:DMA 缓冲区的三种分配策略

vb2 支持三种内存模型,分别对应不同的应用场景和性能特征。驱动通过 vb2_queue 的 io_modes 字段声明支持的模型,用户空间通过 VIDIOC_REQBUFS 的 memory 字段选择。

内存模型枚举值分配方式CPU 访问典型场景
MMAPVB2_MEMORY_MMAP内核分配物理连续/非连续内存,通过 mmap 映射到用户空间直接访问映射地址摄像头预览、编码输入
USERPTRVB2_MEMORY_USERPTR用户空间分配内存,通过用户指针传递给内核直接访问用户指针需要与用户态库共享内存
DMABUFVB2_MEMORY_DMABUF通过 dma-buf 机制共享 DMA 缓冲区通过 dma-buf 接口间接访问跨设备共享(如 ISP → GPU)

2.4.1 MMAP 模型(最常用)

MMAP 是默认且最常用的模型。内核通过 vb2_mem_ops 中的 alloc() 回调分配 DMA 缓冲区,然后通过 mmap 系统调用映射到用户空间。分配的内存可以是 DMA 连续(通过 dma_alloc_coherent)或 DMA 非连续(通过 dma_buf + scatterlist),取决于驱动实现和硬件约束。

2.4.2 USERPTR 模型(零拷贝接收)

USERPTR 允许用户空间直接提供已分配的内存缓冲区。内核通过 get_user_pages 将用户页锁定并映射到 DMA 地址空间。此模型避免了内核到用户空间的数据拷贝,但要求用户空间内存必须物理连续或通过 IOMMU 支持。

2.4.3 DMABUF 模型(跨设备共享)

DMABUF 是最高级的模型,通过 dma-buf 文件描述符实现缓冲区共享。一个设备导出 dma-buf,另一个设备导入。此模型在复杂多媒体 pipeline(如 ISP → GPU → 显示)中至关重要,可避免多次内存拷贝。

性能对比: MMAP 在单设备场景下性能最优(分配快、CPU 访问直接);USERPTR 适合用户态已有内存池的场景;DMABUF 在多设备 pipeline 中延迟最低,但实现复杂度最高。对于 DMA 调优,优先选择 MMAP 并确保分配的是 DMA 连续内存。

2.5 操作回调:驱动与核心的契约

vb2 框架通过两组回调函数实现驱动与核心的解耦:vb2_ops 用于驱动逻辑控制,vb2_mem_ops 用于内存分配策略。

2.5.1 vb2_ops:驱动行为回调

回调函数调用时机驱动职责
queue_setupREQBUFS 或 STREAMON 时验证格式、设置缓冲区数量和大小
buf_prepare每个缓冲区入队前验证缓冲区参数、准备 DMA 描述符
buf_queue缓冲区入队后将缓冲区加入硬件 DMA 队列
start_streamingSTREAMON 时启动 DMA 引擎、开始传输
stop_streamingSTREAMOFF 时停止 DMA、取消未完成缓冲区
buf_finish缓冲区出队后清理 DMA 描述符、解除映射

2.5.2 vb2_mem_ops:内存操作回调

struct vb2_mem_ops {
    void *(*alloc)(struct vb2_buffer *vb, struct device *dev,
                   unsigned long size);
    void (*put)(void *buf_priv);
    struct dma_buf *(*get_dmabuf)(struct vb2_buffer *vb,
                                   void *buf_priv);
    int (*map_dmabuf)(struct vb2_buffer *vb, struct dma_buf *dbuf);
    void (*unmap_dmabuf)(struct vb2_buffer *vb, struct dma_buf *dbuf);
    void *(*get_userptr)(struct device *dev, unsigned long vaddr,
                         unsigned long size, enum dma_data_direction dir);
    void (*put_userptr)(void *buf_priv);
    // ... 其他回调
};

调优实践: 在 queue_setup 回调中,根据硬件对齐要求调整 sizeimage(例如对齐到 32 字节)。在 buf_prepare 中,提前分配 DMA 描述符并建立映射,避免在 buf_queue 中做耗时操作。对于高帧率场景,建议在 start_streaming 中预填充多个缓冲区到硬件队列。

2.6 核心概念关系总结

下图展示了 vb2_queue、vb2_buffer、vb2_mem_ops 和 vb2_ops 之间的协作关系:

vb2 核心概念协作关系 用户空间 (V4L2 应用) vb2_queue vb2_buffer[] vb2_plane[] vb2_ops (驱动回调) vb2_mem_ops (内存回调) DMA 引擎 / 硬件 说明:用户空间通过 V4L2 系统调用与 vb2_queue 交互;vb2_queue 管理 vb2_buffer 数组; 每个 vb2_buffer 通过 vb2_plane 指向实际内存;驱动通过 vb2_ops 控制流程,通过 vb2_mem_ops 管理内存分配

学习路径建议: 掌握本章核心概念后,建议阅读内核源码 drivers/media/common/videobuf2/videobuf2-core.c 中的 __vb2_queue_cancel() 和 vb2_core_qbuf() 函数,它们展示了缓冲区状态转换和队列管理的完整实现。下一章将深入 vb2_queue 的初始化与配置细节。