第11章 内存与I/O访问

来源:互联网 发布:windows defender 评测 编辑:程序博客网 时间:2024/04/29 12:56

本文摘自(偶有较小改动)《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)是转换表的CacheTTW(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 DMACache一致性

       在采用Cache的系统中,同样一个数据可能既存在于Cache中,也存在于主存中,数据若不一样则具有不一致性。

       解决不一致性问题的最简单方法是直接禁止DMA目标地址范围内内存的Cache功能。

11.6.2 Linux下的DMA编程

       基于DMA的硬件使用总线地址而非物理地址,总线地址是从设备角度上看到的内存地址,物理地址则是从CPU角度上看到的未经转换的内存地址。

申请和释放DMA通道:request_dma(), free_dma()