DMA即Direct Memory Access,是一種容許外設直接存取內存數據而沒有CPU參與的技術,當外設對於該塊內存的讀寫完成以後,DMAC經過中斷通知CPU,這種技術多用於對數據量和數據傳輸速度都有很高要求的外設控制,好比顯示設備等。編程
咱們知道,爲了提升系統運行效率,現代的CPU都採用多級緩存結構,其中就包括使用多級Cache技術來緩存內存中的數據來緩解CPU和內存速度差別問題。在這種前提下,顯而易見,若是DMA內存的數據已經被Cache緩存了,而外設又修改了其中的數據,這就會形成Cache數據和內存數據不匹配的問題,即DMA與Cache的一致性問題。爲了解決這個問題,最簡單的辦法就是禁掉對DMA內存的Cache功能,顯然,這會致使性能的下降緩存
在有MMU的計算機中,CPU看到的是虛擬地址,發給MMU後轉換成物理地址,虛擬地址再通過相應的電路轉換成總線地址,就是外設看到的地址。因此,DMA外設看到的地址實際上是總線地址。Linux內核提供了相應的API來實現三種地址間的轉換:函數
//虛擬->物理 virt_to_phys() //物理->虛擬 ioremap() //虛擬->總線 virt_to_bus() //總線->虛擬 bus_to_virt()
DMA外設並不必定能在全部的內存地址上執行DMA操做,此時應該使用DMA地址掩碼性能
int dma_set_mask(struct device *dev,u64 mask);
好比一個只能訪問24位地址的DMA外設,就使用dma_set_mask(dev,0xffffff)
ui
下面是在內核程序中使用DMA內存的流程:code
若是在驅動中使用DMA緩衝區,可使用內核提供的已經考慮到一致性的API:orm
/** * request_dma - 申請DMA通道 * On certain platforms, we have to allocate an interrupt as well... */ int request_dma(unsigned int chan, const char *device_id); /** * dma_alloc_coherent - allocate consistent memory for DMA * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices * @size: required memory size * @handle: bus-specific DMA address * * Allocate some memory for a device for performing DMA. This function * allocates pages, and will return the CPU-viewed address, and sets @handle * to be the device-viewed address. */ void * dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag) //申請PCI設備的DMA緩衝區 void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle) //釋放DMA緩衝區 void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t dma_handle ) //釋放PCI設備的DMA緩衝區 void pci_free_consistent() /** * free_dma - 釋放DMA通道 * On certain platforms, we have to free interrupt as well... */ void free_dma(unsigned int chan);
若是使用應用層的緩衝區創建的DMA申請而不是驅動中的緩衝區,可能僅僅使用kmalloc等函數進行申請,那麼就須要使用流式DMA緩衝區,此外,還要解決Cache一致性的問題。blog
/** * request_dma - 申請DMA通道 * On certain platforms, we have to allocate an interrupt as well... */ int request_dma(unsigned int chan, const char *device_id); //映射流式DMA dma_addr_t dma_map_single(struct device *dev,void *buf, size_t size, enum dma_datadirection direction); //驅動得到DMA擁有權,一般驅動不應這麼作 void dma_sync_single_for_cpu(struct device *dev,dma_addr_t dma_handle_t bus_addr,size_t size, enum dma_data_direction direction); //將DMA擁有權還給設備 void dma_sync_single_for_device(struct device *dev,dma_addr_t dma_handle_t bus_addr,size_t size, enum dma_data_direction direction); //去映射流式DMA dma_addr_t dma_unmap_single(struct device *dev,void *buf, size_t size, enum dma_datadirection direction); /** * free_dma - 釋放DMA通道 * On certain platforms, we have to free interrupt as well... */ void free_dma(unsigned int chan);