🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## 15.5. 快速参考 本章介绍了下列关于内存处理的符号: ### 15.5.1. 介绍性材料 ~~~ #include <linux/mm.h> #include <asm/page.h> ~~~ 和内存管理相关的大部分函数和结构, 原型和定义在这些头文件. ~~~ void *__va(unsigned long physaddr); unsigned long __pa(void *kaddr); ~~~ 在内核逻辑地址和物理地址之间转换的宏定义. ~~~ PAGE_SIZE PAGE_SHIFT ~~~ 常量, 给出底层硬件的页的大小(字节)和一个页面号必须被移位来转变为一个物理地址的位数. struct page 在系统内存映射中表示一个硬件页的结构. ~~~ struct page *virt_to_page(void *kaddr); void *page_address(struct page *page); struct page *pfn_to_page(int pfn); ~~~ 宏定义, 在内核逻辑地址和它们相关的内存映射入口之间转换的. page_address 只用在低地址页或者已被明确映射的高地址页. pfn_to_page 转换一个页面号到它的相关的 struct page 指针. ~~~ unsigned long kmap(struct page *page); void kunmap(struct page *page); ~~~ kmap 返回一个内核虚拟地址, 被映射到给定页, 如果需要并创建映射. kunmap 为给定页删除映射. ~~~ #include <linux/highmem.h> #include <asm/kmap_types.h> void *kmap_atomic(struct page *page, enum km_type type); void kunmap_atomic(void *addr, enum km_type type); ~~~ kmap 的高性能版本; 结果的映射只能被原子代码持有. 对于驱动, type 应当是 KM_USER1, KM_USER1, KM_IRQ0, 或者 KM_IRQ1. struct vm_area_struct; 描述一个 VMA 的结构. ### 15.5.2. 实现 mmap ~~~ int remap_pfn_range(struct vm_area_struct *vma, unsigned long virt_add, unsigned long pfn, unsigned long size, pgprot_t prot); int io_remap_page_range(struct vm_area_struct *vma, unsigned long virt_add, unsigned long phys_add, unsigned long size, pgprot_t prot); ~~~ 位于 mmap 核心的函数. 它们映射 size 字节的物理地址, 从 pfn 指出的页号开始到虚拟地址 virt_add. 和虚拟空间相关联的保护位在 prot 里指定. io_remap_page_range 应当在目标地址在 I/O 内存空间里时被使用. ~~~ struct page *vmalloc_to_page(void *vmaddr); ~~~ 转换一个由 vmalloc 获得的内核虚拟地址到它的对应的 struct page 指针. ### 15.5.3. 实现直接 I/O ~~~ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start, int len, int write, int force, struct page **pages, struct vm_area_struct **vmas); ~~~ 函数, 加锁一个用户空间缓冲到内存并且返回对应的 struct page 指针. 调用者必须持有 mm->mmap_sem. ~~~ SetPageDirty(struct page *page); ~~~ 宏定义, 标识给定的页为"脏"(被修改)并且需要写到它的后备存储, 在它被释放前. ~~~ void page_cache_release(struct page *page); ~~~ 释放给定的页从页缓存中. ~~~ int is_sync_kiocb(struct kiocb *iocb); ~~~ 宏定义, 返回非零如果给定的 IOCB 需要同步执行. ~~~ int aio_complete(struct kiocb *iocb, long res, long res2); ~~~ 函数, 指示一个异步 I/O 操作完成. ### 15.5.4. 直接内存存取 ~~~ #include <asm/io.h> unsigned long virt_to_bus(volatile void * address); void * bus_to_virt(unsigned long address); ~~~ 过时的不好的函数, 在内核, 虚拟, 和总线地址之间转换. 总线地址必须用来和外设通讯. ~~~ #include <linux/dma-mapping.h> ~~~ 需要来定义通用 DMA 函数的头文件. ~~~ int dma_set_mask(struct device *dev, u64 mask); ~~~ 对于无法寻址整个 32-位范围的外设, 这个函数通知内核可寻址的地址范围并且如果可进行 DMA 返回非零. ~~~ void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *bus_addr, int flag); void dma_free_coherent(struct device *dev, size_t size, void *cpuaddr, dma_handle_t bus_addr); ~~~ 分配和释放一致 DMA 映射, 对一个将持续在驱动的生命周期中的缓冲. ~~~ #include <linux/dmapool.h> struct dma_pool *dma_pool_create(const char *name, struct device *dev, size_t size, size_t align, size_t allocation); void dma_pool_destroy(struct dma_pool *pool);void *dma_pool_alloc(struct dma_pool *pool, int mem_flags, dma_addr_t *handle); void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t handle); ~~~ 创建, 销毁, 和使用 DMA 池来管理小 DMA 区的函数. ~~~ enum dma_data_direction; DMA_TO_DEVICE DMA_FROM_DEVICE DMA_BIDIRECTIONAL DMA_NONE ~~~ 符号, 用来告知流映射函数在什么方向数据移入或出缓冲. ~~~ dma_addr_t dma_map_single(struct device *dev, void *buffer, size_t size, enum dma_data_direction direction); void dma_unmap_single(struct device *dev, dma_addr_t bus_addr, size_t size, enum dma_data_direction direction); ~~~ 创建和销毁一个单使用, 流 DMA 映射. ~~~ void dma_sync_single_for_cpu(struct device *dev, dma_handle_t bus_addr, size_t size, enum dma_data_direction direction); void dma_sync_single_for_device(struct device *dev, dma_handle_t bus_addr, size_t size, enum dma_data_direction direction); ~~~ 同步一个由一个流映射的缓冲. 必须使用这些函数, 如果处理器必须存取一个缓冲当使用流映射时.(即, 当设备拥有缓冲时). ~~~ #include <asm/scatterlist.h> struct scatterlist { /* ... */ }; dma_addr_t sg_dma_address(struct scatterlist *sg); unsigned int sg_dma_len(struct scatterlist *sg); ~~~ 这个散布表结构描述一个涉及不止一个缓冲的 I/O 操作. 宏 sg_dma_address he sg_dma_len 可用来抽取总线地址和缓冲长度来传递给设备, 当实现发散/汇聚操作时. ~~~ dma_map_sg(struct device *dev, struct scatterlist *list, int nents, enum dma_data_direction direction); dma_unmap_sg(struct device *dev, struct scatterlist *list, int nents, enum dma_data_direction direction); void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction direction); void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction direction); ~~~ dma_map_sg 映射一个 发散/汇聚 操作, 并且 dma_unmap_sg 恢复这些映射. 如果在这个映射被激活时缓冲必须被存取, dma_sync_sg_* 可用来同步. /proc/dma 包含在 DMA 控制器中的被分配的通道的文本快照的文件. 基于 PCI 的 DMA 不显示, 因为每个板独立工作, 不需要分配一个通道在 DMA 控制器中. ~~~ #include <asm/dma.h> ~~~ 定义或者原型化所有和 DMA 相关的函数和宏定义. 它必须被包含来使用任何下面符号. ~~~ int request_dma(unsigned int channel, const char *name); void free_dma(unsigned int channel); ~~~ 存取 DMA 注册. 注册必须在使用 ISA DMA 通道之前进行. ~~~ unsigned long claim_dma_lock( ); void release_dma_lock(unsigned long flags); ~~~ 获取和释放 DMA 自旋锁, 它必须被持有, 在调用其他的在这个列表中描述的 ISA DMA 函数之前. 它们在本地处理器上也关闭和重新使能中断 ~~~ void set_dma_mode(unsigned int channel, char mode); void set_dma_addr(unsigned int channel, unsigned int addr); void set_dma_count(unsigned int channel, unsigned int count); ~~~ 编程 DMA 信息在 DMA 控制器中. addr 是一个总线地址. ~~~ void disable_dma(unsigned int channel); void enable_dma(unsigned int channel); ~~~ 一个 DMA 通道必须被关闭在配置期间. 这些函数改变 DMA 通道的状态. ~~~ int get_dma_residue(unsigned int channel); ~~~ 如果这驱动需要知道一个 DMA 传送在进行, 它可调用这个函数, 返回尚未完成的数据传输的数目. 在成功的 DMA 完成后, 这个函数返回 0; 值是不可预测的当数据仍然在传送时. ~~~ void clear_dma_ff(unsigned int channel); ~~~ DMA flip-flop 被控制器用来传送 16-位值, 通过 2 个 8 位操作. 它必须被清除, 在发送任何数据给处理器之前.