虚拟内存
- 为什么需要虚拟内存
- Linux虚拟内存的结构
- 32位系统下的虚拟地址空间
- 64位系统下的虚拟地址空间
- 页表
- 多级页表
- TLB
- 流程
- 虚拟内存的作用
为什么需要虚拟内存
为了在进行多进程编码进行内存访问的时候保持内存的隔离性,数据安全性,所以出现了虚拟内存。举一个例子:
如果没有虚拟内存,我们在编码一个程序的时候将数据写在了编号为1000的物理内存上,当我们编码另外一个程序的时候,就需要注意,避免写入或修改上一个程序在编号为1000的物理内存处的数据,当已经有了几百个程序对物理内存进行过读写时,我们在进行内存读写的时候就会变得格外困难(因为需要避免覆写已经有数据的物理内存空间)。
所以虚拟内存的出现解决了这个问题。
Linux虚拟内存的结构
每一个进程都会分配一个独立的虚拟内存空间,独立的页表。不同的进程拥有相同结构的虚拟内存空间。分为32位和64位系统的虚拟地址空间。
32位系统下的虚拟地址空间
可以看的出来,内核空间位1G,用户空间为3G。
64位系统下的虚拟地址空间
进入内核态的时候使用内核空间,进入用户态的时候使用用户空间,比如使用系统调用的时候就会进入内核态,发生缺页中断也会进入内核态。
不同进程的内核空间都是统一的,不同进程的用户空间都是独立的。
页表
虚拟空间是通过页表,就能通过虚拟地址,得到物理内存上的物理地址,从而进行对物理内存上的读写。
页表是存储在内核空间的,而内核空间是当操作系统启动的时候就加载到物理内存上的,一般不会进行内存交换。
页表可以像上图一样理解,虚拟地址和物理地址一一映射,除了这个,其他的页表项,比如有效位可以分辨该空间是否被使用,读写权限可以保障数据的安全性。
多级页表
虚拟内存和物理内存一定得是一一对应的,但是如果4Gb的物理内存和虚拟内存都一一映射的话,就会导致页表过于庞大。为了解决这个问题,引入了多级页表。如下图所示:
但是如果多个进程都要实现自己的虚拟内存和物理内存一一映射的话,那么就算是多级页表,也会使得存储在内核空间的多进程的页表十分庞大。通过局部性原理我们知道,经常使用访问的数据大部分时候都是集中在那么几块页中,因此我们运行程序的时候不需要将全部程序都加载进内存中,需要时再换入,内存紧张时再换出到磁盘,这就使得每一个进程实际使用的物理内存空间不大,相应的,所需要的页表也就不需要那么多。这带来的好处就是进程使用的内存可以大于实际的物理内存。
TLB
多级页表在检索的时候也会出现效率低下的问题,并且由于局部性原理,我们可以将一些常访问的页表项存储带I/O效率更快的硬件TLB(也叫做快表)中,这样就可以解决上述问题。
流程
- cpu拿到虚拟内存中的虚拟地址。
- 通过MMU(内存管理单元)查询TLB中的页表项是否含有该虚拟地址到物理地址的映射,如果有,直接返回,如果没有,MMU则查询页表。
- 如果在页表中查询到该虚拟地址到物理地址的映射,则直接返回,如此,cpu就拿到了相应的物理地址,可以进行操作。否则,cpu会触发一个缺页中断,然后操作系统会将磁盘上相应的页加载进物理内存,并且更新页表,将新加载进物理内存的页面进行同虚拟内存的映射。
- 然后回到第3步,MMU重新查询页表,此时已经有了映射,cpu成功拿到物理地址。
虚拟内存的作用
- 使得进程使用的内存空间大于物理内存实际的空间。
- 每个进程拥有独立的虚拟内存,在多进程的情况下,使得进程之间不会访问到除自己内存空间外的地址,解决了多进程地址冲突的问题。
- 页表项中还含有一些表明页面属性的项,比如读写权限,有效位,使得我们不可以随意访问一些不可修改的页, 增加了数据的安全性。
- 每一个进程都拥有相同的虚拟地址空间,使得我们对于内存管理更加容易了,减少了代码的错误和地址的冲突。
😄 创作不易,你的点赞和关注都是对我莫大的鼓励,再次感谢您的观看😄