linux内存管理的相关知识有哪些呢?
Linux 的虚拟内存管理有几个关键概念:
1、每个进程都有独立的虚拟地址空间,进程访问的虚拟地址并不是真正的物理地址;
2、虚拟地址可通过每个进程上的页表(在每个进程的内核虚拟地址空间)与物理地址进行映射,获得真正物理地址;
3、如果虚拟地址对应物理地址不在物理内存中,则产生缺页中断,真正分配物理地址,同时更新进程的页表;如果此时物理内存已耗尽,则根据内存替换算法淘汰部分页面至物理磁盘中。
谁能介绍下偏移地址物理地址的计算方法呢?
没记错的话,很久很久以前,计算机没什么保护,数据访问(实际上就是内存里面的内容啦)都是直接给一个地址就行,反正那个时代,计算机也不是非常复杂,能干的事情也就那几样吧。
然后,计算机进化了一下,数据地址不再直接给出来了,而是通过段+偏移计算出来。一个典型的场景就是指令获取,通过cs:ip计算。cs是代码段寄存器,里面的内容是代码段的物理地址,ip是指令相对cs段寄存器的偏移地址。
大概是80年代吧,计算机又进化了,这次开始出现所谓的保护模式了。顾名思义,有些东西不能随便用了,被保护起来了,想要访问的话,必须得有权限才行。那么被保护的东西是什么呢?从系统层面看,主要就是内存的位置,有些位置存的东西比较敏感,不能随便访问。比如内核空间里面存储的数据,就不是随便能访问的。
但是内存看起来好像并没有那么明显的特权划分。就好像一条马路,我们走着走着,突然前面不让走了,比如设置了路障什么的。但是内存好像并没有什么特殊路障啊。比如地址0的位置存的是普通数据,地址100处存的是敏感数据,我们如何实现类似路障的效果呢?
理论上实现方法有很多种,intel的实现方案是:通过段寄存器来实现。前面说过,最早是没有段寄存器的,段寄存器是后来才出现的。段寄存器出现之后内存地址计算方式出现了变化,即按照段+偏移进行计算,且段寄存器里面存的是段的真实起始地址。
但是保护模式下,段寄存器又变了,不再存段的真实地址,而是通过一种间接的方式来获得真实地址,
这时段的真实地址被抽取出来,存在所谓的GDT或LDT中,而段寄存器改为存储内存所在段在GDT或LDT中的索引值(也就是数组下标,因为GDT/LDT从结构上看就是个数组)。这里先不纠结GDT/LDT是什么,长什么样子,只要知道它里面存了段地址(基址)就行。
重点:这时段寄存器最高13位用来存索引值,而最低3位被保留,其中最低2位用来存权限等级,0级最大,3级最小(注:段寄存器一直都是16位的)。
可能你也看出来了,2位最多是4个级别,是的,但是操作系统,这里指的是linux,只用了2个级别:0和3。
下面说下保护模式,前面说过,内存地址是通过段+偏移计算出来的,其中段寄存器最低2位指明了特权等级,也就是当前访问者的特权级别。而内存也是有等级的,如果你了解过内存管理,应该知道有页表这么个东西,页表项里面也有特权级的,这里不展开了。现在就比较有意思了,访问者(即段寄存器+偏移)是有特权级的,被访问者(即内存,特权级别存在对应的页表项中)也是有特权级的,一般而言,访问者的特权级别一定要大于等于被访问者的特权级别,也就是说寄存器中的特权级别必须大于或等于页表项中的特权等级。
举个例子就是,大家都知道很多资料都是有密级的,大boss可以查看密级很高的资料,而普通人只能看密级很低的资料。
大概就是这么个理解。
至于用户栈和内核栈,首先程序运行时是需要一段内存来保存运行时数据的,比如局部变量,这个内存就是所谓的栈。保护模式下,用户态的代码是不能访问内核的数据的,否则就像谁都可以进你家里一样,很容易被人恶意利用。但是程序执行一定要一个栈的,所以内核态和用户态各自有自己的栈,就是所谓的内核栈和用户栈。
进程切换特权级别时,会自动切换栈的。