在嵌入式开发和驱动程序编写中,DMA(直接内存访问)传输的正确性直接关系到系统稳定性。许多开发者会遇到这样的困惑:为什么使用memcpy
的DMA传输正常,而直接赋值指针却会导致错误? 本文将通过底层原理分析和代码实验,揭示这一现象的根本原因。
一、两种写法的直观对比
1. 正确写法:memcpy拷贝数据
/* 正确写法 */
__u8 *pBuff = malloc(mats_sz); // 预先分配缓冲区
memcpy(pBuff, (__u8*)rd_rio_transfer.user_mmap_handle[0], mats_sz);
2. 错误写法:直接指针赋值
/* 错误写法 */
__u8 *pBuff = NULL;
pBuff = (__u8*)rd_rio_transfer.user_mmap_handle[0]; // 直接赋值指针
二、底层原理深度分析
1. 内存地址的本质差异
关键概念:
- 虚拟地址 (Virtual Address):CPU看到的地址空间
- 物理地址 (Physical Address):实际内存硬件的地址
- DMA地址 (Bus Address):外设看到的地址
内存操作对比:
操作类型 | 地址类型 | 内存区域属性 |
---|---|---|
malloc 分配 |
虚拟地址(可能非物理连续) | Cacheable, 可写 |
user_mmap_handle |
用户空间映射的虚拟地址 | 可能带特殊属性 |
DMA传输目标地址 | 物理地址或IOVA(IO虚拟地址) | Non-cacheable |
实验验证:
在Linux内核中打印地址类型:printk("user_mmap_handle virt: %p, phys: %llx\n", rd_rio_transfer.user_mmap_handle[0], virt_to_phys(rd_rio_transfer.user_mmap_handle[0]));