snifer

【原创】基于嵌入式系统的高端内存问题

0
阅读(2215)
在一般情况下,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分配内存的地址空间。 
使用vmalloc函数的一个例子函数是create_module系统调用,它利用vmalloc函数来获取被创建模块需要的内存空间。而在insmod调用重定位模块代码后,将会调用memcpy_fromfs函数把模块本身拷贝进分配而得的空间内。
用vmalloc分配得到的内存空间用vfree函数来释放,这就像是要用kfree函数来释放kmalloc函数分配得到的内存空间。
和vmalloc一样,ioremap也建立新的页表,但和vmalloc不同的是,ioremap实际上并不分配内存。ioremap的返回值是个虚拟地址,可以用来访问指定的物理内存区域,得到的这个虚拟地址最后要调用vfree来释放掉。