今年工作这么难找?小黄同志要努力啊········咱可不能拖社主义的后退啊·····
linux将物理内存分成每个4K大小的页,来进行管理。
linux中 号称 PFN, 是 page frame number的缩写。 取值范围是 0----(memory size >> 12)。
但是由于物理内存映射的关系,物理内存的0地址对应到到系统上并不是物理地址的0。 例如:s3c2440上,内存的地址是从0x30000000开始的。当连接ram后,ram的0地址在s3c2440看来,就是0x30000000. 所以在系统中 pfn的值 应该等于 (physical address - memory base address) >> 12 。
但是linux中,用的又都是虚拟地址,所以要先将 virtual address转换成 physical address 才行。 所以在linux中有个宏定义 将内核线性空间的虚拟地址和pfn转换:
#define virt_to_pfn(kaddr) (__pa(kaddr) >> PAGE_SHIFT) //__pa() 将虚拟地址转化成物理地址 #define pfn_to_virt(pfn) __va((pfn) << PAGE_SHIFT)
所以pfn在linux内核中应该对应的是页帧号。可以通过简单的转换找到对应的物理地址。
每个物理上的页,内核给与之分配了一个描述符来描述: page。 pfn可以和page进行转换:
#define page_to_pfn __page_to_pfn #define pfn_to_page __pfn_to_page
虚拟地址也可以和page进行转换:
#define virt_to_page(addr) (mem_map + (((unsigned long)(addr)-PAGE_OFFSET) >> PAGE_SHIFT)) #define page_to_virt(page) ((((page) - mem_map) << PAGE_SHIFT) + PAGE_OFFSET)
所有的page结构存放在mem_map中,方便进行管理。
由于linux是将物理内存分成4K大小的页来进行管理的。所以在软件上也会设置MMU进行匹配对应。后面叙述。
linux将4G的虚拟空间分成了用户空间和内核空间。用户空间是0---3G,内核空间是3G---4G。 从内核空间可以访问到用户空间,但是从用户空间必须通过系统调用来能访问内核空间。
linux的内核空间分为
如上所述,内核空间要可以同时访问内核空间和用户空间,说白了就是所有空间。 因为内核要访问所有的空间,即所有内存。但是当内存大于1G的时候,而内核空间只有1G,那么内核就要用一种方法,将大于1G的RAM映射到自己1G的空间。使用自己1G的空间来访问大于1G的ram。并且,内核还不能完全使用自己1G的空间来完全映射所有内存,因为还有一些其他空间,如:寄存器等,还有一些空间留作管理等。
所以内核要综合考虑,既要用自己的有限的空来,来访问可能大于1G的内存。还有留下一写空间来使用其他功能。 就有了上面的分类。
来分以上的两个方面讨论,1 如果使用有限的空间访问大于1G的内存。 2. 剩下的空间做什么用。
1. linux内核将内存 分为 低位内存和高位内存。 低位内存是<896M的内存,高位内存是>896M的内存。
低于896M的内存使用线性映射,映射到 图中的 直接映射区,可以直接访问。在这个位置的 虚拟内核 和 物理内存是 线性的关系。 物理内存 = 虚拟内存 - 3G。
高位896M的内存使用 图中的KMAP区,KMAP区只有4M,所以高位内存只能部分映射 到KMAP区,然后访问。 访问结束后 取消映射,继续映射其他的高位内存。当然这是有算法的。 高位也有 临时映射和永久映射和vmalloc的方式。当申请高位内存时,如alloc_page(),返回的并不是内存的起始线性地址,因为高位地址的起始地址可能会大于32bit,也不会在896M的区间内,所以,alloc_page返回的是高端内存的 page 描述符。显然,他的 page 的描述符 会在《896M的空间内。得到描述符后,会进行再次的映射映射到 KMAP区。 因为嵌入式中内存还很少有>896M的,这里不做讨论高位。
低位内存的映射: 在内存的启动阶段,在head.S文件中,会在C0004000的地方建立一个临时的映射表。 仅仅将kernel code的空间映射到直接映射区。并执行。 在执行到后面,会在start_kernel>paging_init函数中,正式的映射。 将所有的低位内存 映射到 直接映射区, 也会映射中断向量表 到FFFF0000,和0地址。这样就完成了低位内存的内核空间的映射。 (高位内存好像也会在paging_init中进行简单的映射等,没仔细看code)。
2. 剩下的空间做什么? 还剩下 动态映射区 和 固定映射区。
动态映射区主要是用来vmalloc用的。由于在 直接映射区是线性映射。kmalloc等 申请的空间都是映射在直接映射区,因为虚拟地址连续的,所以物理地址也是连续的。但是内核会不短malloc和不断的free,就会造成很多不连续的地址。(当然内会也会有自己的算法,尽量减少不连续的地址)。 所以就 找了这段空间,用vmalloc能申请出虚拟地址上连续物理地址上不连续的空间,就映射在动态映射区这段空间中。 注意:vmalloc申请的空间也可能是 在高位内存中。
固定映射区用来映射一些用于特殊用途的内存,如ACPI_BASE等。
在进程建立的时候,内核会给他分配相应的页表等,来让其访问。
由以上可知,linux对内存的管理是按页管理的。但是由于在正常的使用中,有时候会 kmalloc(sizeof(struct ....));等这样。内核这样分配给其一个page显然是不合适的,浪费的。 所以有了slab/slub 模块。
slab是在2.4和2.6.**之前的一个分配器。 slub是新版本的一个分配器。
在创建相应的 slab时,他会预先申请一连串的 相应的struct 结构体空间。 当进行申请时候,会直接从list中找一个free的空间 返回。这样节省了时间,也节省了空间。
但是我们又时候还会随意的申请malloc(10)这样的空间。所以slab在linux启动初始化时,会预先申请 2 4 8 16 32 64 128......等这样字节的空间,当要malloc(10)时候,他会自动从 16的预先得到的空间中取出一个返回。
忽然想起: vmalloc时候,后面的size是 按page对齐的。所以当vmalloc申请小数据时候,是按照kmalloc申请的,当大数据时候,才会不连续的page分配。
PAE技术:以前的32bit的芯片最多寻址4G空间,但是由时候4G的内存是远远不够的。所以intel有了PAE技术(physical address extension)。将地址线从32扩展到了36bit,达到了内存64G。
节点:理论上说,cpu不管访问内存中的任何地址,所需的时间应该是一样的,但是在mips等一些结构上着个结论是不成立的。所以linux2.6支持将内存分成不同的节点。 cpu访问一个节点内的任意空间所需的时间是相等的。在arm等结构中,应该用不到这个节点。但是为了兼容arm中的节点数为1.
zone:有些cpu访问地址是由限制的,如DMA的destination address必须是《32M的,所以linux 也将内存分成了zone_dma ,zone_normal, zone_highmem区域。 当alloc_page时,可以对区域进行选择。
保留页框池: != 内存池。
伙伴系统算法:系统中会不断的alloc_page,free_page,这样会知道有很多小的内存块。 linux有伙伴算法,伙伴算法会自动寻找和他相连的内存块是不是free的,如果是,就将其和自己连接到一起成为一个大的内存块。
blog:http://write.blog.csdn.net/postlist
深入理解linux内核