性能文章>Linux内核这样学才能学会(内存篇)>

Linux内核这样学才能学会(内存篇)原创

458274

哈喽,我是子牙,一个很卷的硬核男人

深入研究计算机底层、Windows内核、Linux内核、Hotspot源码……聚焦做那些大家想学没地方学的课程。为了保证课程质量及教学效果,一年磨一剑,三年先后做了三个课程:手写JVM、手写OS及带你用纯汇编写OS、手写64位多核OS及Linux内核…

事情是这样的,上篇文章我分享说到如果你想玩转Linux内核,你要具备这些基础。其中第二条说到你要了解核心硬件的工作原理,比如CPU、内存条、CPU+内存条…很多小伙伴看完后觉得没概念,让我展开说说

于是,安排~我会分两篇文章分享,一篇关于CPU,一篇关于内存。本篇文章关于内存

1、研究内存的正确姿势

大家研究内存都是:进程的内存结构、虚拟内存、物理内存、伙伴算法、slab、写时复制、零拷贝、swap memory、共享内存、缺页异常…

其实这些技术相对于内存条来说,已经是很上层的技术了,如图

所以大家在研究的时候,因为存在知识断层,总是get不到点。而且因为做不了实验,无法论证,心里没底,没把握,也不知道自己是否真的掌握了这个知识

那应该如何研究呢?自下而上去研究。从研究OS是如何知晓内存容量开始,到OS的物理内存管理模块是如何实现的,到深刻理解CPU提供的四种分页模式并能够编码实现。其实把这三步做完,关于内存的所有知识点,你都可以做到秒懂!

如何确定自己真的学会了呢?做实验!如果你对Linux内核比较熟悉的话,可以通过写Linux驱动的方式去实践。如果你不熟,那就只能通过自己手写操作系统的方式实践

看到这里,我相信小伙伴们有点理解我说的:手写一个64位多核OS是能够真正玩转Linux内核的唯一方式!瓦特?还不理解?那我再进一步展开说说

2、OS怎么知道内存容量的

你是否想过一个这样的问题:内存条是硬件,OS是软件,那OS是如何知道主板上插了几块内存条呢,又是如何知道内存容量的呢?就是通过下面这段代码实现的

这段代码怎么理解呢?首先你的大脑里要有一个概念,就是内存条是插在主板上的,主板掌握的所有信息,都是以提供BIOS中断的方式让我们写OS的时候获取。所以获取内存信息的0x15号中断是这么用的

但是BIOS提供的0x15号中断有它的局限性,就是如果你主板上插了多根内存条,它只能获取内存的总容量,没办法获取每根内存条的信息。还有一个限制就是这个中断只能在实模式下运行。实模式是什么?CPU的运行模式之一,比如保护模式、IA32-e模式、long mode长模式

如果想在非实模式下获取内存信息要怎么做呢?通过解析ACPI信息是一种,还有UEFI、SMBIOS、DMI

你看,这个知识点你不知道,Linux内核中,内存模块的起始代码你肯定看不懂。起始代码看不懂,在这之上的物理内存管理模块代码看不懂…最后就是整个内存模块代码看不懂,研究内存相关技术点就get不到点!

3、四种分页模式

我们经常说的虚拟内存,其实指的是CPU的分页机制,也叫分页模式。内存相关的很多花里胡哨的技术,都是基于分页模式玩出来的,赤裸裸的物理内存是没什么花样可以玩的

CPU提供了四种分页模式,32位CPU两种,64位CPU两种,5-level paging我没贴出来,因为目前没用上

从这张图我们可以得出很重要的信息:

  1. 32位CPU的线性地址有效位是32位,支持的最大虚拟内存是4G

  2. 64位CPU的线性地址有效位是48位,而不是64位,所以支持的最大虚拟内存是256T

  3. 32位CPU,有的支持的最大地址总线是40根,有的是52根

  4. 64位多核Linux支持1G大页,其实也是CPU提供的。怎么提供的呢?往后看

  5. 不同分页模式的开启方式,比如32-bit paging,只需要将CR0寄存器的pg位置为1就可以了。而PAE paging还需要将CR4寄存器中的PAE位置为1才可以。4-level paging还需要操作64位CPU专属寄存器IA32-EFER

