32bit系统中,系统由各个进程的3G用户空间和1G的公用内核空间组成。
32bit系统中,Linux物理内存空间分为DMA内存(DMA Zone)、低端内存(Normal Zone)、高端内存(Highmem Zone),DMA Zone一般为16M。
而64bit系统中,内存分布机制变化也十分大,可以参考博客http://blog.csdn.net/liminyu/article/details/12519729,在这里转载下,内容如下:
0x0000,0000,0000,0000 – 0x0000,7fff,ffff,f000这128T地址用于用户空间。参见定义:
#define TASK_SIZE_MAX ((1UL << 47) - PAGE_SIZE),注意这里还减去了一个页面的大小做为保护。
而0xffff,8000,0000,0000以上为系统空间地址。注意:该地址前4个都是f,这是因为目前实际上只用了64位地址中的48位(高16位是没有用的),而从地址0x0000,7fff,ffff,ffff到0xffff,8000,0000,0000中间是一个巨大的空洞,是为以后的扩展预留的。
而真正的系统空间的起始地址,是从0xffff,8800,0000,0000开始的,参见:
#define __PAGE_OFFSET _AC(0xffff,8800,0000,0000, UL)
另外0xffff,8800,0000,0000 – 0xffff,c7ff,ffff,ffff这64T直接和物理内存进行映射,
0xffff,c900,0000,0000 – 0xffff,e8ff,ffff,ffff这32T用于vmalloc/ioremap的地址空间。
而32位地址空间时,当物理内存大于896M时,由于地址空间的限制,内核只会将0~896M的地址进行映射,而896M以上的空间用做一些固定映射和vmalloc/ioremap。而64位地址时是将所有物理内存都进行映射。转载结束
回归正题,32bit系统中,低端内存是指存在逻辑地址的内存;高端内存是那些不存在逻辑地址的内存。
内核只能寻址4G,所以32bit系统被限制使用少于4G内存,不过内核地址空间虚拟映射物理内存,内核不能直接操作没有映射的物理内存,内核能处理的物理内存数量就是将物理内存映射到内核虚拟地址的1G大小空间,出去内核代码自己所占空间,系统可用内存空间会小于1G,因此系统只让内核映射896M空间,剩余的128M空间预留给映射大于4G物理内存的场景,这就是高端内存。大于4G物理内存没有逻辑地址,所以对其访问需要建立虚拟映射到内核空间的最后128M空间。
所以内核中某些数据结构必须放在低端内存中,高端内存大家都可以使用,具体看应用,常见的kmalloc,get_free_page, vmalloc等;如vmalloc比较特殊,能分配非物理地址连续的内存,其也是通过多次获取单个page,之后建立页表,其效率可想而知就很低了,它也可以分配高端内存。
而在用户态使用的内存,其实现大概为:malloc分配虚拟内存,然后在访问时,产生缺页异常而且分配物理内存,由于获得物理内存一般都是alloc_page之类的接口,然后修改页表,将其映射到该进程的用户空间中,不过用户态的程序的内存管理也基于glibc中内存管理,有点类似slab机制。
vmalloc是内核分配非连续内存和高端内存的主要方式,用户态进程当然不能使用,vmalloc就是以GFP_KERNEL | __GFP_HIGHMEM的。
void *vmalloc(unsigned long size){
return __vmalloc_node(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL, -1, __builtin_return_address(0));
},当然我说的是有MMU的构架,不然哪来的虚拟地址映射。
对于用户态使用的内存,在内核中的实现通常为:malloc分配虚拟内存-->缺页异常分配物理内存(alloc_page之类的接口),然后修改页表建立映射。
举个例子:
物理内存4G,只有开始的低端内存896M会线性映射到内核的地址空间中,其余的内存(4G-896M)可以认为是高端内存,无法直接被使用,并且它只有128M地址空间去管理超过896M的内存,且这些非线性区映射,使用上有很多限制,只有需要使用时,才去映射(如kmap用atmoic参数,当然要尽快释放。),也有永久映射的一些内存,主要也是为了特定需求。
内核线性映射896M只是个限制,如果内核只有1G,线性映射的就会小于896M,要是内存只有512M,那就更少啦。