在一般情况下,Linux在初始化时,总是尽可能的将所有的物理内存映射到内核地址空间中去。如果内核地址空间起始于0xC0000000,为vmalloc保留的虚拟地址空间是128M,那么最多只能有(1G-128M)的物理内存直接映射到内核空间中,内核可以直接访问。如果还有更多的物理内存,就称为高端内存,内核不能直接访问,只能通过修改页表映射后才能进行访问。
内存分区可以使内核页分配更加合理。当系统物理内存大于1G时,内核不能将所有的物理内存都预先映射到内核空间中,这样就产生了高端内存,高端内存最适于映射到用户进程空间中。预映射的部分可直接用于内核缓冲区,其中有一小块可用于DMA操作的内存,留给DMA操作分配用,一般不会轻易分配。内存分区还可以适应不连续的物理内存分布,是非一致性内存存取体系(NUMA)的基础。
在 32 位机器上页表通常只可以存储在低端内存中。低端内存只限于物理内存的前 896 MB,同时还要满足内核其余的大部分要求。在应用程序使用了大量进程并映射了大量内存的情况下,低端内存可能很快就不够用了。在 2.6 内核中有一个配置选项叫做Highmem PTE,让页表条目可以存放在高端内存中,释放出更多的低端内存区域给那些必须放在这里的其他内核数据结构。作为代价,使用这些页表条目的进程会稍微慢一些。不过,对于那些在大量进程在运行的系统来说,将页表存储到高端内存中可以在低端内存区域挤出更多的内存。
在申请和释放较小且连续的内存空间时,使用kmalloc()和kfree()在物理内存中进行分配, 申请较大的内存空间时,使用vmalloc()。由vmalloc()申请的内存空间在虚拟内存中是连续的,它们映射到在物理内存时,可以使用不连续的物理页面,而且仅把当前访问的部分放在物理页面中。本节要介绍的内存分配函数是vmalloc。尽管这段区域在物理上可能是不连续的(要访问其中的每个页面都必须独立地调用函数__get_free_page),内核却认为它们在地址上是连续的。分配的内存空间被映射进入内核数据段中,从用户空间是不可见的-这一点上与其他分配技术不同。vmalloc发生错误时返回0(NULL地址),成功时返回一个指向一个大小为size的线性地址空间的指针。vmalloc函数在核心中所分配内存有vm_struct结构的链表所支持,如图所示。
使用vmalloc时应将<linux/vmalloc.h>包含进来。与其他内存分配函数不同的是,vmalloc返回很“高”的地址值-这些地址要高于物理内存的顶部。由于vmalloc对页表调整后允许用连续的“高”地址访问分配得到的页面,因此处理器是可以访问返回得到的内存区域的。内核能和其他地址一样地使用vmalloc返回的地址,但程序中用到的这个地址与地址总线上的地址并不相同。
用vmalloc分配得到的地址是不能在微处理器之外使用的,因为它们只有在处理器的分页单元之上才有意义。但驱动程序需要真正的物理地址时(象外设用以驱动系统总线的DMA地址),这样的地址是不能通过vmalloc函数分配的。正确使用vmalloc函数的场合是为软件分配一大块连续的用于缓冲的内存区域。注意vmalloc的开销要比__get_free_pages大,因为它处理获取内存还要建立页表。因此,不值得用vmalloc函数只分配一页的内存空间。
vmalloc分配的内核虚拟内存与kmalloc/__get_free_pages分配的内核逻辑内存位于不同的区间,不会重叠。因为内核空间被分区管理,各司其职。用户空间被分配在0-3G,3G之后紧跟着是物理内存映射区间,再后面才是vmalloc_start开始的用于vmalloc分配内存的地址空间。
ioremap用于将高内存空间的PCI缓冲区映射到用户空间。例如,如果VGA设备的帧缓冲区被映射到地址0xf0000000(典型的一个值)后,ioremap就可以建立正确的页表让处理机可以访问。而系统初始化时建立的页表只是用于访问低于物理地址空间的内存区域。系统的初始化过程并不检测PCI缓冲区,而是由各个驱动程序自己负责管理自己的缓冲区。
如果希望驱动程序能在不同的平台间移植,那么使用ioremap时就要小心。在一些平台上是不能直接将PCI内存区域映射到处理机的地址空间的,例如Alpha上就不行。此时你就不能象普通内存区域那样地对重映射区域进行访问,你要用readb函数或者其他一些I/O函数。这套函数可以在不同平台间移植。
对vmalloc和ioremap函数可分配的内存空间大小并没有什么限制,但为了能检测到程序员的犯下的一些错误,vmalloc不允许分配超过物理内存大小的内存空间。但是记着,vmalloc函数请求过多的内存空间会产生一些和调用kmalloc函数时相同的问题。
ioremap和vmalloc函数都是面向页的(它们都会修改页表),因此分配或释放的内存空间实际上都会上调为最近的一个页边界。而且,ioremap函数并不考虑如何重映射不是页边界的物理地址。
这样对内存的知识大家应该全明白了吧,谢谢大家。
联系客服