转载linux内核笔记之高端内存映射

米罗西 米罗西     2022-08-04     681

关键词:

原文:linux内核笔记之高端内存映射

 

在32位的系统上,内核使用第3GB~第4GB的线性地址空间,共1GB大小。内核将其中的前896MB与物理内存的0~896MB进行直接映射,即线性映射,将剩余的128M线性地址空间作为访问高于896M的内存的一个窗口。

引入高端内存映射这样一个概念的主要原因就是我们所安装的内存大于1G时,内核的1G线性地址空间无法建立一个完全的直接映射来触及整个物理内存空间,而对于80x86开启PAE的情况下,允许的最大物理内存可达到64G,因此内核将自己的最后128M的线性地址空间腾出来,用以完成对高端内存的暂时性映射。

而在64位的系统上就不存在这样的问题了,因为可用的线性地址空间远大于可安装的内存。下图描述了内核1GB线性地址空间是如何划分的。

技术分享

其中PAGE_OFFSET表示内核使用的1GB线性地址的起始处(第3GB),high_memory往右的部分则表示高端内存,共 128M 的线性地址。可以用来完成上述映射目的的区域为vmalloc area,Persistent kernel mappings区域和固定映射线性地址空间中的FIX_KMAP区域,这三个区域对应的映射机制分别为 非连续内存分配 , 永久内核映射和 临时内核映射 。

永久内核映射

在内核初始化页表管理机制时,专门用pkmap_page_table这个变量保存了PKMAP_BASE对应的页表项的地址,由pkmap_page_table来维护永久内核映射区的页表项的映射,页表项总数为LAST_PKMAP个。

这里的永久并不是指调用kmap()建立的映射关系会一直持续下去无法解除,而是指在调用kunmap()解除映射之间这种映射会一直存在,这是相对于临时内核映射机制而言的。

需要注意一点的是,当永久内核映射区没有空闲的页表项可供映射时,请求映射的进程会被阻塞,因此永久内核映射请求不能发生在中断和可延迟函数中。

临时内核映射

临时内核映射和永久内核映射相比,其最大的特点就是不会阻塞请求映射页框的进程,因此临时内核映射请求可以发生在中断和可延迟函数中。系统中的每个CPU都有自己的13个临时内核映射窗口,根据不同的需求(用于内核控制路径),选择不同的窗口来创建映射。每个CPU的映射窗口集合用 enum km_type 数据结构表示,该数据结构中的每个符号,如 KM_BOUNCE_READ 、 KM_USER0 或 KM_PTE0 ,标识了窗口的线性地址,其实是一个下标。当要内核建立一个临时映射时,通过 cpu_id 和 窗口下标 来确定线性地址。

临时内核映射的实现也比永久内核映射要简单,当一个进程申请在某个窗口创建映射,即使这个窗口已经在之前就建立了映射,新的映射也会建立并且覆盖之前的映射,所以说这种映射机制是临时的,并且不会阻塞当前进程。

非连续内存分配

非连续内存分配是指将物理地址不连续的页框映射到线性地址连续的线性地址空间,主要应用于大容量的内存分配。采用这种方式分配内存的主要优点是避免了外部碎片,而缺点是必须打乱内核页表,而且访问速度较连续分配的物理页框慢。

非连续内存分配的线性地址空间是从 VMALLOC_START 到 VMALLOC_END ,每当内核要用vmalloc类的函数进行非连续内存分配,就会申请一个vm_struct结构来描述对应的vmalloc区,两个vmalloc区之间的间隔至少为一个页框的大小,即PAGE_SIZE。

总结

由于内核的线性地址空间有限,因此采取上面介绍的三种方式来映射高端内存。但是每种映射的本质都是通过页表来建立线性地址与物理地址之间的联系。

永久内核映射和临时内核映射,都由内核指定了需要进行映射的页面,也就是说指定了页描述符(页描述符和物理页框之间的关系是固定不可变的)。在永久内核映射中,内核只需要在永久内核映射区找到空闲的,也就是未被映射的线性地址对应的页表项,然后将其分配给page即可,若找不到则将阻塞申请建立映射的进程;而临时内核映射更直接,连进行映射的线性地址窗口都是固定的,若是其已经分配给了某个页框,则直接抢过来用,因此之前的映射就被覆盖了,体现出了临时性。

非连续内存分配,内核不用指定具体的页框,只需指定要申请的内存大小,内核将在非连续内存分配区找到一块相应大小虚拟地址空间,然后再由伙伴系统分配页框,还要通过slab分配器为一些数据结构分配内存,最后再用同样的方式(设置PTE表项)来建立映射。

linux内存从0到1学习笔记(七,用户空间虚拟内存之三-内存映射)(代码片段)

         C库函数提供了mmap函数建立映射。在内核一端,提供了两个系统调用mmap和mmap2.通常C标准库只提供了一个函数,由应用程序用来创建内存映射,接下来该函数调用在内部转换为适合于体系结构的系统调用mm... 查看详情

linux的内核空间和用户空间是如何划分的(以32位系统为例)?

  通常32位Linux内核地址空间划分0~3G为用户空间,3~4G为内核空间。地址分配如下图所示 内核地址空间分布  直接映射区:线性空间中从3G开始最大896M的区间,为直接内存映射区,该区域的线性地址和物理地址存在线性转换... 查看详情

linux内核源码解析03–启动代码分析之主内核页表创建(代码片段)

