LDD DMA访问内存

DMA 直接内存访问,

DMA是设备与内存之间不经过cpu直接传输数据的一种机制,CPU读取设备数据每次需要经过读取指令,执行指令,读取数据的过程,所以有一部分时间花费在读取指令和执行指令的过程;DMA在设备与内存之间传输数据时不需要执行指令,而且cpu在DMA传输数据的同时可以执行其他程序,极大的提高了计算机读取设备数据的能力;


DMA读取数据过程需要的设备有:DMA缓冲区,具有DMA能力的设备,DMAC(DMA控制器)

1,DMA缓冲区,DMA缓冲区是内存中存放DMA数据的内存空间,DMA缓冲区分配最大的困难是:DMA缓冲区需要在物理内存中连续的内存空间,即连续的物理页帧;

      所以通常分配即使比128K还小的内存时也会分配失败;

      为了解决这个问题,通常采取6中措施:在内核引导时通过mem=31M 参数预留物理内存顶端的物理页帧,

                                                                             通过在主板北条芯片中集成IOMMU单元来实现设备的总线地址虚拟化,设备在虚拟地址空间能够映射连续的虚拟地址空间,而虚拟地址通过IOMMU映射的物理页帧可以不连续;

                                                                             分散和聚集操作:

2,总线地址(设备使用的设备地址):

      CPU与总线复用地址线和数据线,所以CPU的地址空间与总线地址空间一致,在x86架构下面(地址总线位宽为32位)都为4G,总线地址为物理地址,总线地址是从设备的角度来看总线的寻址空间;


3,DMA地址映射(物理地址和总线地址映射,这个物理地址是CPU访问内存时通过地址总线传输的物理地址);

      Linux内核假设,CPU使用32位地址总线访问内存,设备使用32位总线地址进行DMA操作,如果设备寻址位不同,则用dma_set_mask(device *dev,u64 mask)更新设备的寻址能力;

所申请内存的物理地址必须要在设备的dma_mask寻址范围内(dma_mask表示与设备寻址能力对应的位)。为了确保由kmalloc申请的内存在dma_mask中,驱动程序需要定义板级相关的标志位来限制分配的物理内存范围(比如在x86上,GFP_DMA用于保证申请的内存在可用物理内存的前16Mb空间,可以由ISA设备使用);

      Linux对DMA地址映射的实现分为两种情况,两种情况通过对CPU缓存的处理来划分,由于在DMA操作期间不访问CPU缓存,如果对内存数据修改而CPU cache数据未修改,CPU就会产生错误的结果,因此Linux内核对映射分为一下两种情况:

         1》一致性映射:在DMA操作期间,是CPU cache失效,这样就CPU读取数据的时候就会直接从内存加载然后写入cache,由于从内存直接加载数据,内存的访问速度没有cache快,所以就会有性能的损失;

               api接口:void *dma_alloc_coherent(device *dev,size_t  size,dma_addr_t dma_handle); 函数返回虚拟地址,在dma_handle中保存总线地址(DMA访问内存的物理地址),将dma_handle写入DMAC基地址寄存器;

                               这个函数负责两方面操作:缓冲区分配和映射;

                              void dma_free_coherent(device *dev,void *cpu_addr,,size_t size,dma_addr_t dma_handle);

                               这个函数为缓冲区释放;

警告:内存一致性操作基于高速缓存行(cache line)的宽度。为了可以正确操作该API创建的内存映射,该映射区域的起始地址和结束地址都必须是高速缓存行的边界(防止在一个高速缓存行中有两个或多个独立的映射区域)。因为在编译时无法知道高速缓存行的大小,所以该API无法确保该需求。因此建议那些对高速缓存行的大小不特别关注的驱动开发者们,在映射虚拟内存时保证起始地址和结束地址都是页对齐的(页对齐会保证高速缓存行边界对齐的)。

         2》流式映射:在DMA操作开始的时候,如果是读操作,避免读取到过期的数据,首先flush CPU cache,将CPU cache的数据写入到内存里面,然后在从内存里面读取最新数据,如果写操作,则使CPU cache失效,这样DMA操作完毕,CPU直接从内存读取最新数据;

               api接口:dma_addr_t dma_map_single(device *dev,void *cpu_addr,size_t size,enum dma_data_direction dir);

                                 enum dma_data_direction 枚举值:DMA_TO_DEVICE,DMA_FROM_DEVICE,DMA_BIDIRECTIONAL;

                     这个函数只负责映射,因此需要先分配缓冲区,然后将缓冲区void *cpu_addr传人该函数来映射;

                              void dma_unmap_single(device *dev,dma_addr_t dma_handle,size_t size,enum dma_data_direction dir);

                    这个函数为取消地址映射;


(需完善)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值