linux内核内存,【研究任务】linux内存管理机制——内核空间

Linux内存中线性地址为4G,0~3G为用户空间,3~4G为内核空间

0c4c1c75cbe224508d2fca96a4eacc0c.png

一、内核空间

内核空间是3~4G的内存地址,主要用来存储高优先级的代码

在X86结构中的内核地址存在三种类型的区域:

ZONE_DMA     内存开始的16m

ZONE_NORMAL       16m~896m

ZONE_HIGHMEM    896M~

ZONE_DMA是DMA使用的页(DMA是直接路径访问,不经过cpu缓存而直接访问内存)ZONE_NORMAL是正常可寻址的页。ZONE_HIGHMEM是动态映射的页。Linux把系统的页划分为不同的区,形成不同的内存池,这样就可以根据用途进行分配了。其中还有ZONE_DMA32,他只能被32位设备访问。

其中低端内存实现了对物理内存一对一的线性映射(只有1G),高端内存用来临时存放指向其余物理内存空间的地址。

3e22db399bcf89d5845234214da91f86.png

Linux内核将高端内存划分为三个部分:vmalloc_start~vmalloc_end、kmap_base~fixaddr_start和fixaddr_start~4G,可以通过alloc_page()来获得高端内存对应的页,这个函数获得的只是线性地址。高端内存映射是临时存放非线性内存地址。

36c7c5365fa83fd10bdbee95fc88afa7.png

物理页在Linux中的表示

Linux通过struct page结构来表示物理页,是系统内存的最小单位struct page {

unsigned long flags; //flag用来表示这些页的存放状态,比如是否为脏页(总共能够表示32位,在page-flags.h中可以看到)

atomic_t  _count; //_count表明这个物理页的计数,若为-1表示内核还没有用到这一页,他是调用page_count()函数来检查这个域(返回0表示空闲,正整数表示正在使用)

void    *virtual;//这个用来指向该页在虚拟内存中的地址,常用作高端内存的动态映射

}

高端内存的动态映射

0~896M的内存地址称为低端内存,他们跟物理内存是一对一的映射关系。896M~1G的内存地址称为高端内存,实现的是动态映射,目的在于访问内核中一对一映射以外的物理内存。进程A申请了4G开始的内存空间,而内存只能实现0~896M的一对一映射,因此需要将这个地址临时存放到高端内存中,等进程A使用完之后再释放掉。这样就可以访问所有的物理内存地址了。

内存申请

内核中申请内存的方法有:kmalloc()、kzalloc()、vmalloc()、alloc_page()

1、Kmalloc用于申请物理内存,地址上无须连续,因此性能较快。对应的内存释放函数是 kfree()。

2、Kzalloc相对于kmalloc多了一个__GFP_ZERO标志,这个标志位会对申请到的内存内容清零。对应的内存释放函数也是 kfree()。get_zeroed_page()也是同样的效果。

3、Vmalloc用于申请虚拟内存,由于虚拟内存需要保证一定的连续性,因此性能较慢(可能造成比较大的TLB抖动)。由于对象是虚拟内存,所以申请的内存大小没有限制,通常被用来申请大内存空间。对应的内存释放函数是 vfree()。

4、alloc_page()用于申请连续的物理页,可以通过page_address()把指定的页转化成逻辑地址。

Kmalloc()和__get_free_pages()不能分配高端内存,因为这两个函数返回的是物理地址上的逻辑地址,可能还没有映射到虚拟地址上,并非page结构。只有alloc_page()才能分配高端内存

低级页的分配标志位

Linux内存管理的接口都会涉及到一个标志位,如unsigned long get_zeroed_page(unsigned int gfp_mask)。gfp_mask是一些标志的集合,这些标志可以分为两大类:行为修饰符和区修饰符。行为修饰符表示内核如何分配所需的内存。区修饰符表示从哪里分配内存。

常见的行为修饰符标志            描述

__GFP_WAIT      分配器可以睡眠

__GFP_IO        分配器可以启动磁盘I/O

__GFP_FS        分配器可以启动文件系统I/O

__GFP_HIGH      分配器可以访问紧急事件缓冲池

常见的区修饰符标志              描述

__GFP_DMA         只从ZONE_DMA分配

__GFP_HIGHMEM     从ZONE_HIGHMEM或ZONE_NORMAL分配,但会优先考虑高端内存

Slab层

c678e7788a50754bd6653d34fd0ab407.png

slab分配器专门用来分配小内存。其中,slab分配器将SLAB分为两大类:专用SLAB和普通SLAB。专用SLAB用于特定的场合(比如TCP有自己专用的SLAB,当TCP模块需要小内存时,会从自己的SLAB中分配),而普通SLAB就是用于常规分配的时候。我们可以通过查看/proc/slabinfo看到slab的状态

对于kmalloc-8这些普通SLAB,都有一个对应的dma-kmalloc-8这种类型的普通SLAB,这种类型是专门使用了ZONE-DMA区域的内存,方便用于DMA模式申请内存。

e525b686c540679d130e7e73f162dd68.png

在slab中,可分配的内存块叫做对象。不通的slab所包含对象的大小也不同。如kmalloc-8这个普通SLAB,里面所有的对象都是8B大小,同理,kmalloc-16中的对象都是以16B为大小。申请内存时就会依据这些对象来划分,这样做可以减小内存碎片化。其中申请的对象释放后也会回到他的slab中。

当对象拥有者释放一个对象后,SLAB的处理是仅仅标记对象为空闲,并不做多少处理,而又有申请者申请相应大小的对象时,SLAB会优先分配最近释放的对象,这样这个对象甚至有可能还在硬件高速缓存中,有点类似管理区页框分配器中每CPU高速缓存的做法。

kmem_cache(slab缓存)是slab分配器的最上层。kmem_cache结构用于描述一种SLAB(比如kmalloc-8,kmalloc-16等),并且管理着这种SLAB中所有的对象。所有的kmem_cache结构会保存在以slab_caches作为头的链表中。在内核模块中可以通过kmem_cache_create自行创建一个kmem_cache用于管理属于自己模块的SLAB。Kmalloc()就是基于slab来申请的。

Slab中有三种状态:满、部分满、空。当分配内存时会优先从部分满的slab中划分。分别通过三个链表来表示:

structlist_head slabs_full;

structlist_head slabs_partial;

structlist_head slabs_free;

二、用户空间

(详情见《linux内存管理机制——用户空间》)

用户空间是0~3G的内存地址,主要用来存储低优先级的代码,组成如下

5b13972c54b40d093141942406f442cb.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值