操作系统之内存管理篇

 

物理内存和虚拟内存

物理内存:CPU的地址线可以直接进行寻址的内存空间大小

虚拟内存:程序所使用的内存

为什么引入虚拟内存?

1.如果没有虚拟内存,我们运行的程序都将在物理内存上运行,当内存空间不足时,需要将其他程序装入硬盘中,将新的程序装入内存中,频繁的装入装出效率极低。

2.程序直接访问物理内存,所以进程可以修改其他进程的数据,甚至会修改内核地址空间的数据。

3.因为内存是随机分配的,所以程序运行的地址也是不正确的。

为了解决以上几个问题,让操作系统为每个进程分配独立的一套虚拟地址。让每一个进程都有自己的地址,互不干涉。但是进程不能访问物理地址,而操作系统会提供一种机制,将不同进程的虚拟地址和不同内存的物理地址映射起来。

 

  

  那么问题来了,操作系统是如何管理虚拟地址与物理地址之间的关系?

主要有三种方式:

  一、内存分段

  程序是由若干个逻辑分段组成的,可由代码分段、数据分段、栈段、堆段组成。不同的段拥有不同的属性,所以就用分段的形式把这些段分离出来。

分段机制下,虚拟地址和物理地址是如何映射的?

  在内存分段机制下的虚拟地址由2部分组成:段选择子和段内偏移量

  段选择子中最重要的是段号,其用作段表的索引,段表中保存的是这个段的基地址、段的界限和特权等级,而物理内存地址就由段表中这个段的基地址加上段内偏移量。

  分段的缺点:

    1.内存碎片的问题

      外部内存碎片:产生了多个不连续的小物理内存,导致新的程序无法被装载。

      内部内存碎片:程序所有的内存都被装载到了物理内存,但是有部分内存不常用,导致内存的浪费。

    解决方案:内存交换,在Linux系统里,常看到的Swap空间,这块空间是从硬盘划分出来的,用于内存与硬盘的空间交换。

    2.内存交换的效率低的问题

      是由内存碎片引起的,使用分段的方式,内存碎片很容易产生,而产生内存碎片,就要进行内存交换,每一次内存交换,我们都需要把一大段连续的内存数据写到硬盘上。当内存交换交换的是一个占内存空间很大的程序,硬盘的访问速度比内存慢太多了,导致整个机器都会显得卡顿。

 

  总结:分段的好处是能产生连续的内存空间,但会产生内存碎片和内存交换的空间太大的问题。

  二、内存分页

  分页是把整个虚拟和物理内存切成一段段固定尺寸的大小,这样一个连续且尺寸固定的内存空间,我们称它为页。在LInux下,每一页的大小为4KB

分页机制下,虚拟地址和物理地址是如何映射的?

   在分页机制下,虚拟地址分为两部分:页号和页内偏移

   同样的,页号作为页表的索引,页表包含物理页每页所在物理内存的基地址,这个基地址加上页内偏移量就形成了物理内存地址。

  分页的优点:

    采用了分页,释放的内存都是以页为单位释放的,也就不会产生无法给进程使用的小内存。

    如果内存空间不够,操作系统会把其他正在运行的进程中的最近没被使用的内存页面释放掉,暂时写在硬盘上,成为换出,需要的时候正再加载进来,成为换入,每次写入磁盘的只有少数的几页,内存交换的效率相对比较高。

  缺点:简单的分页有空间上的缺陷,每个进程都有自己的虚拟地址,如果很多个进程,就需要很大的内存来存储页表,这是对内存极大的浪费。

  解决以上的问题,需要采用多级页表,因为页表一定要覆盖全部虚拟地址空间,不分级的页表就需要有100多万个页表来映射,而二级分页则只需要1024个页表项,此时一级页表覆盖了全部虚拟空间,而二级页表再需要时创建。

  三、段页式内存管理

  简单的来说就是将内存分段和分页组合起来。

 段页式内存管理是如何实现的呢?

  先将程序划分为多个有逻辑意义的段,也就是先分段,再将每个段划分为多个页,对段划分出来的连续空间,再划分固定大小的页。

 如何得到物理地址?

  第一次访问段表,得到页表起始地址

  第二次访问页表,得到物理页号

  第三次将物理页号与业内位移组合,得到物理地址

 

  Linux的内存管理

  Linux内存主要采取了页式内存管理,但由于硬件的原因,不可避免的涉及到段机制。但是实际上,段式映射的过程实际上没有什么作用。

  Linux系统中的每个段都是从0地址开始的整个4GB虚拟空间(32位),也就是所有段的起始地址都是一样的,这意味着Linux系统下的所有代码,都面对的所有地址空间都是虚拟地址。而段只用于访问控制和内存保护。

  Linux虚拟空间的部分分为内核空间和用户空间:

    32位系统的内核空间占用1G,而用户空间占用3G

    64位系统的内核空间和用户空间都是128T,剩下的中间部分是未定义的。

  对应的也有内核态和用户态:

    当进程运行在内核空间时就处于内核态,而进程运行在用户空间时则处于用户态。

    在内核态时,进程运行在内核空间,此时CPU可以执行任何指令,运行的代码也不受任何的限制,可以自由地访问任何有效地址,也可以进行端口地访问。

    在用户态时,进程运行在用户空间,被执行地代码要受CPU的检查,它只能访问映射其地址空间的页表项中规定的在用户态下可访问页面的虚拟地址。

  所以,区分内核空间和用户空间本质上是要提高操作系统的稳定性及可用性。

 

 

   

posted @ 2020-10-09 22:21  zzzgzx  阅读(152)  评论(0编辑  收藏  举报