本文摘自(偶有较小改动)《Linux 设备驱动开发详解》(宋宝华 编著;人民邮电出版社;),留作纪念。
——Living Park
第11章 内存与I/O访问
11.1 CPU与内存和I/O
11.1.1 内存空间与I/O空间
可以通过函数指针调用一个没有函数体的”函数”,本质上只是换一个地址执行。
内存空间是必须的,而I/O空间是可选的。
11.1.2 内存管理单元MMU
MMU辅助操作系统进行内存管理,提供虚拟地址和物理地址的映射、内存访问权限保护和Cache缓存控制等硬件支持。
TLB(Translation Lookaside Buffer)是转换表的Cache,TTW(Translation Table Walk)是转换表漫游,执行成功后结果应写入TLB。
MMU并非对所有处理器都是必须的,Linux 2.6 支持不带MMU的处理器。
11.2 Linux内存管理
Linux系统中,进程的4GB内存空间被分为两个部分——用户空间与内核空间。用户空间地址一般分布为0~3GB,剩下的3~4GB为内核空间。
在3~4GB之间的内核空间中,从低地址到高地址依次为:物理内存映射区-隔离带-vmalloc虚拟内存分配器-隔离带-高端内存映射区-专用页面映射区-保留区。
11.3内存存取
11.3.1 用户空间内存动态申请
使用malloc()和free()。
11.3.2 内核空间内存动态申请
Kmallo()和_ _get_free_pages()申请的内存位于物理内存映射区,而且在物理上是连续的,它们与真实的物理地址只有一个固定的偏移,而vmalloc()则不是。
1. kmalloc()
void *kmalloc(size_t size, int flags);,它的底层依赖_ _get_free_pages的实现。使用kmalloc()申请的内存应使用kfree()释放。
2. _ _get_free_pages()
其实现中调用了alloc_pages()函数,申请的内存应使用free_page()或free_pages()释放。
3. vmalloc()
用它申请的内存应使用vfree()释放。
4. slab与内存池
slab操作:kmem_cache_create();kmem_cache_alloc();kmem_cache_free();kmem_cache_destroy()。
内存池操作:mempool_create();mempool_alloc();mempool_free();mempool_destroy()。
11.3.3 虚拟地址与物理地址关系
对于常规内存:virt_to_phys()实现内核虚拟地址转化为物理地址;phys_to_virt()则相反。
11.4设备I/O端口和I/O内存的访问
11.4.1 Linux I/O端口和I/O内存访问接口
1. I/O端口
inb(),outb();inw(),outw();inl(),outl();insb(),outsb();insw(),outsw();insl(),outsl()。
2. I/O内存
使用前需将物理地址映射到虚拟地址:ioremap(),释放使用iounmap()。
ioread8(),ioread16(),ioread32(),iowrite8(),iowrite16(),iowrite32();ioread8_rep(),ioread16_rep (),ioread32_rep (),iowrite8_rep (),iowrite16_rep (),iowrite32_rep ();
memcpy_fromio(),memcpy_toio();memset_io()。
3. 把I/O端口映射到内存空间
ioport_map(),ioport_unmap()。
11.4.2 申请与释放I/O端口和I/O内存
1. I/O端口
request_region(),release_region()。
2. I/O内存
request_mem_region(),release_mem_region()。
11.4.3 设备I/O端口和I/O内存访问流程
I/O端口可以直接访问,也可以映射为内存后访问。
11.4.4 将设备地址映射到用户空间
1. 内存映射与VMA
mmap()函数可使得用户空间能直接访问设备的物理地址,必须以PAGE_SIZE为单位进行映射。
这种能力对于显示适配器一类的设备非常有意义。
2. nopage()函数
nopage()当访问的页不存在时会被内核自动调用,还可用于RAM映射。
11.5 I/O内存静态映射
Linux移植到目标电路板的过程中,通常会建立外设I/O内存物理地址到虚拟地址的静态映射,这个映射通过在电路板对应的map_desc结构体数组中添加新的成员来完成,设备驱动中访问映射后I/O内存时,直接在map_desc中该段的虚拟地址上加上相应的偏移即可,不再需要使用ioremap()。
11.6 DMA
DMA是一种无须CPU的参与就可以让外设与系统内存之间进行双向数据传输的硬件机制。
11.6.1 DMA与Cache一致性
在采用Cache的系统中,同样一个数据可能既存在于Cache中,也存在于主存中,数据若不一样则具有不一致性。
解决不一致性问题的最简单方法是直接禁止DMA目标地址范围内内存的Cache功能。
11.6.2 Linux下的DMA编程
基于DMA的硬件使用总线地址而非物理地址,总线地址是从设备角度上看到的内存地址,物理地址则是从CPU角度上看到的未经转换的内存地址。
申请和释放DMA通道:request_dma(), free_dma()。