日期:2014-05-16  浏览次数:20875 次

Linux内核中的内存(一)

版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127  

 

内核把物理页作为内存管理的基本单位,尽管处理器的最小可寻址单位通常为字节,但是MMU(内存管理单元,管理内存并把虚拟地址转换为物理地址的硬件)通常以页作为单位进行处理。大多数32位体系结构支持4KB的页,内核用struct page结构表示系统的物理页,这个page与物理页相关,而并非与虚拟页相关。页的拥有者可能是用户空间进程、动态分配的内核数据、静态内核代码或页高速缓存等。

 

由于有些页位于内存特定的物理地址上,所以不能将其用于一些特定的任务。故内核把页分成不同的区,分别是ZONE_DMAZONE_DMA32ZONE_NORMALZONE_HIGHMEM(高端内存,其中的页并不能永久地映射到内核地址空间)。每个区都用struct zone结构体表示,对于32位的X86上,ZONE_DMA包含的页在0-16MB的内存范围里,ZONE_NORMAL是从16MB896MB的所有物理内存,ZONE_HIGHMEM是高于896MB的所有物理内存。需要注意的是,不是所有的体系结构都定义了全部区,比如X86-64没有ZONE_HIGHMEM区。

 

如果需要以页为单位的一族连续物理页时,尤其只需要一两页时,有些低级页函数很有用。alloc_page(返回页结构指针)+page_address=__get_free_pages(返回逻辑地址指针)

 

如果需要连续的物理页,除了使用上面说的低级页函数外,还可以使用kmalloc函数,传递给这个函数的常用标志GFP_KERNELGPF_ATOMIC。其中GPF_ATOMIC用在中断处理程序、下半部、持有自旋锁以及其他不能睡眠的地方;GPF_KERNEL是常用分配方法,可能会阻塞,在睡眠安全时用在进程上下文中;GPF_NOIO这种分配可以阻塞,但不会启动磁盘I/O

 

如果不需要连续的物理地址,只要连续的虚拟地址,可以使用vmalloc函数,但是由于物理地址的不连续性,导致通过vmalloc获得的页必须一个个的进行映射,这就导致比直接内存映射大的多的TLB(转换旁路缓存,用来缓存虚拟地址到物理地址的映射关系)抖动,所以,迫不得已不用该函数,典型的就是为了获得大块内存时,比如模块动态加载。

 

如果需要从高端内存进行分配,我们知道高端内存(物理地址高于896MB)中的页被映射到3GB-4GB上,我们应该使用alloc_pages获得页指针,而不能用__get_free_pageskmalloc,因为这两个函数返回的都是逻辑地址,而不是page结构,这两个函数分配的内存当前有可能还没有映射到内核的虚拟空间。如果需要将获得的页永久映射到内核地址空间,可以使用kmap函数,这个函数可以睡眠,因此只能在进程上下文使用,因为允许永久映射的数量是有限的,所以当不需要高端内存时,应该用kunmap解除映射。如果需要获得的页临时映射到内核地址空间,可以使用kmap_atomic函数,它不能睡眠,可以用在中断处理程序中,同样释放用kunmap_atomic函数。