一、虚拟地址
1.为什么需要虚拟地址
以前计算机的内存中是直接把进程整个放进去的,这可能导致不同进程访问同一块物理地址造成错误。
后来又引入了基址寄存器和界限寄存器,基址寄存器负责记录进程的起始地址,界限寄存器负责规定进程的大小界限。这样就保证了进程的独立,但是随着进程数量的增多、大小的增大,就会出现以下问题:
- 进程的大小是动态的,无法事先确定,一旦进程大小达到界限就要重新分配
- 进程数量很多时,只有部分进程能放在内存中,而其他内存只能暂时存储在磁盘中,需要用到时再从磁盘传输到内存,但磁盘的速度是很慢的,这降低了程序运行的性能
为了解决这些问题,虚拟地址技术应运而生。
我们为每个进程都分配一个独立的虚拟地址空间,虚拟地址空间就是虚拟地址的范围。虚拟地址空间对于每个进程来说是一块连续的空间,每个进程并不关心在实际物理内存中的地址,只关心自己的虚拟地址。
操作系统会提供一种机制,将不同进程的虚拟地址和不同内存的物理地址映射起来。 因此,不同进程的虚拟地址可能是一样的,但是会被操作系统映射到不同的物理地址。
这样我们就保证了进程间的独立
2.管理虚拟地址与物理地址之间的关系
目前操作系统主要有两种方式来建立虚拟地址和物理地址的映射关系,一种是内存分段,一种是内存分页
(1)内存分段
程序是由若干个逻辑分段组成的,如可由代码段、数据段、栈段、堆段组成。不同的段是有不同的属性的,所以就用分段(Segmentation)的形式把这些段分离出来。
但是内存分段仍然存在一些问题:
- 在不断地交换内存中的进程和磁盘中的进程的过程中,会产生很多内存碎片,这些内存碎片无法被新的进程利用,从而造成了大量的资源浪费。为了解决这一问题,我们需要不断地移动进程在内存中的地址来避免资源浪费(内存紧缩),但是实现内存紧缩,我们需要把数据写入磁盘,再从磁盘写回内存,而磁盘的读取速度很慢,从而导致性能大幅下降
为了解决这些问题,我们可以使用内存分页
(2)内存分页
我们把一个进程切分为一个个小单位,成为1个页面,比如大小为4kb的页面。进程只有部分页面需要加载到内存中,称为换入(swap in),来保证程序运行,暂时用不到的页面先放在磁盘,需要时再从磁盘读取;内存中长期不访问的页面则从内存读取到磁盘中,为其他页面留出空间,称为换出(swap out)
操作系统会建立一个页表,用来表示进程的虚拟地址和物理地址的映射关系
可以发现这种映射是无序的
在分页机制下,虚拟地址分为两部分,页号和页内偏移。页号作为页表的索引,页表包含物理页每页所在物理内存的基地址,这个基地址与页内偏移的组合就形成了物理内存地址,见下图。
假设我们要访问虚拟地址20500,5个页面就是20kb=20×1024=20480,因此需要的虚拟地址就是距离第5个页面偏移20个字节(地址在第6个页面),而虚拟地址中第6个页面映射到了物理地址中的第4个页面,其地址为12288,因此我们最终要访问的物理地址就是122288+20=12308
通过内存分页,我们只需要找到页面的映射关系后加上偏移量即可,而不需要一个字节一个字节去寻找映射
而实现查找虚拟地址与物理地址映射的是CPU芯片中的MMU(memory management unit,内存管理单元),MMU在拿到CPU给出的虚拟地址后需要从内存读取操作系统建立的页表,来找到映射的物理地址。
在内存分页中,内存是以页面page为单位写入、释放的,因此也就不会出现内存碎片导致资源浪费了,同时以页为单位的交换也提高了性能
(3)多级页表
……
二、CPU时间片
CPU时间片(CPU time slice)是操作系统调度策略中的一个概念,指的是在多任务环境下,操作系统分配给每个进程或线程在CPU上执行的时间段。操作系统通过时间片来实现多任务处理,使得多个进程能够在同一个CPU上轮流执行,从而实现并发执行的效果。
当一个进程的时间片用完时,操作系统会将CPU资源重新分配给其他准备就绪的进程,这个过程称为上下文切换。通过时间片和上下文切换,操作系统可以实现公平地分配CPU时间给各个进程,同时保证系统的响应速度和效率。
ps:同一个时间片内多线程也并不是真正的并行,而是并发(cpu只能看到线程,线程是cpu调度分配的最小单位)
三、并发和并行
- 并行的多进程同时运行是真实存在的,可以在同一时刻同时运行多个进程
- 并行需要依赖多个硬件资源,单个是无法实现的