分享

理解linux内核内存管理(32bit版)

 mzsm 2015-09-28
作者:新浪微博(@NP等不等于P

计算机学习微信公众号(jsj_xx)

内存管理是操作系统核心功能,尽管有点老(过时),我们还是以经典的32位x86为例(之后会分析x86_64),看看linux内核的内存管理。参考linux kernel source code 4.0,为之后分析x86_64版本的内存管理做热身(如有错误,还望指正,谢谢)!

1 内存区划分

有三种类型内存区(假设物理内存大于896M):

  • ZONE_DMA(0到16M)

  • ZONE_NORMAL(16M到896M)

  • ZONE_HIGHMEM(大于896M)

其中,ZONE_DMA和ZONE_NORMAL称为低端内存,ZONE_HIGHMEM称为高端内存(后面会详述高端内存)。

2 内存的申请使用

有三种申请函数:

  • alloc_pages()支持高端内存,返回page指针。

  • __get_free_pages()仅支持低端内存,返回内核线性地址(内核逻辑地址)。

  • kmalloc()支持低端内存的字节粒度,实质是调用了__get_free_pages()。

在申请时,有三种标志(属性):

  • 动作属性

    __GFP_WAIT之类的。

  • 区类型属性

    __GFP_DMA则仅从ZONE_DMA区申请,__GFP_HIGHMEM则从ZONE_HIGHMEM或ZONE_NORMAL申请。此标志位空表示从ZONE_NORMAL或ZONE_DMA申请。

  • 第三种属性是前两种的组合而已。

我们关注一下kmalloc():

  • 如果有GFP_KERNEL,则由于可能睡眠,只能用户进程上下文。

  • 如果有GFP_ATOMIC,则可以用于中断或软中断场景。

  • 如果有GFP_DMA,则只能从ZONE_DMA申请,一般用于设备驱动。

3 高端内存

32bit x86的内存管理中,很重要的就是高端内存的概念,我们详细讨论一下。

3.1 高端内存基础
首先要明确,使用高端内存由于会构建页表所以会损失些效率。用到高端内存的时机:

  • alloc_pages(GFP_HIGHUSER)

  • vmalloc()->alloc_pages(GFP_HIGUSER)

内核一般不用高端内存,除非装载模块时可能会用到vmalloc()。
高端内存又分三种:

  • 固定
    kmap()完成,PKMAP_BASE到FIXADDR_START的4M,数量有限,故可能睡眠,一般用在进程上下文。

  • 临时

    kmap_atomic()完成,从FIXADDR_START到FIXADDR_TOP,属于预留性质,不会导致睡眠。注意,临时映射需要禁本地cpu中断,因为预留是根据cpu划分,不容切换。

  • 非连续

    一般场景是,从VMALLOC_START到VMALLOC_END,同时申请物理内存和线性地址。

注意,这里只是高端映射,有可能映射到低端内存。

直白地理解,就是:内核用1G空间管理4G空间,自然不够,所以从中留128M做动态反复使用。

3.2 结合源码理解高端内存

下面,我们结合源码(linux kernel souce code 4.0)看看。

以下参考find_low_pfn_range():

 
将实际物理内存以896M为界,分别处理。

先说大于896M的场景,此时896M和实际物理内存之间的就属于高端内存。不管是否打开CONFIG_HIGHMEM,都会设置低端内存为896M:(MAXMEM_PFN即896M)

max_low_pfn = MAXMEM_PFN;

但是,如果不打开CONFIG_HIGHMEM,就会无视高端内存:

max_pfn = MAXMEM_PFN;

这样,如果内存高于896M,但是不打开CONFIG_HIGHMEM,就只能使用896M以内的内存了,真是浪费!

需要注意的是,高端内存区间不能容纳超过此范围的内存空间,否则忽略并报错:

 

再看物理内存小于896M的场景,此时真的没有高端内存的概念了么?首先,不管是否打开CONFIG_HIGHMEM,都会设置低端内存为实际物理内存大小(小于896M的一个值):

max_low_pfn = max_pfn;

如果没打开CONFIG_HIGHMEM,确实没有高端内存的事了。但是,如果打开CONFIG_HIGHMEM,则在低端内存(max_low_pfn)留出部分空间(假定依然128M),此空间就还是高端内存的概念:

 

总结就是:

  1. 假设实际物理内存大小为a

  2. 如果192M<=a<=896M(低端必须至少64M,所以128M+64M=192M),(如果打开CONFIG_HIGHMEM)则高端内存为[a-128M,a]

  3. 如果a>896M,则高端内存为[896M,a]

注意,此处的高端内存是从物理内存角度说的,要与内核虚拟空间的角度(1G空间的128M)分开理解。
最后,我们再看看VMALLOC区间:

 

可见,VMALLOC_START其实就是高端内存地址加上8M的间隔。我的理解:VMALLOC区不是固定从896M开始,而是从高端内存开始的,VMALLOC_END是固定的,这样,VMALLOC区间完全可能大于128M(考虑实际物理内存小于896M且打开CONFIG_HIGHMEM的场景下,最大可达1G-64M=960M)!想想,其实很好理解,VMALLOC区间诞生的背景是物理内存远大于内核空间,而如果内核空间远大于物理内存呢?

4  总结

就x86 32bit版来说,主要是要搞清高端内存的含义,需要从物理内存角度和内核虚拟空间角度两个角度去结合理解。其存在的意义,就是为了将更大的内存映射进内核空间!下次分析64bit版本(相当于,64T取代896M),因为32bit版本已经老去。。。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多