Linux内核层和用户层的内存管理

前言

看了一些书和别人的博客,记录一下自己对Linux内核层和用户层的内存管理的理解。
以下都是针对32位操作系统的知识理解。
在这里插入图片描述

一、内核层的内存管理

首先得有概念,内核层的内存很紧张,不像用户层可用内存很多。所以内核中分配内存是很复杂的。

1.页

①页作为内核管理的基本单位。MMU(内存管理单元,把虚拟地址转换为物理地址的硬件)通常以页为单位管理内存。
②页是物理内存,与虚拟内存无关。
③内核对于每个物理页都用struct page描述。

2.区

①不同物理地址的页具有不同的功能,所以内核对页进行了分类,就有了区(针对物理内存的分区)。Linux的区主要分为:ZONE_DMA、ZONE_NORMAL、ZONE_HIGHMEM。
ZONE_DMA是执行DMA操作的。(0~16MB)
ZONE_NORMAL是正常操作的页。(16MB~896MB)
ZONE_HIGHMEM是所谓的高端内存,这些页不能永久映射到虚拟地址上,需要动态的映射。(896MB~最大物理地址)64位的系统因为虚拟内存很大,32位只有4GB小于实际物理内存,所以64位系统没有高端内存一说
②也没有那么严格,如果ZONE_NORMAL,可以用ZONE_DMA的页来代替。
③内核对于每个区都用struct zone描述。

3.获取内存

获得页:
①alloc_pages、alloc_page返回物理地址(可以使用page_address将物理页转换为虚拟地址)
②__get_free_pages、__get_free_page直接返回虚拟地址。
③__free_pages释放地址,参数位物理地址。free_pages、free_page参数为虚拟地址。

获取字节:
①kmalloc,获取物理地址和虚拟地址都连续的内存,返回虚拟地址,kfree释放。
②vmalloc,获取物理地址不一定连续的内存,返回的也是虚拟地址,vfree释放,可能引起睡眠。

参数说明:
①区修饰符,从哪个区得到内存,__GFP_DMA、__GFP_HIGHMEN,不指定的话从NORMAL区分配。__GFP_HIGHMEN不能用于__get_free_pagesh和kmalloc,因为这两个返回的是虚拟地址,而高端内存可能还没有映射,还没有虚拟地址。只有alloc_pages(返回物理地址的)和vmalloc才能分配高端内存。

②类型标志(行为修饰符+区修饰符)
分配过程严格程度:GFP_KERNEL < GFP_NOFS < GFP_NOIO < GFP_ATOMIC

GFP_KERNEL :常规分配方式、可以阻塞(首选)
GFP_NOFS:可以阻塞,可以启动磁盘I/O,不会启动文件操作系统
GFP_NOIO :可以阻塞,不会启动磁盘I/O和文件操作系统
GFP_ATOMIC:不可以阻塞,不能睡眠!(中断中使用,或则持有不能睡眠的锁时使用)

3.slab层

①slab分配器可以缓冲一些数据结构,从而避免重复申请和释放内存,导致内存碎片化和浪费性能。如task_struct就是在slab层分配的(task_struct通过内核栈中的thread_info找到)。
②slab层包括多个高速缓存(不同高速缓存其数据类型不同),每个高速缓存包括多个slab,每个slab又包括多个对象(对应的数据类型结构),所以slab有三种状态:空、部分满和满。

在这里插入图片描述
③slab分配器首先从部分满的slab中分配,如果没有部分满的slab,则从空的slab中分配,如果没有空的slab,则从物理连续页分配新的slab,并把它赋给一个高速缓存,然后从新的slab中分配。

④slab层只能在ZONE_DMA和ZONE_NORMAL区分配,因为ZONE_HIGHMEN区还没有虚拟地址。

补充:
每个进程有两个栈:用户栈和内核栈,内核栈一般为两页,8KB或者16KB。进程的调用链和中断处理程序都在内核栈中完成,所以内核栈压力很大,后来引入中断栈,为每个进程提供一个用于中断处理的栈,不再使用内核栈进行中断处理。

二、用户层的内存管理

每个进程都有4GB的地址空间,可以寻址4GB,但是不一定用到了4GB,需要的时候再向系统申请,系统会分配实际的物理地址。0-3GB是每个用户独有的,3GB-4GB是内核使用。

虽然一个进程可以寻址4GB的虚拟内存(32位的地址空间),但是它并不能访问所有的虚拟地址。那些可以被访问的地址空间成为内存区域

1.内存描述符(mm_struct)

内存描述符mm_struct位于进程的task_struct中。用来表示进程的地址空间,包含进程地址空间的所有信息。
①参数mm_users记录有多少个线程在使用该地址,mm_count记录有多少个进程在使用改地址。mmap和mm_rb都是描述内存区域,但存放形式不同,mmap是链表形式,mm_rb是红黑树形式存放。
②对于mm_struct,线程是相同的共享的,进程不同,而内核线程位nullptr(使用上一进程的地址空间)。

2.内存区域(VMA)

也叫虚拟内存区域,是虚拟地址,由vm_area_struct描述,就是放在mmap里面的数据类型。
①每个内存区域作为一个单独的内存对象管理,每一个内存区域代表不同类型的内存区域。(如进程的用户栈和内存映射文件)。
②每个VMA对其相关的mm_struct结构体都是唯一的。意思就是即使是同一块共享区域、同一个文件映射,对于不同的进程,它们的vm_area_struct也不同。

在这里插入图片描述
这就是一个进程地址空间包含的内存区域。有很多个VMA。

3.页表

应用程序操作的是虚拟地址,但是处理器操作的是物理地址。所以需要把虚拟地址转换为物理地址(MMU)。Linux是使用三级页表完成地址转换(为了减小映射表、节约空间)。
①mm_struct中有一个pgd_t的指针指向页全局目表。每个进程都拥有自己的页表。
②顶级页表是页全局目录PGD、二级页表是中间页目录(PMD)、最后一级页表就叫做页表(PTE)。

③为了优化页表的操作性能,加入了 翻译后缓冲器技术,现在TLB缓冲器中是否有相应的虚拟地址转物理地址的映射,有则直接用,没有才进行三级页表完成地址转换。
也加入了写时拷贝技术。fork时子进程共享页表,只有修改页表时才进行页表项的拷贝(不共享了)。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值