热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

内存管理小解

内核在内存中的位置在默认情况下,Linux内核被装载到一个内存中固定的位置,该位置在编译时确定,配置选项PHYSICAL_START用于

内核在内存中的位置

在默认情况下,Linux内核被装载到一个内存中固定的位置,该位置在编译时确定,配置选项PHYSICAL_START用于确定内核在内存中的位置。

Linux内核的初始化

start_kernel—>setup_arch—>…

内存相关的初始化工作:

  1. 创建列表,包括系统占据的内存区以及空闲内存区。
  2. 通过setup_memory确定每个结点可用的物理内存页的数目,初始化bootm内存分配器(采用位图来管理内存页),分配各种内存区等。
  3. 通过page_init初始化内核页表(用户空间无法访问)并启动内存分页机制。
  4. 通过zone_sizes_init初始化系统中所有结点的pgdat_t实例(保存结点内存数量以及内存在各个内存域的分配信息)。

内核地址空间的划分:

32位系统(IA-32):
IA-32架构的内核地址空间划分示意图

​ IA-32架构的内核地址空间划分示意图

32位系统的地址空间一般按照3:1的比例划分,内核地址空间一般为1G,进程地址空间一般为3G,所有进程共享一个内核地址空间。当然3:1的比例也不是绝对的,我们可以根据自己的需要对该比例进行调整。

IA-32最大支持4GB的物理内存,但是内核只能将其896MB直接映射(地址的线性平移)到内核地址空间,剩余的128MB(高端内存区)的用法如下:

  1. 虚拟地址连续但物理地址不连续的内存区,可以在vmalloc区域分配(主要用于设备和声音驱动)。
  2. 持久映射。将高端内存域中的非持久页映射到内核中。
  3. 固定映射。将与物理地址空间中的固定页与虚拟地址空间关联。

64位系统(AMD64):

AMD64的系统为避免地址空间跨度太大采用48位的地址字寻址256TB的地址空间。但是虽然物理地址字位被限制为48位,但是在寻址虚拟地址空间时仍采用了64位的指针,这会导致部分的物理地址空间无法寻址。设计师通过符号扩展的方法避免了这种问题:

img

如图所示,高16位全0是用户空间地址,高16位全1是内核空间地址。在64位系统中,物理页可以一致映射到从PAGE_OFFSET开始的64TB的内核地址空间中

内核空间申请内存的函数

我们都知道在用户空间申请内存使用的是malloc(),那么在内核空间申请内存使用的是哪些函数呢?

  • vmalloc()用于为内核分配虚拟地址连续但物理地址不连续的空间。

  • kmalloc()申请的内存位于物理内存映射区域,而且在物理上也是连续的,它们与真实的物理地址只有一个固定的偏移,因为存在较简单的转换关系,所以对申请的内存大小有限制,不能超过128KB

  • kzalloc() 函数与 kmalloc() 非常相似,参数及返回值是一样的,可以说是前者是后者的一个变种,因为 kzalloc() 实际上只是额外附加了 __GFP_ZERO 标志。所以它除了申请内核内存外,还会对申请到的内存内容清零

kmalloc()、kzalloc()、vmalloc() 的共同特点是:

  1. 用于申请内核空间的内存;
  2. 内存以字节为单位进行分配;
  3. 所分配的内存虚拟地址上连续;

kmalloc()、kzalloc()、vmalloc() 的区别是:

  1. kzalloc 是强制清零的 kmalloc 操作;(以下描述不区分 kmalloc 和 kzalloc)
  2. kmalloc 分配的内存大小有限制(128KB),而 vmalloc 没有限制;
  3. kmalloc 可以保证分配的内存物理地址是连续的,但是 vmalloc 不能保证;
  4. kmalloc 分配内存的过程可以是原子过程(使用 GFP_ATOMIC),而 vmalloc 分配内存时则可能产生阻塞;
  5. kmalloc 分配内存的开销小,因此 kmalloc 比 vmalloc 要快;

一般情况下,内存只有在要被 DMA 访问的时候才需要物理上连续,但为了性能上的考虑,内核中一般使用 kmalloc(),而只有在需要获得大块内存时才使用 vmalloc()。例如,当模块被动态加载到内核当中时,就把模块装载到由 vmalloc() 分配的内存上。

内核页表和进程页表

内核和进程使用不同的地址空间,因此也需要不同的页表来管理自己的地址空间。那么内核页表和进程页表都是什么呢?

