在 Linux 工控机平台上进行工业相机采集时,图像数据通常具有高带宽、连续流式传输、低延迟处理 等特点。对于高分辨率、高帧率相机,如果采用传统 read() 方式从驱动读取图像帧,系统很容易受到内存复制开销的限制。
例如,一帧 2448 × 2048 的 8-bit 灰度图像约为 5 MB,若采集帧率为 60 fps,则单路相机的数据吞吐量已接近:
text
2448 × 2048 × 1 × 60 ≈ 300 MB/s
如果每一帧都经历:
text
设备 DMA -> 内核缓冲区 -> copy_to_user -> 用户缓冲区 -> 图像算法
那么 CPU 会承担大量内存搬运工作,影响采集稳定性和后续算法处理能力。
mmap 的核心价值在于:将驱动管理的缓冲区映射到用户进程虚拟地址空间,使用户态程序能够直接访问驱动缓冲区中的图像数据,从而避免每帧通过 read() / copy_to_user() 进行整帧复制。
需要强调的是,mmap 本身并不负责采集数据,也不负责 DMA 传输。它的本质是:
建立用户进程虚拟地址空间与内核/设备缓冲区之间的页表映射关系
1. mmap 在相机采集链路中的作用
以 PCIe 工业相机采集卡、CoaXPress 采集卡或 Camera Link 帧采集卡为例,典型数据路径如下:
用户空间
内核空间
硬件层
图像数据
DMA 写入
mmap 映射
同一块物理内存
直接访问
📷 Camera
🎞️ Frame Grabber / PCIe Device
💾 Frame Buffer
(Host Memory 中的 DMA 缓冲区)
🖧 设备驱动
👤 用户态程序
说明 :
实线箭头表示数据流或操作方向。
虚线箭头表示
mmap后用户态程序直接访问同一块物理内存(零拷贝)。驱动负责管理帧缓冲区并执行
mmap映射。
未使用 mmap 时,数据路径通常为:
text
设备 DMA -> 内核 buffer -> copy_to_user -> 用户 buffer -> 算法处理
使用 mmap 后,数据路径可简化为:
用户空间
内核空间
DMA 传输
mmap 映射
用户态直接访问
(同一物理内存)
📟 设备
💾 驱动管理的 DMA Buffer
同一块物理内存
👤 用户进程
因此,在相机驱动场景中,mmap 主要用于解决以下问题:
text
减少整帧图像复制
降低 CPU 内存搬运开销
提升高帧率采集稳定性
为用户态图像处理提供直接访问帧缓冲区的能力
2. mmap 与 malloc、read 的区别
理解 mmap 前,必须区分它与 malloc()、read() 的语义差异。
malloc() 是用户态内存分配:
text
用户进程虚拟地址 -> 普通匿名页 / heap
read() 是内核向用户态复制数据:
text
kernel buffer -> copy_to_user -> user buffer
而 mmap() 是建立映射关系:
text
用户进程虚拟地址
|
| 页表映射
v
驱动 buffer / DMA buffer / 设备内存
用户态代码中看到的是普通指针:
c
void *ptr = mmap(NULL, // addr: 由内核自动选择映射起始地址
size, // length: 映射区域的大小
PROT_READ | PROT_WRITE, // prot: 映射区域可读可写
MAP_SHARED, // flags: 共享映射,修改会写回文件
fd, // fd: 被映射文件的文件描述符
offset); // offset: 文件中的映射起始偏移
但该指针背后的内存并不是普通 malloc() 得到的用户堆内存,而是由驱动在 file_operations->mmap() 中映射给用户进程的缓冲区。
因此,mmap 的关键不在于"分配内存",而在于"把某段由内核或设备管理的内存区域映射到用户态"。
3. 用户态 mmap 参数在相机驱动中的含义
典型用户态调用如下:
c
int fd = open("/dev/my_camera", O_RDWR);
void *frame = mmap(NULL,
frame_size,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
buffer_index * frame_size);
if (frame == MAP_FAILED) {
perror("mmap");
return -1;
}
各参数在相机驱动场景中的含义如下。
addr
通常传入 NULL,表示由内核自动选择用户进程中的虚拟地址区域。这是最常见、也最具可移植性的用法。
length
表示映射长度,通常为单帧图像缓冲区大小,或者某个 buffer slot 的大小。
例如:
text
frame_size = width × height × bytes_per_pixel
prot
表示用户态对该映射区域的访问权限。相机采集场景通常至少需要:
c
PROT_READ
如果用户态需要写入该区域,例如用于双向共享、调试或用户态填充数据,则可能使用:
c
PROT_READ | PROT_WRITE
flags
相机驱动通常使用:
c
MAP_SHARED
因为驱动和用户态需要共享同一块缓冲区 。若使用 MAP_PRIVATE,则会引入 copy-on-write 语义,不适合设备采集缓冲区共享。
fd
表示设备文件描述符,例如:
text
/dev/video0
/dev/my_camera
offset
offset 是 mmap 中非常关键的参数。它通常不是随意的文件偏移,而是用户态和驱动之间约定的 buffer 偏移或 buffer 编号。
在 V4L2 中,用户态通常通过 VIDIOC_QUERYBUF 获取每个 buffer 对应的 m.offset,再将该 offset 传给 mmap()。
需要注意的是:offset 必须按页大小对齐,否则 mmap() 会失败。
按页对齐就是让 offset 变成 页大小的整数倍。
例如页大小是 4096 字节,那么合法的 offset 必须是0,4096,8192,12288
4. 驱动侧 mmap 的执行逻辑
对于一个简化的字符设备相机驱动,通常会实现如下文件操作接口:
c
static const struct file_operations cam_fops = {
.owner = THIS_MODULE,
.open = cam_open,
.release = cam_release,
.mmap = cam_mmap,
.poll = cam_poll,
.unlocked_ioctl = cam_ioctl,
};
当用户态调用:
c
mmap(NULL, size, PROT_READ, MAP_SHARED, fd, offset);
内核最终会进入驱动实现的:
c
static int cam_mmap(struct file *filp, struct vm_area_struct *vma)
其中,vm_area_struct 描述的是用户进程中的一段虚拟地址区域:
text
vma->vm_start 用户虚拟地址起始地址
vma->vm_end 用户虚拟地址结束地址
vma->vm_pgoff 用户传入 offset 转换后的页偏移
vma->vm_page_prot 页保护属性
驱动侧 mmap 通常需要完成以下工作:
text
1. 校验用户请求映射的长度是否合法
2. 根据 vma->vm_pgoff 定位对应的帧缓冲区
3. 检查该缓冲区是否允许被 mmap
4. 设置 VMA 属性
5. 将驱动管理的内存页映射到用户进程地址空间
简化示例:
c
static int cam_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct cam_dev *cam = filp->private_data; // 取出当前摄像头设备对象
unsigned long size = vma->vm_end - vma->vm_start; // 用户请求映射的大小
unsigned long index = vma->vm_pgoff; // mmap offset 转换后的页偏移,这里当作 buffer 下标
if (index >= cam->num_buffers) // buffer 下标越界,严格对应驱动的buffer
return -EINVAL; // 参数无效
if (size > cam->buffers[index].size) // 用户请求映射大小超过 buffer 实际大小,严格对应驱动的buffer大小
return -EINVAL; // 参数无效
vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; // 禁止扩展,禁止 core dump
return dma_mmap_coherent(cam->dev, // 设备对象
vma, // 用户空间虚拟内存区域
cam->buffers[index].cpu_addr, // DMA buffer 的内核虚拟地址
cam->buffers[index].dma_addr, // DMA buffer 的 DMA 地址,给硬件用
cam->buffers[index].size); // DMA buffer 大小
}
这里的关键点是:用户态传入的 offset 会被驱动解释为某个 buffer 的索引或偏移,因此驱动必须严格校验 offset 和 length,防止越界映射或非法访问内核内存。
5. mmap 与 DMA 的关系
在工业相机驱动中,mmap 往往与 DMA 一起出现,但二者属于不同层面的机制。
- DMA 解决的问题是:设备如何将图像数据写入主机内存
- mmap 解决的问题是:用户进程如何访问这块已经写入图像数据的内存
完整流程通常如下:
👤 用户进程
📡 采集卡 / PCIe 设备
🔧 设备驱动
配置 DMA 地址
中断
分配 DMA buffer
获得 CPU 虚拟地址 & 设备 DMA 地址
将 DMA 地址配置给采集卡
中断处理: 标记 buffer 状态为 DONE
设备通过 DMA 写入图像数据
DMA 完成后触发中断
用户态通过 mmap 映射 buffer
通过 mmap 后的地址
直接访问图像数据
(零拷贝)
需要特别注意,Linux DMA API 明确区分:
text
CPU 虚拟地址:供 CPU 访问
DMA 地址:供设备进行 DMA 访问
即:
c
void *cpu_addr;
dma_addr_t dma_addr;
dma_addr_t 不是 CPU 可以直接解引用的地址。系统中可能存在 IOMMU、总线地址转换或 bounce buffer,因此不能简单地将虚拟地址通过 virt_to_phys() 转换后直接交给硬件。
错误示例:
c
device_reg = virt_to_phys(buffer);
推荐方式:
c
cpu_addr = dma_alloc_coherent(dev, size, &dma_addr, GFP_KERNEL);
或在 streaming DMA 场景下:
c
dma_addr = dma_map_single(dev, cpu_addr, size, DMA_FROM_DEVICE);
对于相机采集,DMA 方向通常为:
c
DMA_FROM_DEVICE
因为图像数据是从设备传输到主机内存。
6. 相机 mmap buffer 的生命周期管理
mmap 本身只负责建立映射关系,并不负责缓冲区同步。因此,在相机驱动开发中,真正复杂的是 buffer 生命周期和所有权管理。
一个帧缓冲区通常会经历以下状态:
text
FREE
-> QUEUED
-> ACTIVE_DMA
-> DONE
-> USER_PROCESSING
-> FREE / QUEUED
含义如下:
text
FREE 空闲 buffer,可重新入队
QUEUED 用户已提交给驱动,等待硬件使用
ACTIVE_DMA 设备正在向该 buffer 写入数据
DONE DMA 完成,等待用户态取帧
USER_PROCESSING 用户态正在处理该帧
在任何时刻,都必须明确 buffer 的所有权:
text
属于设备:CPU 不应随意访问
属于驱动:用户态不应修改
属于用户态:驱动不能重新交给硬件
如果所有权管理不严格,就可能出现:
text
图像撕裂
帧数据前后不一致
半帧新数据半帧旧数据
低概率花屏
帧序号错乱
偶发崩溃
因此,mmap 必须配合队列机制、中断通知、poll/epoll 或 ioctl 状态机一起使用。仅仅把一块内存映射到用户态,并不能构成可靠的相机采集框架。
7. V4L2 mmap 模型的工程价值
如果工业相机或采集卡希望以标准 Linux 视频设备形式暴露给用户态,推荐使用 V4L2 的 mmap streaming 模型。
V4L2 的 mmap 采集流程如下:
text
open("/dev/video0")
-> VIDIOC_QUERYCAP
-> VIDIOC_S_FMT
-> VIDIOC_REQBUFS
-> VIDIOC_QUERYBUF
-> mmap
-> VIDIOC_QBUF
-> VIDIOC_STREAMON
-> poll / select / epoll
-> VIDIOC_DQBUF
-> 处理图像
-> VIDIOC_QBUF
-> ...
-> VIDIOC_STREAMOFF
-> munmap
其核心思想是:
text
REQBUFS:请求驱动分配一组帧缓冲区
QUERYBUF:查询每个 buffer 的大小和 mmap offset
mmap:将 buffer 映射到用户进程地址空间
QBUF:用户将空 buffer 提交给驱动
STREAMON:启动采集
DQBUF:用户取出已完成的图像帧
QBUF:用户处理完成后归还 buffer
STREAMOFF:停止采集
这种模型的优点是:
text
接口标准化
支持多 buffer 队列
支持 poll/epoll
可被 OpenCV、GStreamer、FFmpeg 等生态复用
便于后续扩展 DMABUF 零拷贝路径
从工程角度看,如果目标是工业级稳定性和生态兼容性,建议优先使用 V4L2 + videobuf2,而不是完全自定义一套 mmap ioctl 协议。
8. 用户态 V4L2 mmap 采集流程
下面代码用于说明 mmap 在用户态采集流程中的位置。
c
#define BUFFER_COUNT 4 // 定义视频采集使用的缓冲区数量(4个,构成循环队列)
// 自定义结构体,用于记录每个缓冲区的用户空间映射信息
struct frame_buffer {
void *start; // mmap 映射后的用户态虚拟地址
size_t length; // 缓冲区大小(字节)
};
struct frame_buffer buffers[BUFFER_COUNT]; // 存放4个缓冲区的映射信息
// 打开 V4L2 视频设备(通常是摄像头或采集卡),以可读写方式
int fd = open("/dev/video0", O_RDWR);
/* 1. 请求驱动分配 buffer */
struct v4l2_requestbuffers req = {0}; // 请求结构体,先全部置零
req.count = BUFFER_COUNT; // 请求4个缓冲区
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 缓冲区类型:视频捕获
req.memory = V4L2_MEMORY_MMAP; // 内存交互方式:内存映射(mmap)
// 向驱动发送 VIDIOC_REQBUFS 命令,请求分配缓冲区
ioctl(fd, VIDIOC_REQBUFS, &req);
/* 2. 查询 buffer 信息,并 mmap 到用户态 */
// 遍历所有已分配的缓冲区(驱动可能实际分配的数量在 req.count 中)
for (int i = 0; i < req.count; i++) {
struct v4l2_buffer buf = {0}; // V4L2 缓冲区描述符,清零
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 类型:视频捕获
buf.memory = V4L2_MEMORY_MMAP; // 内存类型:mmap
buf.index = i; // 要查询的缓冲区索引
// 查询第 i 个缓冲区的详细信息(长度、偏移量等)
ioctl(fd, VIDIOC_QUERYBUF, &buf);
buffers[i].length = buf.length; // 记录缓冲区长度
// 将内核空间的缓冲区映射到用户空间,获得可直接访问的地址
buffers[i].start = mmap(NULL, // 让内核选择映射地址
buf.length, // 映射长度
PROT_READ | PROT_WRITE, // 可读可写
MAP_SHARED, // 共享映射,修改会写回内核
fd, // 设备文件描述符
buf.m.offset); // 设备内存中的偏移量
}
/* 3. 将所有 buffer 入队,交给驱动 */
// 把每个缓冲区放入驱动的输入队列,准备接收视频数据
for (int i = 0; i < req.count; i++) {
struct v4l2_buffer buf = {0}; // 缓冲区描述符
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i; // 指定要入队的缓冲区索引
// VIDIOC_QBUF:将缓冲区放入驱动队列(Queue Buffer)
ioctl(fd, VIDIOC_QBUF, &buf);
}
/* 4. 启动视频流 */
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 要启动的流类型
// VIDIOC_STREAMON:通知驱动开始采集视频,硬件将把数据填入已入队的缓冲区
ioctl(fd, VIDIOC_STREAMON, &type);
/* 5. 循环取帧 */
while (running) { // running 为用户控制标志(例如 int running = 1)
struct v4l2_buffer buf = {0}; // 缓冲区描述符
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
// VIDIOC_DQBUF:从驱动取出一个已经填好数据的缓冲区(Dequeue Buffer)
// 若无数据则阻塞,直到有帧捕获完成
ioctl(fd, VIDIOC_DQBUF, &buf);
// 根据出队缓冲区中的索引,找到对应的用户空间映射地址
void *image = buffers[buf.index].start;
/*
* image 指向的就是 mmap 映射后的帧缓冲区。
* 用户态可直接在该地址上进行图像处理。
*/
// 调用用户定义的图像处理函数,传入帧数据地址和实际数据大小(字节)
process_image(image, buf.bytesused);
// 处理完后,将该缓冲区重新入队,让驱动继续填充新数据
ioctl(fd, VIDIOC_QBUF, &buf);
}
这段代码中,真正访问图像数据的位置是:
c
void *image = buffers[buf.index].start;
该地址并不是 read() 拷贝出来的用户态 buffer,而是驱动帧缓冲区在用户进程中的映射地址。
9. 驱动侧 buffer 映射方式
不同的 buffer 来源,对应不同的 mmap 实现方式。
9.1 DMA coherent buffer
适用于 DMA descriptor、状态区、小型控制结构,也可用于简单帧缓冲示例。
c
cpu_addr = dma_alloc_coherent(dev,
size,
&dma_addr,
GFP_KERNEL);
映射到用户态:
c
dma_mmap_coherent(dev,
vma,
cpu_addr,
dma_addr,
size);
优点是 CPU 和设备之间的一致性处理相对简单。
缺点是 coherent memory 通常不适合无限制地用于大规模图像帧池,尤其是在多相机、高分辨率、高帧率场景下,可能面临内存资源紧张或性能不可控的问题。
9.2 Streaming DMA buffer
大尺寸图像帧更常采用 streaming DMA 模型:
c
dma_addr = dma_map_single(dev,
cpu_addr,
size,
DMA_FROM_DEVICE);
设备完成 DMA 写入后,CPU 访问前应根据平台一致性要求进行同步:
c
dma_sync_single_for_cpu(dev,
dma_addr,
size,
DMA_FROM_DEVICE);
当用户处理完成、该 buffer 重新交给设备前,应再次同步给设备:
c
dma_sync_single_for_device(dev,
dma_addr,
size,
DMA_FROM_DEVICE);
在 x86 平台上,缓存一致性问题有时不明显;但在 ARM、嵌入式工控机、国产 SoC 或非一致性 DMA 平台上,忽略 cache 同步会导致非常典型的图像异常,例如:
text
局部花屏
旧帧残留
图像块错乱
偶发数据不一致
9.3 remap_pfn_range / vm_insert_page
如果驱动管理的是特定物理页、设备 BAR 空间或非标准内存区域,可能需要使用:
c
remap_pfn_range()
vm_insert_page()
vm_iomap_memory()
但对于工业相机图像 DMA buffer,通常更推荐使用 DMA API、V4L2 videobuf2 或内核已有缓冲区框架,而不是自行通过 virt_to_phys() 和 remap_pfn_range() 拼接实现。
10. mmap 的性能收益来源
mmap 的性能收益主要来自减少内核态到用户态的重复数据复制。
假设单帧图像为 5 MB,帧率为 60 fps:
text
图像吞吐量约为 300 MB/s
若使用 read():
text
设备 DMA 到内核 buffer:300 MB/s
内核 copy_to_user 到用户 buffer:额外复制 300 MB/s
后续算法或显示模块可能再次复制
若使用 mmap:
text
设备 DMA 到驱动 buffer:300 MB/s
用户态直接访问映射后的同一 buffer
避免额外整帧 copy_to_user
当然,mmap 并不意味着完全没有开销。它仍然涉及:
text
页表映射
TLB 行为
cache 一致性维护
DMA 同步
buffer 队列管理
ioctl / poll 系统调用
但对于大帧图像流而言,避免整帧复制通常可以显著降低 CPU 占用并提升采集稳定性。
11. mmap 不是绝对意义上的端到端零拷贝
在相机系统中,需要准确区分 mmap 能够优化的边界。
PCIe / CoaXPress / Camera Link 采集卡
这类设备中,mmap 可以非常接近理想模型:
text
采集卡 DMA -> 驱动 DMA buffer -> 用户态 mmap 直接访问
此时,mmap 是主数据通路中的关键机制。
GigE Vision + Aravis
对于标准 GigE Vision 相机,普通数据路径通常是:
text
相机发送 UDP/GVSP 数据包
-> 网卡 DMA 到内核网络缓冲区
-> 网络协议栈 / packet socket 接收
-> Aravis 重组图像帧
-> ArvBuffer
-> 用户处理
在这种架构中,mmap 不一定能实现"相机直接 DMA 到最终图像 buffer"。因为网卡 DMA 的目标通常是网络接收缓冲区,而不是应用最终使用的图像矩阵。
因此,对于 Aravis 这类用户态采集框架,优化重点往往是:
text
减少上层二次复制
优化 socket buffer
优化 packet ring
调整接收线程优先级
提升网络接收稳定性
合理复用 ArvBuffer
而不是简单地将 PCIe 采集卡的 mmap + DMA 模型套用到 GigE Vision 场景。
USB3 Vision
USB3 Vision 场景下,底层 DMA 主要由 USB host controller 完成。用户态通常通过 libusb 或相应传输层库获得数据。因此,mmap 在该路径中的作用不如 PCIe 采集卡场景直接,更多需要关注 USB 传输队列、buffer 数量、host controller 带宽和线程调度。
12. mmap 与 DMABUF 的关系
mmap 和 DMABUF 经常同时出现在高性能图像系统中,但它们解决的问题不同。
mmap 解决的是:
text
CPU 用户态如何访问驱动 buffer
DMABUF 解决的是:
text
多个设备或驱动之间如何共享同一个 DMA buffer
例如,在相机采集后直接送入 GPU、DRM 显示或 NPU 推理时,更理想的路径是:
text
Camera DMA
-> V4L2 buffer
-> 导出 DMABUF fd
-> GPU / NPU / DRM 导入同一个 buffer
-> 显示或推理
而不是:
text
Camera DMA
-> mmap 到 CPU
-> CPU memcpy 到 GPU/NPU 输入 buffer
因此可以这样理解:
text
mmap:面向 CPU 用户态访问
DMABUF:面向设备间 buffer 共享
在现代工业视觉系统中,如果目标是端到端低延迟、低 CPU 占用,通常应同时考虑:
text
V4L2 mmap
DMABUF export / import
GPU / NPU 零拷贝输入
13. 相机 mmap 驱动开发中的常见问题
13.1 将 mmap 误认为同步机制
mmap 只负责地址映射,不负责通知用户态某一帧是否已经采集完成。
帧完成通知仍然需要依赖:
text
硬件中断
wait queue
poll / select / epoll
ioctl DQBUF
eventfd
否则,用户态可能读取到正在 DMA 写入的 buffer。
13.2 缺少 buffer ownership 管理
必须明确每个 buffer 当前属于谁:
text
设备拥有:CPU 不应访问
驱动拥有:用户态不应修改
用户态拥有:驱动不能重新提交给设备
否则极易出现帧撕裂、脏数据和随机异常。
13.3 mmap offset 缺少严格校验
用户传入的 offset 会影响驱动映射哪一块内存,因此驱动必须检查:
text
offset 是否页对齐
offset 是否对应合法 buffer
映射长度是否越界
访问权限是否匹配
buffer 是否允许 mmap
13.4 混淆 dma_addr_t 与 CPU 地址
dma_addr_t 是设备用于 DMA 的地址,不是 CPU 指针。
正确理解应为:
text
CPU 访问:cpu_addr
设备访问:dma_addr
二者不能混用。
13.5 忽略 cache coherency
在非 cache-coherent 平台上,设备 DMA 写入后,CPU 直接读取可能读到旧数据。
必须根据 DMA API 规则执行:
text
dma_sync_single_for_cpu()
dma_sync_single_for_device()
13.6 只使用单 buffer
单 buffer 无法有效支撑采集与处理并行,容易造成丢帧。
工业相机采集通常至少需要多 buffer ring,例如:
text
buffer 0:用户态正在处理
buffer 1:设备正在 DMA
buffer 2:已完成,等待用户取帧
buffer 3:空闲,等待重新入队
总结
在工业相机驱动开发中,mmap 的核心作用是:
text
将驱动管理的图像帧缓冲区映射到用户进程虚拟地址空间,
使用户态程序能够直接访问 DMA 写入后的图像数据,
从而避免 read/copy_to_user 带来的整帧复制开销。
更准确地说,mmap 解决的是用户态访问驱动 buffer 的方式 ;DMA 解决的是设备向主机内存写入数据的方式 ;而 V4L2、videobuf2、DMABUF 等机制则进一步解决了buffer 队列管理、跨设备共享和生态兼容性问题。
对于 PCIe、CoaXPress、Camera Link 等采集卡类设备,mmap + DMA 通常是高性能采集驱动的核心数据路径。对于 GigE Vision、USB3 Vision 等标准相机,mmap 仍然是理解高性能数据通路的重要基础,但实际优化还需要结合网络栈、USB 传输层、Aravis buffer 管理以及系统调度策略综合考虑。
mmap 的本质不是复制数据,而是建立映射;在工业相机驱动中,它让用户态直接访问驱动中的 DMA 图像缓冲区,是高吞吐、低 CPU 占用采集系统的重要基础。
参考资料
-
Linux man-pages:
mmap(2)说明
mmap()、munmap()、MAP_SHARED、PROT_READ/WRITE、offset 页对齐等基础语义。 -
Linux Kernel Documentation:V4L2 Streaming I/O Memory Mapping
说明 V4L2 mmap 采集流程以及
REQBUFS / QUERYBUF / QBUF / DQBUF / STREAMON / STREAMOFF模型。 -
Linux Kernel Documentation:DMA API
说明
dma_addr_t、DMA direction、streaming DMA、coherent DMA、cache synchronization 等驱动开发关键概念。 -
Linux Kernel Documentation:DMA-BUF / V4L2 DMABUF
说明 DMA buffer 在多设备之间共享的机制,以及 V4L2 中 DMABUF 的导出和导入方式。