分段(segmentation)
什么是分段?
基本思路是:创建进程的时候,首先在进程的虚拟内存空间中,分配出一段地址(0x00000000~0x00000100),然后将这段地址映射到实际的物理地址中(0x01000000 ~ 0x01000100)。也即当程序访问地址0x00000000的时候,实际上访问就是屋里地址0x01000000。
为什么分段?
为什么分段?如果不太好回答,那我们就反其道行之,看看不分段会有哪些缺点?如果不分段,不采用虚拟地址空间。用户每个进程直接使用实际的屋里内存地址,就会存在两个问题:
- 地址空间不隔离
每个进程都可以访问物理地址,如果进程A强行访问B进程的空间,会直接破坏A进程的数据。这是不能容忍的。 - 程序运行地址不确定
在编写程序的时候,存在某些数据的访问和指令跳转的目标地址是固定的,如果直接使用屋里地址,每次程序A结束,然后当前B进程要运行,这样的地址都是不确定的,导致程序编写很麻烦。
为了解决以上两个问题,我们增加了中间层:虚拟地址空间,即分段。
分页(paging)
什么是分页?
将地址空间(这里包括虚拟地址和物理地址)分成固定大小的长度(成为页(page))。每一页的大小有硬件决定,目前常用的x86系统每页大小为4kB。
为什么要分页呢?
同样的,我们回答不分页会有哪些缺点,如果不分页,在程序运行的时候,加入目前系统内存是128M, 此时系统已经运行了一个100M的进程A,我还需要运行一个内存是30M的进程B,很明显,内存不足,需要将进程A西安置换出来,那么就需要将整个内存的100M数据都保存下来,然后去执行进程B。
如果系统中进程调度频繁,向这么大的内存空间,频繁的需要加载和重新置换,效率很低。
为了解决这个问题,提出了分页的想法。
为什么能分页呢?
根据程序的局部性原理,也就是程序运行时,在某个时刻,程序知识频繁的用到了一部分数据,而不是每时每刻都是所有的数据都在参与计算。所以如果将内存分隔成大小相同的很多块,这个时候,在遇到需要内存置换的操作时候,我们仅仅需要将活跃的数据加入内存,这样不仅占用内存少,而且在置换的时候,也不需要大段的内存同时置换。大大提高了效率。
总结,分页解决了内存地址使用效率低的问题。
写时复制(copy on write)
在多进程创建的时候,实际上,新的进程,不是在创建的时候,立马将主进程的地址空间全部复制,这个时候,多个进程可以同事自由的读取内存,之后,如果任意一个进程需要对内存进行修改的时候,内存就会重新复制一份提供给剔除修改的进程单独使用,起到了隔离的作用。这种技术提供了快速的进程创建,并最小化必须分配给新创建进程的新页面的数量。