...下页表映射:1.恒等映射:页表基地址idmap_pg_dir;2.粗粒度内核镜像映射:页表基地址init_pg_dir;3.fixmap映射:页表基地址为init_pg_dir,待paging_init之后为swapper_pg_end;4.细粒度内核镜像映射:页表基地址为swapper_pg_dir;5.线性映射:页表基... 查看详情

arm-linux内存管理学习笔记-页表前戏

start_kernel之前的汇编代码建立了内核临时页表,完成了内核区域的静态线性映射,保证内核可以在舒适的虚拟地址空间(运行地址和链接地址一致)运行。进入start_kernel之后就要准备建立完整的页表映射,这... 查看详情

arm-linux内存管理学习笔记-页表前戏

start_kernel之前的汇编代码建立了内核临时页表,完成了内核区域的静态线性映射,保证内核可以在舒适的虚拟地址空间(运行地址和链接地址一致)运行。进入start_kernel之后就要准备建立完整的页表映射,这... 查看详情

Linux - 在内核代码中映射用户空间内存

】Linux-在内核代码中映射用户空间内存【英文标题】:Linux-Mappinguserspacememoryinkernelcode【发布时间】:2012-11-1504:42:23【问题描述】:我正在编写一段代码,该代码需要在SOC关闭之前存储位于特定物理地址的10k内存。我的问题是这... 查看详情

linux内存管理架构之四(mmap内存映射机制)(代码片段)

...2.3brk的实现3.实例3.1实现文件映射3.2实现进程共享2.3实现内核驱动和进程共享 4.mmap的调用流程5.反向映射·匿名映射的反向映射:文件映射的反向映射:6.相关问题 7.参考1.是什么        mmap是一种内存映射文件的方... 查看详情

linux为啥要采用三级页表?该机制如何工作

...立一套完整的页表机制要经过以下几个步骤:  1.临时内核页表的初始化(setup_32.s)  2.启动分页机制(head_32.s)  3.建立低端内存和高端内存固定映射区的页表(init_memory_mapping())  4.建立高端内存永久映射区的页表并获取固定... 查看详情

linux内存从0到1学习笔记(6.8,物理内存初始化之buddy伙伴系统)

...在前面在linux启动的那一刻,内存管理就已经开始了。在内核中,实现物理内存管理的allocator包括:初始化阶段物理内存管理memblock连续物理内存管理buddy非连续物理内存管理vmallocallocator小块物理内存管理slaballocator在系统初始化... 查看详情

linux内存从0到1学习笔记(九,内存优化调试之三-内存拆解)---持续更新(代码片段)

...余物理内存大小、已使用物理内存大小、交换空间大小和内核使用的 查看详情

内核解读之内存管理内存管理三级架构之内存区域zone(代码片段)

...,一个页框就是一个内存的分配单元,可用于任何事情:存放内核数据,用户数据和缓冲磁盘数据等等。任何种类的数据页都可以存放在任页框中,没有任何限制。但是Linux内核又把各个物理内存节点分成n个不同的管理区域zone,这是为... 查看详情

linux内核内存管理内存管理系统调用④(代码示例|mmap创建内存映射|munmap删除内存映射)(代码片段)

文章目录一、mmap创建内存映射代码示例1、fopen打开或创建文件2、lseek设置文件大小3、mmap函数使用4、munmap删除内存映射二、完整代码示例一、mmap创建内存映射代码示例1、fopen打开或创建文件使用fopen函数,打开一个文件,此时文件... 查看详情

linux内核内存管理内存映射原理②(内存映射概念|文件映射|匿名映射|内存映射原理|分配虚拟内存页|产生缺页异常|分配物理内存页|共享内存|进程内存)(代码片段)

...一块”虚拟内存区域";2、产生缺页异常缺页异常:Linux内核在分配"物理内存“时,采用了”延迟策略“,即进程第一次访问,不会立即分配物理内存,而是产生一个”缺页异常";3、分配物理内存页分配物理内存页:缺页异常后的... 查看详情

《linux内核设计与实现》读书笔记linux进程管理(代码片段)

...于执行期的程序,通常进程还包含挂起的信号,内核内部数据,处理器状态,一个或多个具有内存映射的内存地址空间及一个或多个执行线程,还包含存放全局变量的数据段等。②线程是进程中活动的对象ÿ... 查看详情

linux内核内存分配函数之kzalloc和kcalloc

参考技术A本文介绍Linux内核内存分配函数:kzalloc()和kcalloc()。文件:include/linux/slab.h,定义如下:kzalloc()函数功能同kmalloc()。区别:内存分配成功后清零。每次使用kzalloc()后,都要有对应的内存释放函数kfree()。举例:文件:includ... 查看详情

linux-用户态内存映射和内核态内存映射

参考技术A操作系统的内存管理,主要分为三个方面。第一,物理内存的管理,相当于会议室管理员管理会议室。第二,虚拟地址的管理,也即在项目组的视角,会议室的虚拟地址应该如何组织。第三,虚拟地址和物理地址如何... 查看详情

linux内存从0到1学习笔记(9.7内存优化调试之page_owner内存分配堆栈详解)---更新中

写在前面前面已经介绍了开启内核pageowner功能的方法,以及如何对pageowner信息进行排序,解析。最终可以确认那些任务或进程占用了多少内存。无论是正常分配的,还是内存黑洞的内存,我们都可以通过解析pageown... 查看详情

arm-linux内存管理学习笔记-内核临时页表的建立

...,接下来就开始咱们软件工程师的本职工作,对内核相关代码进行分析。内核代码那么复杂,该从哪里下手呢,想来想去。其实不管代码逻辑如何复杂,最终的落脚点都是在对页表项的操作上,那么内核是... 查看详情