dma-buf 由浅入深(二) —— kmap / vmap
dma-buf 由浅入深(一) —— 最简单的 dma-buf 驱动程序
dma-buf 由浅入深(二) —— kmap / vmap
dma-buf 由浅入深(三) —— map attachment
dma-buf 由浅入深(四) —— mmap
dma-buf 由浅入深(五) —— File
dma-buf 由浅入深(六) —— begin / end cpu_access
dma-buf 由浅入深(七) —— alloc page 版本
dma-buf 由浅入深(八) —— ION 简化版
前言
在上一篇《最简单的 dma-buf 驱动程序》中,我们学习了编写 dma-buf 驱动程序的三个基本步骤,即 dma_buf_ops 、 dma_buf_export_info、 dma_buf_export()。在本篇中,我们将在 exporter-dummy 驱动的基础上,对其 dma_buf_ops 的 kmap / vmap 接口进行扩展,以此来演示这两个接口的使用方法。
dma-buf 只能用于 DMA 硬件访问吗?
在内核代码中,我们见得最多的 dma-buf API 莫过于 dma_buf_attach()
、dma_buf_map_attachment()
,以至于有些小伙伴会问:dma-buf 难道只能给 DMA 硬件来访问吗?当然不是!在上一篇的 概念 小节中就曾讲过,dma-buf 本质上是 buffer 与 file 的结合,因此它仍然是一块 buffer。不要看它带了 dma 字样就被迷惑了,dma-buf 不仅能用于 DMA 硬件访问,也同样适用于 CPU 软件访问,这也是 dma-buf 在内核中大受欢迎的一个重要原因。
正因如此,我才决定将 dma_buf_kmap()
/ dma_buf_vmap()
作为 dma-buf 系列教程的第二篇文章来讲解,因为这两个接口使用起来实在是比 DMA 操作接口简单太多了!
dma-buf 只能分配离散 buffer 吗?
当然不是!就和内核中 dma-mapping 接口一样,dma-buf 既可以是物理连续的 buffer,也可以是离散的 buffer,这最终取决于 exporter 驱动采用何种方式来分配 buffer。
因此为了尽量让读者易于理解,本篇特意使用了内核中最简单、最常见的 kzalloc()
函数来分配 dma-buf,自然,这块 buffer 就是物理连续的了。
CPU Access
从 linux-3.4 开始,dma-buf 引入了 CPU 操作接口,使得开发人员可以在内核空间里直接使用 CPU 来访问 dma-buf 的物理内存。
- dma-buf: add support for kernel cpu access
如下 dma-buf API 实现了 CPU 在内核空间对 dma-buf 内存的访问:
dma_buf_kmap()
dma_buf_kmap_atomic()
dma_buf_vmap()
(它们的反向操作分别对应各自的 unmap 接口)
通过 dma_buf_kmap() / dma_buf_vmap() 操作,就可以把实际的物理内存,映射到 kernel 空间,并转化成 CPU 可以连续访问的虚拟地址,方便后续软件直接读写这块物理内存。因此,无论这块 buffer 在物理上是否连续,在经过 kmap / vmap 映射后的虚拟地址一定是连续的。
上述的3个接口分别和 linux 内存管理子系统(MM)中的 kmap()、 kmap_atomic() 和 vmap() 函数一一对应,三者的区别如下:
函数 | 说明 |
---|---|
kmap() | 一次只能映射1个page,可能会睡眠,只能在进程上下文中调用 |
kmap_atomic() | 一次只能映射1个page,不会睡眠,可在中断上下文中调用 |
vmap() | 一次可以映射多个pages,且这些pages物理上可以不连续,只能在进程上下文中调用 |
- 从 linux-4.19 开始,dma_buf_kmap_atomic() 不再被支持。
- dma_buf_ops 中的 map / map_atomic 接口名,其实原本就叫 kmap / kmap_atomic,只是后来发现与 highmem.h 中的宏定义重名了,为了避免开发人员在自己的驱动中引用 highmem.h 而带来的命名冲突问题,于是去掉了前面的“k”字。
想了解更多关于 kmap() 、vmap() 的信息,推荐阅读参考资料中的《Linux内核内存管理架构》一文。
示例程序
本示例分为 exporter 和 importer 两个驱动。
首先是 exporter 驱动,我们基于上一篇的 exporter-dummy.c,对其 exporter_kmap() 和 exporter_vmap() 进行扩展,具体如下:
exporter-kmap.c
#include <linux/dma-buf.h>
#include <linux/module.h>
#include <linux/slab.h>struct dma_buf *dmabuf_exported;
EXPORT_SYMBOL(dmabuf_exported);static void *exporter_kmap(struct dma_buf *dmabuf, unsigned long page_num)
{return dmabuf->priv;
}static void *exporter_kmap_atomic(struct dma_buf *dmabuf, unsigned long page_num)
{return dmabuf->priv;
}static void *exporter_vmap(struct dma_buf *dmabuf)
{return dmabuf->priv;
}static void exporter_release(struct dma_buf *dmabuf)
{kfree(dmabuf->priv);
}...static const struct dma_buf_ops exp_dmabuf_ops = {.map = exporter_kmap,.map_atomic = exporter_kmap_atomic,.vmap = exporter_vmap,.release = exporter_release,...
};static struct dma_buf *exporter_alloc_page(void)
{DEFINE_DMA_BUF_EXPORT_INFO(exp_info);struct dma_buf *dmabuf;void *vaddr;vaddr = kzalloc(PAGE_SIZE, GFP_KERNEL);exp_info.ops = &exp_dmabuf_ops;exp_info.size = PAGE_SIZE;exp_info.flags = O_CLOEXEC;exp_info.priv = vaddr;dmabuf = dma_buf_export(&exp_info);sprintf(vaddr, "hello world!");return dmabuf;
}static int __init exporter_init(void)
{dmabuf_exported = exporter_alloc_page();return 0;
}module_init(exporter_init);
然后我们再编写一个 importer 驱动,用于演示如何在 kernel 空间,通过 dma_buf_kmap()
/ dma_buf_vmap()
接口操作 exporter 驱动导出的 dma-buf。
importer-kmap.c
#include <linux/dma-buf.h>
#include <linux/module.h>
#include <linux/slab.h>extern struct dma_buf *dmabuf_exported;static int importer_test(struct dma_buf *dmabuf)
{void *vaddr;vaddr = dma_buf_kmap(dmabuf, 0);pr_info("read from dmabuf kmap: %s\n", (char *)vaddr);dma_buf_kunmap(dmabuf, 0, vaddr);vaddr = dma_buf_vmap(dmabuf);pr_info("read from dmabuf vmap: %s\n", (char *)vaddr);dma_buf_vunmap(dmabuf, vaddr);return 0;
}static int __init importer_init(void)
{return importer_test(dmabuf_exported);
}module_init(importer_init);
示例描述:
- exporter 通过 kzalloc 分配了一个 PAGE 大小的物理连续 buffer,并向该 buffer 写入了“hello world!” 字符串;
- importer 驱动通过 extern 关键字导入了 exporter 的 dma-buf,并通过 dma_buf_kmap()、dma_buf_vmap() 函数读取该 buffer 的内容并输出到终端显示。
开发环境
内核源码 | 4.14.143 |
示例源码 | hexiaolong2008-GitHub/sample-code/dma-buf/02 |
开发平台 | Ubuntu14.04/16.04 |
运行平台 | my-qemu 仿真环境 |
运行
在 my-qemu 仿真环境中执行如下命令:
# insmod /lib/modules/4.14.143/kernel/drivers/dma-buf/exporter-kmap.ko
# insmod /lib/modules/4.14.143/kernel/drivers/dma-buf/importer-kmap.ko
将看到如下打印结果:
read from dmabuf kmap: hello world!
read from dmabuf vmap: hello world!
注意:执行 insmod 命令时,必须先加载 exporter-kmap.ko,后加载 importer-kmap.ko,否则将出现符号依赖错误。
或者直接使用“modprobe importer_kmap”命令来自动解决模块依赖问题。
结语
通过本篇,我们学习了 dma_buf_kmap()
、dma_buf_vmap()
函数的底层实现,以及如何使用这两个 API,它们是 CPU 在 kernel 空间访问 dma-buf 的典型代表。在下一篇,我们将一起来学习如何通过 DMA 硬件来访问 dma-buf 的物理内存。
参考资料
wahaha02博客:Linux内核内存管理架构
i915 drm selftests: mock_dmabuf.c
上一篇:《dma-buf 由浅入深(一)—— 对简单的 dma-buf 驱动程序》
下一篇:《dma-buf 由浅入深(三)—— map attachment》
文章汇总:《DRM(Direct Rendering Manager)学习简介》
dma-buf 由浅入深(二) —— kmap / vmap相关推荐
- v4l2 use V4L2_MEMORY_MMAP方式导出为 DMA BUF fd 方式使用
V4L2_MEMORY_MMAP 导出 fd 需要使用 vb2_ioctl_expbuf (只能使用于VB2_MEMORY_MMAP 方式). int buffer_export(int v4lfd, ...
- STM32 串口DMA收发(二)
STM32 串口DMA收发数据 一.STM32 DMA简介与功能说明 1.STM32F4 DMA简介 DMA(Direct memory access),即直接存储器访问.用于在外设与存储器之间以及存 ...
- linux内存管理:kmap、vmap、ioremap
目录 散列表也是哈希表 kmap实现 page_address_map pkmap_count page_address_slot 哈希函数 kmap函数实现 kmap_init kmap kmap_ ...
- dma-buf 由浅入深(三) —— map attachment
dma-buf 由浅入深(一) -- 最简单的 dma-buf 驱动程序 dma-buf 由浅入深(二) -- kmap / vmap dma-buf 由浅入深(三) -- map attachmen ...
- dma-buf 由浅入深(一) —— 最简单的 dma-buf 驱动程序
dma-buf 由浅入深(一) -- 最简单的 dma-buf 驱动程序 dma-buf 由浅入深(二) -- kmap / vmap dma-buf 由浅入深(三) -- map attachmen ...
- dma-buf 由浅入深(六) —— begin / end cpu_access
dma-buf 由浅入深(一) -- 最简单的 dma-buf 驱动程序 dma-buf 由浅入深(二) -- kmap / vmap dma-buf 由浅入深(三) -- map attachmen ...
- dma-buf 由浅入深(五) —— File
dma-buf 由浅入深(一) -- 最简单的 dma-buf 驱动程序 dma-buf 由浅入深(二) -- kmap / vmap dma-buf 由浅入深(三) -- map attachmen ...
- dma-buf 由浅入深(四) —— mmap
dma-buf 由浅入深(一) -- 最简单的 dma-buf 驱动程序 dma-buf 由浅入深(二) -- kmap / vmap dma-buf 由浅入深(三) -- map attachmen ...
- dma-buf 由浅入深(八) —— ION 简化版
dma-buf 由浅入深(一) -- 最简单的 dma-buf 驱动程序 dma-buf 由浅入深(二) -- kmap / vmap dma-buf 由浅入深(三) -- map attachmen ...
最新文章
- cutterman 导出html,电脑中如何使用cutterman插件
- Mahout推荐算法API详解
- linux 挂载u盘区别不到,linux系统下为什么不能挂载U盘
- 常用的7大排序算法汇总
- caffe硬件条件配置选择+LeNet识别MNIST(第四章读书笔记)
- 深度学习(十二)——Winograd(2)
- APP技巧:手机连接WiFi后,移动数据流量要不要关闭,看完你就懂了!
- 养肝粥,用电脑过度人群必备! - 健康程序员,至尚生活!
- socks5协议详细说明
- 一淘搜索之网页抓取系统分析与实现(4)- 实现总结
- 小红书话题笔记是什么意思?小红书话题的形式有哪些?
- Linux命令全家桶以及vim/gcc/gdb/makefile/yum
- Virut.ce-感染型病毒分析报告
- 系统分析师易错题整理
- webstorm最新激活方法。绝对有效
- 优声云打印机打印模板介绍
- UE5引擎 PC端的Landscape渲染浅分析
- 记一次医院挂号抢号流程
- 南昌航空大学计算机操作系统,南昌航空大学计算机操作系统期末考试题及答案...
- cocos creator 划动屏幕以移动摄像机
热门文章
- msp430g2553输出PWM波
- C语言实现YUV420sp图像剪裁
- 神经网络通俗理解和理解,卷积神经网络通俗理解
- 补充知识——结构光之解相位求解的相关问题
- NCE开放可编程培训交流
- 李开复:AI 创业的十个真相 | 深度
- 内存缓存(from memory cache)和硬盘缓存(from disk cache) 的区别
- 交接箱、配线箱、分纤箱……宽带接入工程中的各种箱体有何区别
- 蓝桥杯 — 奖券数目(有些人很迷信数字,比如带“4”的数字)
- python技术培训机构排名