内核页表:即书上说的主内核页表,在内核中其实就是一段内存,存放在主内核页全局目录init_mm.pgd(swapper_pg_dir)中,硬件并不直接使用。
进程页表:每个进程自己的页表,放在进程自身的页目录task_struct.pgd中。
在保护模式下,从硬件角度看,其运行的基本对象为“进程”(或线程),而寻址则依赖于“进程页表”,在进程调度而进行上下文切换时,会进行页表的切换:即将新进程的pgd(页目录)加载到CR3寄存器中。从这个角度看,其实是完全没有用到“内核页表”的,那么“内核页表”有什么用呢?跟“进程页表”有什么关系呢?

1、内核页表中的内容为所有进程共享,每个进程都有自己的“进程页表”,“进程页表”中映射的线性地址包括两部分:
用户态
内核态
其中,内核态地址对应的相关页表项,对于所有进程来说都是相同的(因为内核空间对所有进程来说都是共享的),而这部分页表内容其实就来源于“内核页表”,即每个进程的“进程页表”中内核态地址相关的页表项都是“内核页表”的一个拷贝。
2、“内核页表”由内核自己维护并更新,在vmalloc区发生page fault时,将“内核页表”同步到“进程页表”中。以32位系统为例,内核页表主要包含两部分:
线性映射区
vmalloc区
其中,线性映射区即通过TASK_SIZE偏移进行映射的区域,对32系统来说就是0-896M这部分区域,映射对应的虚拟地址区域为TASK_SIZE-TASK_SIZE+896M。这部分区域在内核初始化时就已经完成映射,并创建好相应的页表,即这部分虚拟内存区域不会发生page fault。

vmalloc区,为896M-896M+128M,这部分区域用于映射高端内存,有三种映射方式:vmalloc、固定映射、持久映射,这里就不像述了。。
以vmalloc为例(最常使用),这部分区域对应的线性地址在内核使用vmalloc分配内存时,其实就已经分配了相应的物理内存,并做了相应的映射,建立了相应的页表项,但相关页表项仅写入了“内核页表”,并没有实时更新到“进程页表中”,内核在这里使用了**“延迟更新”**的策略,将“进程页表”真正更新推迟到第一次访问相关线性地址,发生page fault时,此时在page fault的处理流程中进行“进程页表”的更新。

问题:64位的系统的内核地址空间为256TB,远远大于实际的物理内存,因而没有高端内存的概念。同时,因为地址空间巨大,物理内存一致性映射到内核地址空间的大小是2^46=64TB,因而其实不需页表只用线性偏移即可完成VA和PA的转换,那为什么这里还是要内核页表呢?

答:因为现代计算机上都存在名为MMU的硬件机制,可以直接完成虚拟地址到物理地址的转换,内核也无法绕过该机制,所以该有的页表还是要有。


推荐阅读
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • Linux如何安装Mongodb的详细步骤和注意事项
    本文介绍了Linux如何安装Mongodb的详细步骤和注意事项,同时介绍了Mongodb的特点和优势。Mongodb是一个开源的数据库,适用于各种规模的企业和各类应用程序。它具有灵活的数据模式和高性能的数据读写操作,能够提高企业的敏捷性和可扩展性。文章还提供了Mongodb的下载安装包地址。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • C语言注释工具及快捷键,删除C语言注释工具的实现思路
    本文介绍了C语言中注释的两种方式以及注释的作用,提供了删除C语言注释的工具实现思路,并分享了C语言中注释的快捷键操作方法。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 本文介绍了Linux系统中正则表达式的基础知识,包括正则表达式的简介、字符分类、普通字符和元字符的区别,以及在学习过程中需要注意的事项。同时提醒读者要注意正则表达式与通配符的区别,并给出了使用正则表达式时的一些建议。本文适合初学者了解Linux系统中的正则表达式,并提供了学习的参考资料。 ... [详细]
  • imx6ull开发板驱动MT7601U无线网卡的方法和步骤详解
    本文详细介绍了在imx6ull开发板上驱动MT7601U无线网卡的方法和步骤。首先介绍了开发环境和硬件平台,然后说明了MT7601U驱动已经集成在linux内核的linux-4.x.x/drivers/net/wireless/mediatek/mt7601u文件中。接着介绍了移植mt7601u驱动的过程,包括编译内核和配置设备驱动。最后,列举了关键词和相关信息供读者参考。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • 本文介绍了Linux Shell中括号和整数扩展的使用方法,包括命令组、命令替换、初始化数组以及算术表达式和逻辑判断的相关内容。括号中的命令将会在新开的子shell中顺序执行,括号中的变量不能被脚本余下的部分使用。命令替换可以用于将命令的标准输出作为另一个命令的输入。括号中的运算符和表达式符合C语言运算规则,可以用在整数扩展中进行算术计算和逻辑判断。 ... [详细]
author-avatar
狂风
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有