以下主要讲述进程到内存的映射
DOS时代 - 同一时间只能有一个进程在运行,单进程 windows9x开始,多个进程可以装入内存 引发问题: 内存撑爆 互相打扰
为了解决上面说的问题,引入现在的内存管理系统:使用虚拟地址、分页装入、软硬件结合寻址。
将内存分页(内存不够用),内存中分成固定大小的页框(4K),把程序(硬盘上)分成4K大小的块,用到哪一块,加载那一块,加载的过程中,如果内存已经满了,会把最不常用的一块放到swap分区, 把最新的一块加载进来(LRU算法)
例如,执行QQ.exe时,把它的分好页的页表记录下来,执行时,用到页表中的哪一页,就将这页加载进内存中。 在加载的过程中,如果内存已经满了,会把最不常用的一块放到swap分区(linux交换分区), 把最新的一块加载进来(LRU算法)。
几乎所有涉及到缓存的,都用到了LRU(Least Recently Used:最不常用)或LFU算法。
如何设计LFU缓存机制,支持获取数据,写入数据O(1)复杂度: 如果是数组,每个元素保存时间戳,如果查一遍,复杂度是O(n) 如果用单链表,最近用的加到尾部,头部肯定是最不常用。当中某一个又用到时,是需要挪动位置的,这些是指针操作O(1),但是查找某一个元素还是O(n) 改进:用hashmap哈希表(保证 查找操作O(1))+ 双向链表 (链表保证 排序操作和新增操作 O(1),双向链表保证找到的元素块的左边指针指向的块可以指向右边块),java的LinkedHashMap就是这样实现缓存
虚拟内存 为了保证互不影响 - 让进程工作在虚拟空间,程序中用到的空间地址不再是直接的物理地址,而是虚拟的地址,这样,A进程永远不可能访问到B进程的空间。 虚拟空间大小:看寻址空间 - 64位系统 2 ^ 64,32位系统2^32 (表达有2^32个不同的内存地址),而每个地址可以存放8bit的数据,即单位是byte 站在虚拟空间的角度,进程是独享整个系统 + CPU
虚拟空间分段,段内分页,需要哪页加载到页框
程序用的虚拟地址,那怎么和物理地址映射: 偏移量(如下20) + 段的基地址(如下1000) = 线性地址 (虚拟空间) 得到线性地址后,通过 OS + MMU(cpu内部硬件 Memory Management Unit)
通过下图再深入了解 P1,P2,P3,P4 4个进程都认为自己是独占整个内核的,实际上是共享操作系统内核。 MMU给每一个进程分配他们的内存资源。 如果内存装满了,使用LRU算法将最不常使用的页放入硬盘的交换空间中。
缺页中断 在执行一条指令时,如果发现需要用到页在内存中没有,那么停止该指令的执行,并产生一个缺页异常(中断),由内核处理并加载,之后,原先引起的异常的指令就可以继续执行,而不再产生异常。