在Linux内核空间里,有三种内存区,ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM。
在64位系统上,HIGHMEM是不存在的,只有在32位系统上才会有。
在32位系统上,高于896M的物理内存称为高端内存。
内核地址空间为 3G-4G。
3G ~ 3G+896M为直接映射区,也就是说物理地址和内核虚拟地址只差3G的偏移量,比如说,内核中某个变量地址为3G+24M,那么这个变量所在的物理地址必然是24M。
内核还剩下128M的地址空间,这些是干什么用的呢?因为物理地址可能高于1G,高于1G的地方在内核里无法直接寻址,那怎么办的,这128M就是用来映射高于896M部分的内存的。举个例子,
内核里某个变量的地址是3G+1000M,那么这个变量的物理地址一般不会是1000M,当然有这个可能,因为1000M也位于高端内存。内核需要为这128M空间建立映射关系,128M映射896M~64G的部分(最多64G,开启PAE)。
这128M又分成几个区:
低于896M的部分称为直接映射区,后面分为VMALLOC区,永久映射区,固定映射区。
#define VMALLOC_OFFSET (8*1024*1024) //VMALLOC区与直接映射区的间隙,为了捕捉越界
范围是VMALLOC_START到VMALLOC_END,中间的4K是为了把分配的每段内存隔开,就像用户层的malloc,中间会有间隙。
这个VMALLOC分配内存时,会把地址映射到这个区域,举个例子,假如你用vmalloc得到了1M内存,物理地址(假设连续,实际不一定)为22G~22G+1M,返回给你的地址有可能是920M~921M。kmalloc与vmalloc的区别之一就是kmalloc会在低端内存申请。
后面的PKMAP区和FIXMAP区用法是这样的,你得到了一个高端内存的page,需要在内核里读写,但是内核不能直接操作page,需要地址,这样你调用kmap或者kmap_atomic,会把这个page映射到这两个区。当然加入这个page来自低端内存,直接返回page_address()的结果。
有一个问题一直很困扰我,假入内存不到896M呢?实际上内存不到896M,照样可以使用vmalloc。
先来看个例子
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/vmalloc.h>
- static int hello_init(void)
- {
- char *test;
- char *test1;
- test = vmalloc(8);
- test1 = kmalloc(8,0);
- printk(KERN_ALERT "%x,%d\n",test,(unsigned int)test/(1024*1024));
- printk(KERN_ALERT "%x,%d\n",test1,(unsigned int)test1/(1024*1024));
- vfree(test);
- kfree(test1);
- return 0;
- }
- static void hello_exit(void)
- {
- printk(KERN_ALERT "Goodbye,cruel world!\n");
- }
- module_init(hello_init);
- module_exit(hello_exit);
内存256M:
内存512M:
内存1024M:
可见VMALLOC区不一定是在896M+8M到896M+8M+128M。
kmalloc返回的地址都在物理内存范围内,而vmalloc返回的地址就不一样了。我们再看上面各个区的分布图,发现有个high_memory,其实这是个变量,
上面是在物理内存为256的情况下打印high_memory,(除以1024*1024),可见这个high_memory是根据物理内存算出来的,具体代码如下
- #ifdef CONFIG_HIGHMEM
- highstart_pfn = highend_pfn = max_pfn;
- if (max_pfn > max_low_pfn)
- highstart_pfn = max_low_pfn;
- e820_register_active_regions(0, 0, highend_pfn);
- sparse_memory_present_with_active_regions(0);
- printk(KERN_NOTICE "%ldMB HIGHMEM available.\n",
- pages_to_mb(highend_pfn - highstart_pfn));
- num_physpages = highend_pfn;
- high_memory = (void *) __va(highstart_pfn * PAGE_SIZE-1)+1;
- #else
- e820_register_active_regions(0, 0, max_low_pfn);
- sparse_memory_present_with_active_regions(0);
- num_physpages = max_low_pfn;
- high_memory = (void *) __va(max_low_pfn * PAGE_SIZE - 1)+1;
- #endif
VMALLOC_START在high_memory后面8M开始,一般应该是高于物理内存的。
我们来理清几个问题:
低于896M的物理内存与虚拟内存在内核空间里是一一对应的,这句话是有问题的,需要分情况说。
如果总得物理内存低于896M,假设为A,对于内核中的一个地址,如果小于A那么这个地址与物理地址只差一个偏移量,如果大于A,那么肯定位于VMALLOC区。如果一个物理地址为X的页被内存使用,即使X小于896,它对应的虚拟地址也有可能不是3G+896。
越写越乱,先写到这吧。
本文转自nxlhero 51CTO博客,原文链接:http://blog.51cto.com/nxlhero/711805,如需转载请自行联系原作者