Linux内核驱动(四):2、内核地址空间

一、内核空间

        内核空间是由内核负责映射,它并不会跟着进程改变,是固定的。

二、高端内存:物理内存地址超过896M以上的内存,我们就称之为高端内存

三、内核空间分布图

Linux内核驱动(四):2、内核地址空间_第1张图片



(1)内核空间的地址范围位于虚拟地址的 3G-4G位置处,大小为1G。

(2)直接映射区(Direc Memory Regiont):之所以称之为直接映射区是因为,该区域的线性地址和物理地址之间存在线性转换关系:线性地址 = 3G + 物理地址; Eg: 物理地址区间 0x100000--0x200000映射到线性空间就是3G+0x100000-3G+0x200000

(3)动态映射区(Vmalloc Region):该区域的地址由内核函数vmalloc来进行分配,注意用vmalloc进行分配,只是获取一段内存地址,没有实际为它分配物理内存(vmalloc的v 就是virtual的意思);其特点是线性空间连续,但对应的物理空间不一定连续。

(4)永久内存映射区(PKMap Region):对于896MB以上的高端内存,可使用该区域来访问(注意KMAP区只是一个访问物理高端内存的一个窗口,所以它本身只有4M大;换句话说我们可以通过4M的窗口访问物理内存所有896M以上的内存地址)。访问方式:

           I、使用alloc_page(__GFP_HIGHMEM)分配高端内存页(用alloc_page分配的是物理内存,而物理内存地址无法直接使用,必须先映射,所以还需要使用下面的函数)

           II、使用kmap函数将分配到的高端内存映射到该区域

(5)固定映射区:固定映射区中的虚拟地址与物理内存空间地址之间的映射是固定的。固定映射区中每个地址项都服务于特定的用途,如ACPI_BASE等

四、

【内核空间内存动态申请】

主要包括三个函数:kmalloc(), __get_free_pages, vmalloc

kmalloc(), __get_free_pages申请的内存位于物理地址映射区,而且在物理上也是连续的,它们与真实的物理地址只有一个固定的偏移,因此存在较简单的转换关系。而vmalloc申请的内存位于vmalloc虚拟内存分配区(这些区都是以线性地址为度量),它在虚拟内存空间给出一块连续的内存区,实质上,这片连续的虚拟内存在物理内存中并不一定连续,而vmalloc申请的虚拟内存和物理内存之间也没有简单的换算关系。

 

因为vmalloc申请的在虚拟内存空间连续的内存区在物理内存中并不一定连续,可以想象为了完成vmalloc,新的页表需要被建立,因此,知识调用vmalloc来分配少量内存是不妥的。

一般来讲,kmalloc用来分配小于128K的内存,而更大的内存块需要用vmalloc来实现。

 

【虚拟地址与物理地址关系】

对于内核物理内存映射区的虚拟内存(用kmalloc(), __get_free_pages申请的),使用virt_to_phys()phys_to_virt()来实现物理地址和内核虚拟地址之间的互相转换。它实际上,仅仅做了3G的地址移位。

 

上述方法适用于常规内存(内核物理内存映射区),高端内存的虚拟地址与物理地址之间不存在如此简单的换算关系。因为它涉及到了分离物理页的页表控制机制。  

 

ioremap

ARM中,设备的寄存器或者存储块的这部分空间属于内存空间的一部分,我们称之为IO内存。

 

在内核中访问IO内存之前,我们只有IO内存的物理地址,这样是无法通过软件直接访问的,需要首先用ioremap()函数将设备所处的物理地址映射到内核虚拟地址空间(3GB~4GB)。然后,才能根据映射所得到的内核虚拟地址范围,通过访问指令访问这些IO内存资源。

 

在将I/O内存资源的物理地址映射成核心虚地址后,理论上讲我们就可以象读写RAM那样直接读写I/O内存资源了。为了保证驱动程序的跨平台的可移植性,我们应该使用Linux中特定的函数来访问I/O内存资源,而不应该通过指向核心虚地址的指针来访问。

 

mmap

mmap映射一个设备,意味着使用户空间的一段地址关联到设备内存上,这使得只要程序在分配的地址范围内进行读取或者写入,实际上就是对设备的访问。这种数据传输是直接的,不需要用到内核空间作为数据转移的中间站。

 

remap_page_range函数的功能是构造用于映射一段物理地址的新页表,实现了内核空间与用户空间的映射。

 

在内核驱动程序的初始化阶段,通过ioremap()将物理地址映射到内核虚拟空间;在驱动程序的mmap系统调用中,使用remap_page_range()将该块ROM映射到用户虚拟空间。这样内核空间和用户空间都能访问这段被映射后的虚拟地址。

 

Ioremap

进程空间、内核空间、IO内存

其中,后面两个指的是同一段物理内存区域,只是一个为虚拟地址,一个为物理地址。进程空间和内核空间对应着不同的物理地址,它们之间的数据传递,是实际的数据的拷贝。

 

Mmap

进程空间IO内存

其中,进程空间mmap得到的那段虚拟地址跟IO内存对应着同一段物理地址。这个过程没有额外的数据中转,读写都直接针对硬件的物理地址进行。

 

一般来讲,小数据量的传输用ioremap()就足够了,

 

IO内存的一般访问方法】

1.       首先是调用request_mem_region()申请资源,即告诉内核,本驱动正在使用这段物理内存,其他驱动不得访问它们。在设备驱动模块加载或open()函数中进行。

2.       接着将寄存器地址通过ioremap()映射到内核空间虚拟地址,之后就可以通过Linux设备访问编程接口访问这些设备的寄存器了。在设备驱动初始化、write(),read(),ioctl()函数中进行。

3.       访问完成之后,应对ioremap()申请的虚拟地址进行释放,并释放release_mem_region()申请的IO内存资源。在设备驱动模块卸载或release()函数中进行。


















     

你可能感兴趣的:(Linux内核驱动(四):2、内核地址空间)