【无标题】

不连续页分配器及页表

一、不连续页分配器

1.系统接口
不连续页分配器所提供的接口如下:

void *vmalloc(unsigned long size);
分配不连续的物理页,并且把物理页映射到连续的虚拟地址空间。

void vfree (const void * addr);
释放vmalloc分配的物理页和虚拟地址空间。

void *vmap(struct type **pages,unsigned int count,unsigned long flags,pgprot_t prot);
把已经分配的不连续物理页映射到连续的物理空间。

void vunmap(const void *addr);
释放使用vmap分配的虚拟地址空间。

2.内核提供接口函数
void *kvmalloc(size_t size,gfp_t flags);
首先尝试去使用kmalloc分配内存块,如果失败则使用vmalloc函数分配不连续的物理页。

void kvfree (const void * addr);
如果内存块是使用vmalloc分配的,那么使用vfree释放,否则则使用kfree释放。

3.Linux中常用内存分配函数
用户空间(malloc/calloc/realloc/free)。不保证物理的连续,大小的限制(堆申请),单位为字节,calloc初始化为0,realloc改变内存的大小。
mmap/munmap,场景:将文件利用虚拟内存技术映射到内存当中。
brk/sbrk。场景:虚拟内存到内存的映射。

内核空间(vmalloc/vfree)。虚拟连续/物理不连续,大小的限制(vmalloc区)单位为页(vmalloc区域)。场景:可能睡眠,不能从中断上下文中调用,或者其他不允许阻塞情况下调用。

slab(kmalloc/kcalloc/krealloc/kfree)。物理连续,大小的限制一般是在64b-4mb。单位为2^order字节(normal区域)。场景:大小是有限的,不如vmalloc/malloc大。还有一个叫kmem_cache_create(物理连续,64b-4mb,字节大小需要对齐(Normal区域)。场景:便于固定大小数据的频繁释放和分配,分配时从缓存池中获取地址,释放时也不一定真正的释放内存,通过slab进行管理)。

伙伴系统(_get_free_page/_get_free_pages。物理连续。大小是4mb(1024页),单位为页(Normal区域)。场景:_get_free_pages,但是限定不能使用HIGHMEM)。
alloc_pages/alloc_pages/free_pages,物理连续。4mb,单位为页(区域:Normal/Vmalloc都可以)。场景:配置最大的页面数211,一次能分配到的最大页面数是1024。

4.内核源码数据结构
1)每个虚拟内存区域对应一个vmap_area实例;
2)每个vmap_area实例关联着一个vm_struct实例。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.技术原理
vmalloc虚拟地址空间的范围是(VMALLOC_START,VMALLOC_END),每种处理器架构都需要定义这两个宏,比如:ARM64架构定义的宏如下:

1)MODULES_END:内核模块区域的结束地址;
2)PAGE_OFFSET:是线性映射区域的起始地址;
3)PUD_SIZE:一个页上层目录表项映射的地址空间长度;
4)VMEMMAP_SIZE:vmemmap区域长度。

vmalloc虚拟地址空间的起始地址=内核模块区域的结束地址
vmalloc虚拟地址空间的结束地址=线性映射区域的起始地址-一个页上层目录表项映射的地址空间长度-vmemmap区域的长度-64KB

vmalloc函数执行过程分为三步:
1.分配虚拟内存区域
2.分配物理页
3.在内核的页表中把虚拟页映射到物理页(内核的页表就是0号内核线程的页表)
备注:函数vmap和vmalloc区别在于不需要分配物理页。

二、页表

页表是一种特殊的数据结构,放在系统空间的页表区,存放逻辑页与物理页帧的对应关系。每一个进程都拥有一个自己的页表,PCB表中有指针指向页表。

1.地址结构

逻辑地址、物理地址、逻辑地址空间、物理地址空间?

2.页表

页表用来把虚拟页映射到物理页,并且存放页的保护位,即访问权限。

分页逻辑地址=P页号.D页内位移,分页物理地址=F页帧号.D页内位移。
P=线性逻辑地址/页面大小。D=线性逻辑地址-P*页面大小。
CPU并不是直接访问物理内存地址,而是通过虚拟地址空间来间接访问物理内存地址。虚拟地址空间是操作系统为每个正在执行的进程分配一个逻辑地址,比如在32位操作系统,范围是[0,4G-1]。操作系统通过将虚拟地址空间和物理内存地址之间建立映射关系,让CPU能够间接访问物理内存地址。
一般情况将虚拟地址空间以[512byte,8k]作为一个单位,称为页,并从0开始依次对它进行页编号。这个大小就称为页面。将物理地址按照同样大小,作为一个单位,称为框或者是块。也是从0开始依次进行对每个框编号。OS通过维护一张表,这张表对应每一对页和框的映射关系。(windows页面的大小为4KB)
在这里插入图片描述
系统为每一个进程建立一个页表,在进程逻辑地址空间中的每一页,依次在页表中都有一个表项,记录该页对应的物理块号。通过查找页表就可以很容易的找到该页在内存中的位置。页表具有逻辑地址到物理地址映射的作用。

3.ARM64处理器的页表
ARM64处理器把页表称为转换表,最多4级。ARM64处理器支持3种页长度,4KB、16KB和64KB。页长度和虚拟地址的宽度决定了转换表的级数。

Linux内核把页表分为4级:页全局目录(PGB)、页上层目录(PUD)、页中间目录(PMD)、直接页表(PT)。
如果选取四级目录,则就是页全局目录(PGB)、页上层目录(PUD)、页中间目录(PMD)、直接页表(PT)。
如果选取三级目录,则就是页全局目录(PGB)、页中间目录(PMD)、直接页表(PT)。
如果选取二级目录,则就是页全局目录(PGB)、直接页表(PT)。

五级页表的结构,每一个进程都有其独立的页表,进程的mm_struct实例成员pgd指向页全局目录。前面四级页表的表项存放下一级页表的起始地址,直接页表的表项存放页帧号(PFN)。

在这里插入图片描述
查询页表,把虚拟地址转化成物理地址流程:
1.根据页全局目录的起始地址和页全局目录索引得到页全局目录表项的地址,然后再从表项的得到页四级目录的起始地址;
2.根据页四级目录的起始地址和页四级目录表项的地址,然后从表项得到页上层目录的起始地址;
3.根据页上层目录的起始地址和页上层目录索引得到页上层目录表项的地址,然后从表项得到页中间目录的起始地址;
4.根据页中间目录的起始地址和页中间目录索引得到页中间目录表项的地址,然后从表项得到直接页表的起始地址;
5.根据直接页表的起始地址和直接页表索引得到页表项的地址,然后从表项得到页帧号。
6.把页帧号和页内偏移组合成物理地址。

比如虚拟地址的宽度是48位,页长度和转化表级数关系:
页长度是4KB:使用四级转换表,转换表和内核的页表对应关系。0级转换表对应面全局目录,1级转换表对应页上层目录,2级转换表对应页中间目录,3级转换表对应直接页表。

48位虚拟地址直接分解如下:
在这里插入图片描述
每级转换表占用一页,有512项,索引是48位虚拟地址的9个位。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值