细心的小伙伴看完后会发现一个问题:32位CPU支持的线性地址最大32位,支持的最大虚拟内存是4G,而地址总线又可以超过32根。言外之意是说虚拟内存最大4G,物理内存最大可以到2的40次方1T,这有什么用呢?你插的内存条再多也用不起来呀!

看张图你就懂了。如果最大地址总线只能到32根,那意味着支持的最大物理内存就是4G,言外之意就是不管你插多少根内存条,能够被CPU寻址到的最大只能是4G

但是如果你的地址总线达到惊人的40根,意味着CPU能够寻址的物理内存最大可以是1T,那么真正在使用的时候,因为有更多的物理内存可以用,内存中就可以存放更多进程的数据,提升了程序的执行效率。

比如一个进程它就要吃2G内存,如果只是4G内存,内存中只能存放一个进程的数据,另外一个进程需要运行的话,需要将内存swap空出来,然后载入新的进程的内存数据,以此类推,说白了就是那一块内存被多个进程交替使用。但是如果是1T物理内存呢,那就可以同时存放多个进程的数据在内存中,通过分页模式去做映射,是不是so cool。底层的很多东西研究明白,你会不自觉的惊叹这些设计者绝顶的智慧

这个是如何实现的呢?就是32位CPU的PAE paging的知识,64位CPU的4-level paging沿用了其中的思想。这块可能不太好理解,如果真的想搞明白,多找点资料看看

你看,这个知识点你不了解的话,开启虚拟内存相关的代码你肯定看不懂。关于虚拟内存,开启是一部分,做映射又是另外一部分,那如何做映射的呢?我们先看个实战,后面再讲原理

4、实战32-bit paging

像这种底层的课程,别人都只讲原理,我觉得这样大家根本就学不会,我的所有理论,都有实战伴随左右

接下来讲的是模拟CPU中的MMU组件的工作机制,就是开启分页模式以后,我们访问内存的所有内存地址均为虚拟地址,CPU是如何找到对应的物理地址的

步骤一:先在记事本中写入一个字符串

步骤二:通过内存检索工具CE查到字符串在内存中的位置

步骤三:找到记事本的页表地址

步骤四:基于32-bit paging的底层算法,拆分虚拟地址

最终找到了在内存条上的字符串。这就是MMU的工作机制,当然,中间还有TLB的存在。关于MMU与TLB,你是不是看过很多文章,依然不得要义,给你环境实战一遍,你马上就深刻理解了!

所有的分页模式,我都会教你如何做实验

5、虚拟内存

终于来到我们的重头戏,虚拟内存底层到底是如何实现的。是基于页表实现的

OS中的进程内存空间是隔离的,是如何实现的呢?也是基于页表实现的,一个进程一个页表,内核部分的页表是所有进程共享的。切进程空间就是做页表切换,就是这么简单

页表要如何生成呢?就要学很多描述符,我这边就贴几个让大家了解下是啥玩意,就不展开讲了,后面有机会给大家分享系列文章再展开讲

CPU是怎么找到页表的呢?它内部有个寄存器:CR3,就是用来存放每个进程的页表地址的

关于内存的知识很多很多,一篇文章也无法面面俱到全讲到,如果你真的想玩明白Linux的内存机制,可以按照我讲的这个节奏去研究。当然,最好是找有经验的人学习。整个文章看下来你应该发现了,还是非常复杂的,没人教没人带没人答疑,还是非常难学会的。跟着有经验的人学习,能节省大量时间,少走很多弯路

题外话

你好,我是子牙。十余年技术生涯,一路披荆斩棘从小白到技术总监到大厂中间件到创业。技术栈如汇编、C语言、C++、Windows内核、Linux内核及特别喜欢研究虚拟机底层实现,对JVM有深入研究。分享的文章偏硬核,很硬的那种。不考虑交个朋友吗?关注硬核子牙:

 

点赞收藏
子牙_公号硬核子牙

对编程语言的设计与实现有浓厚兴趣。聚焦Hotspot源码、Linux内核研究,硬核干货分享

请先登录,查看7条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

从 Linux 内核角度探秘 JDK MappedByteBuffer

从 Linux 内核角度探秘 JDK MappedByteBuffer

4
7