【第 8 周】深入理解计算机系统共读心得体会

虚拟内存是现代系统提供的一种对主存的抽象概念,它将主存看成是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域按需在磁盘和主存之间来回传送数据;为每个进程提供了一致的地址空间,简化了内存管理;并且保护每个进程的地址空间不被其他进程破坏。

现代系统通过内存映射将虚拟内存和磁盘文件关联,为共享数据、创建新进程以及加载程序提供了一种高效的机制。应用可以使用mmap函数来手工地创建和删除虚拟地址空间的区域。对于运行时才知道大小的数据结构,大多数程序通过动态内存分配器管理虚拟地址空间中的堆区域,并且显式或自动地释放内存。

通过本章学习可以了解虚拟内存是如何工作的,以及如何在程序中使用和管理虚拟内存,避免一些和内存有关的错误出现。

  • 早期的PC通过物理寻址,现代处理器多采用虚拟寻址来访问主存,虚拟地址在被送到内存之前通过MMU地址翻译转换为物理地址。
  • 虚拟页,VM将虚拟内存分割为大小固定的块,虚拟页有以下三种:
    • 未分配的,没有任何数据与之关联,不占用任何磁盘空间;
    • 缓存的,当前已缓存在物理内存中的已分配页;
    • 未缓存的,未缓存在物理内存中的已分配页
  • DRAM缓存表示虚拟内存系统的缓存,是全相联的,因为虚拟页通常为4K-2M,不命中时替换开销较大,因此替换策略非常重要。因为对磁盘访问时间长,DRAM缓存总是使用写回而非直写。
  • 局部性原则保证了任意时刻,程序将趋向于在一个较小的active page集合上工作,即常驻集合/工作集。如果工作集的大小超出了物理内存大小,程序将产生一种不幸的状态,即抖动。页面将不断地换进换出。
  • 将一组连续的虚拟页映射到任意一个文件中的任意位置的表示法称为内存映射,Linux系统提供一个成为mmap的系统调用,允许应用程序自己做内存映射。
  • 正常命中时CPU硬件执行步骤:
    • 处理器生成一个虚拟地址并传送给MMU;
    • MMU生成PTE地址,从高速缓存/主存请求得到它;
    • 高速缓存/主存向MMU返回PTE;
    • MMU构造物理地址,并把它传送给高速缓存/主存;
    • 高速缓存/主存返回所请求的数据字给处理器
  • 当缺页时,需要硬件和操作系统内核写作完成:
    • 前三步相同
    • PTE有效位为0,MMU触发异常,传递CPU中的控制到操作系统内核中的缺页异常处理程序;
    • 缺页处理程序确定出物理内存中的牺牲页,如果该页已经被修改,则把它换出到磁盘;
    • 缺页处理程序页面调入新的页面,并更新内存中的PTE
    • 缺页处理程序返回到原来的进程,再次执行导致缺页的指令。主存将请求字返回给CPU。
  • 频繁读取PTE会使得内存多取一次数据,为了消除开销,现代系统利用TLB(MMU中一个PTE的小缓存)
  • 如果每个进程都在物理内存中保存常用代码的副本会比较浪费,内存映射提供了一种机制控制多个进程共享对象。一个对象可以被映射到虚拟内存的一个区域,作为共享对象或者私有对象。对于共享对象,进程对该区域的任何写操作,其他把该对象映射到虚拟内存的进程也可见。私有对象使用写时复制的技术被映射到虚拟内存中,延迟私有对象中的副本到最后可能的时刻来充分利用物理内存。
  • Fork调用时,内核为新进程创建虚拟内存,创建了当前进程的mm_struct、区域结构和页表的原样副本,将两个进程的每个页面都标记为只读,并将两个进程中的每个区域结构都标记为私有的写时复制。当fork在新进程中返回后,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同。当两个进程中的任何一个后来进行写操作时,写时复制都会创建新页面,即为每个进程保持了私有地址空间的抽象概念。
  • 程序使用动态内存分配的最重要原因是直到程序实际运行时才知道某些数据结构的大小,数据大小的最大值可以由可用的虚拟内存数量来限制。
  • 管理和使用虚拟内存中常见的错误类型:间接引用坏指针,读取未初始化的内存、允许栈缓冲区溢出、假设指针和它们指向的对象大小相同,引用指针而不是它指向的对象、误解指针运算、引用不存在的变量、以及引起内存泄漏等。

所在小组

第五组

组内昵称

郑伟钊

心得体会

虚拟内存

一,内存地址转换,其实就是这样三个步骤:

  • 1.把虚拟内存地址,切分成页号和偏移量的组合;
  • 2.从页表里面,查询出虚拟页号,对应的物理页号;
  • 3.直接拿物理页号,加上前面的偏移量,就得到了物理内存地址;
    20201108131312.jpg

我们的内存需要被分成固定大小的页(Page),然后再通过虚拟内存地址(Virtual Address)到物理内存地址(Physical Address)的地址转换(Address Translation),才能到达实际存放数据的物理内存位置。而我们的程序看到的内存地址,都是虚拟内存地址

二,动态内存分配器维护着一个进程的虚拟内存区域称作堆。

三,使用动态分配内存的原因是 有些数据结构是要在运行的时候才能知道空间的大小。

四,垃圾回收

20201108131113.jpg

####五,操作系统为每个进程提供了一个独立的页表,以此来为每个进程提供一个独立的虚拟地址空间。因此每个进程都使用相同的内存映像格式。

####六,线程调度都有哪些方法?

非抢占的先到先服务的模型是最朴素的,公平性和吞吐量可以保证。但是因为希望减少用户的平均等待时间,操作系统往往需要实现抢占。操作系统实现抢占,仍然希望有优先级,希望有最短任务优先。

七,大多数系统使用物理寻址来访问SRAM高速缓存。高速缓存无需处理保护问题,因为访问权限的检查是地址翻译过程的一部分。

所在小组

第五组

组内昵称

王传义

心得体会

  • cori7 结构没看懂,只理解地址转换在cpu完成,非内存完成。这说明物理内存只是物理内存。
  • 虚拟内存不同段存储第地址上不是连续的。因此在访问时候出现,小缺页,大缺页,只读三个异常。
  • 进程的虚拟内存区域通过映射方式直接把文件加载到内存。虚拟内存存储磁盘可能swap mmap
  • 虚拟内存出现,让动态库共享成为可能(file),虽然此时程序任务各自独占。只读 ,不然cow
  • 因为不确定内存需求情况,需要使用动态内存 new delete
  • 内存碎片造成heap 利用了低。
  • free 一个地址后,如果上下没有释放,这个地址标记为free,但是无法合并成更大的区块的。这也引证了free释放2个方式,小于128k 128k内存区别。
  • 忘记free为什么造成内存泄漏,因为这个区域依然标记被占用,系统并不知道
  • gc问题,不同语言不通处理,不是c++不提供,相关知识就学习了。1960就提出相关理论、过去优点,可能变成今天确定 ,gc通过图来维护,c无法来维护。

所在小组

第四组

组内昵称

魏琮

你的心得体会

虚拟内存 物理地址:计算机的主存被组成一个由M个连续字节大小的单元组成的数组,每个字节都有唯一的物理地址。CPU使用这种地址就叫做物理寻址 现代操作系统使用虚拟寻址,CPU生成的是虚拟地址(Virtual Address,VM),由MMU翻译成物理地址。地址翻译需要硬件和操作系统紧密合作。CPU上有内存管理单元(MMU)利用存放在主存上的查询表来动态翻译虚拟地址,该表的内容有操作系统内核管理

地址空间 在一个带虚拟地址的系统中,CPU在一个N=2n个地址的地址空间生成虚拟地址,每个地址空间称为虚拟地址空间。这里的n就是地址空间大小,也就是常说的系统是32位还是64位 地址空间的概念成功区分了数据对象(字节)和它们的属性(地址)。虚拟内存的基本思想就是允许每个数据对象有多个独立的地址,每个地址都选自不同的地址空间。因此对于主存中的字节都有一个来自虚拟地址空间的虚拟地址和一个来自物理地址空间的物理地址

缓存 逻辑上讲,虚拟内存被组成一个由存放在磁盘上的N个连续的字节大小的单元组成的数组。磁盘上的数据被缓存到主存中。因为磁盘和主存的传输单元为块,VM系统通过将虚拟内存分为虚拟页(VP)大小的固定块来做统一,同样物理页(PP)也分割为同样大小的块,称为页帧 为了讨论清晰,用SRAM缓存表示位于CPU和主存之间的L1、L2和L3高速缓存,用DRAM表示虚拟内存系统的缓存,它在主存中缓存虚拟页 页表 虚拟内存系统需要判断虚拟页是否缓存在DRAM中,并确定是在哪个物理页上,如果没有缓存,系统要判断存放在磁盘的位置,并将其复制到DRAM中

这些功能需要操作系统软件、MMU中的地址翻译软件和一个存放在物理内存中的叫做页表(Page table)的数据结构联合提供。 CPU输出一个VA到MMU的地址翻译硬件,其将VM作为一个索引来定位在页表中的位置(如定位到了PTE 2),由于PTE 2的有效位为1,地址翻译硬件就知道要找的数据就存放在DRAM中,也就是命中,之后就会使用有效位接下来的物理内存地址,构造数据的物理地址 如果没有命中,也就是虚拟地址索引的PTE有效位为0,就发生缺页异常,这会调用内核中的缺页异常调用程序。程序会在DRAM中选择一个牺牲页,将其复制回磁盘,并修改页表。接下来就是根据磁盘地址从磁盘中把缺页复制到内存中。当异常处理程序返回时,就会重启导致缺页的指令。这种直到不命中时,才将页从DRAM换入或换出磁盘的策略就叫按需页面调度 虽然页面调度的处罚代价非常高,但是虚拟内存工作的非常好,因为局部性。因此只要程序有良好的时间局部性,虚拟内存系统就能工作的相当好

地址翻译 地址翻译是一个N元素的虚拟地址空间(Virtual Area Space,VAS)中的元素和一个M元素的物理地址空间(PAS)中元素的映射 CPU中有一个页表寄存器(PTBR),指向当前页表,虚拟页号根据当前页表找到PTE,MMU根据得到的PPN和PPO组合出新的物理地址 当页表命中时,完全是由硬件在处理。MMU根据虚拟地址和当前页表寄存器值得出PTE的地址,并送给Cache/主存,返回PTE之后,组合得到物理地址,再一次向主存/Cache请求,得到数据 当发生缺页时,就要硬件和操作系统内核协作完成。MMU首先检测到得到的PTE的有效位为0,就会触发异常,CPU检测到这个异常,就会根据异常表,跳转到内核的缺页异常处理程序,由内核来将缺页读入主存,最后处理程序返回,CPU重新发送缺页指令 为了消除MMU每次都要向内存查询PTE,通常在MMU中设置一个关于PTE的小缓冲(Translation Lookaside Buffer,TLB),这样读取PTE的周期从几百个降到了几个 多级页表 这种方式减少了内存要求,第一如果第一级页表中的PTE为空,相应的二级页表就不会存在,第二只有一级页表总是存在主存中,二级页表只在需要时创建,并缓存在主存中

内存映射 Linux通过一个虚拟内存区域与一个磁盘对象关联起来,以初始化这个虚拟内存区域的内容 进程使用该mmap函数创建新的虚拟内存区域,并将对象映射到这些区域中 void *mmap(void *start,size_t length,int prot,int flags, int fd,off_t offset); 动态内存分配 使用动态内存分配器具有更好的移植性。显式分配器就是类似于C中的malloc,隐式分配器就类似于Java中的垃圾回收9 动态内存分配器维护着一个进程的虚拟内存区域,称为堆

垃圾回收 也是一种动态内存分配器,它自动释放程序不再需要的已分配块

所在小组

第二组

组内昵称

陶鑫

你的心得体会

地址翻译是一个 N 元素的虚拟地址空间中的元素和一个 M 元素的物理地址空间中元素之间的映射

虚拟内存给予程序员一片连续的地址空间,使得程序员更加容易的编程,同时也使操作系统更加方便地管理内存

虚拟内存地址,由mmu处理,pagetable只负责映射,因此,pagetable可以将不同进程的不同虚拟地址映射为同一块物理地址,使共享成为可能。

同时因为现在内存地址越来越多,导致pagetable的表项也更加多,虽然pagetable的一个表项只占很少的内存,但是因为每一个进程都有自己专属的pagetable所以如果进程多了的话,占据的内存也仍然十分的多。因此,我们需要多级页表,而多级页表的好处是不需要为所有的可能表项都分配内存,按需分配。

常用使用层次结构的页表来亚索页表TLB 是利用VPN的位进行虚拟寻址的高速缓存:直接映射的缓存是用过物理地址中的字段来寻址的
PTE有三个权限位,用来控制对页的访问R/W 位确定页的内容是可以读写的还是只读的
U/S 位确定是否能够在用户模式中访问该页XD位是在64位系统中引入的,可以用来禁止从某些内存页取命令内核虚拟内存包含内核中的代码和数据结构

Linux 通过将一个虚拟内存区域与一个磁盘上的对象关联起来,以初始化这个虚拟内存区域的内容映射到匿名文件的区域中的页面有时也叫请求二进制零的页。一旦一个虚拟页面被初始化,它就在一个由内核维护的专门的交换文件之间换来换去。交换文件也叫做交换空间或者交换区域

在任何时刻,交换空间都限制着当前运行着的进程能够分配的虚拟页面的总数一个对象可以被映射到虚拟内存的一个区域,要么作为共享对象,要么作为私有对象。

所在小组:第二组
组内昵称:跃山
心得体会:

虚拟内存

物理和虚拟寻址

虚拟内存是对于物理内存的抽象。支持虚拟内存的处理器,通过使用虚拟寻址这种间接方法来引用物理内存。

处理器产生一个虚拟地址,在被发送到物理内存之前,这个地址被翻译成一个物理地址。

  1. 物理寻址
  2. 虚拟寻址

地址空间

虚拟内存的作用

虚拟内存提供了三个重要的功能

  1. 在物理内存中自动缓存最近使用的存放磁盘上的虚拟地址空间的内容。(虚拟内存缓存中的块叫做页)。对磁盘上页的引用会触发缺页,缺页将控制转移到操作系统中的一个缺页处理程序。缺页处理程序将页面从磁盘复制到物理内存缓存。
  2. 虚拟内存简化了内存管理,进而简化了链接,在进程间共享数据,进程的内存分配以及程序加载。
  3. 虚拟内存通过在每条页表中加入保护位,从而简化了内存保护

虚拟内存作为缓存的工具

  1. DRAM缓存的组织结构
  2. 页表
  3. 页命中
  4. 缺页
  5. 分配页面

虚拟内存作为内存管理的工具

虚拟内存作为内存保护的工具

地址翻译

大多数页表条目位于L1高速缓存中,但是TLB的页表条目的片上高速缓存,通常会消除访问在L1上的页表条目的开销。

  1. 结合高速缓存和虚拟内存
  2. 利用TLB加速地址翻译
  3. 多级页表端到端的地址翻译

内存映射

内存映射就是,把虚拟内存和磁盘映射起来,从而提高性能。

  1. 共享对象
  2. fork函数
  3. execve函数
  4. 使用mmap函数的用户级内存映射(如mongoDB及pgsql等db都是通过mmap机制(借助mmap函数实现)来提高性能。)

动态内存分配

  1. 显式分配器。要求应用显式地释放他们的内存块。
  2. 隐式分配器(GC)。自动释放任何未使用或者不可达的块。

为什么要使用动态内存分配?

虽然我们可以使用mmap函数或者munmap函数来创建和删除虚拟内存,但是使用动态内存分配更方便,也有更好的移植性。

分配器的要求和目标

隐式空闲链表

所在小组
第一组

组内昵称
广州-盆栽

心得体会

这里

所在小组

第五组

组内昵称

chensongbin

你的心得体会

Chapter9

Subject: Virtual Memory虚拟内存

Date: 2020-11-15

地址翻译

大致流程如图:

image

  • VPN: Virtual Page Number
  • VPO: Virtual Page Offset
  • TLBT: TLB Tag
  • TLBI: TLB 索引
  • CR3: Intel Core i7中的寄存器,存储指向一级页表的指针
  • PTE: Page Table Entry
  • PPN: Physics Page Number
  • PPO: Physics Page Offset
  • CT: Cache Tag
  • CI: Cache Index
  • CO: Cache Offset

内存映射

  • 将虚拟内存片与磁盘上的文件片关联起来,将该关联信息存在vm_area_struct中
  • 这样每当第一次读取虚拟内存的某个页表时候,会触发缺页异常,根据vm_area_struct中的信息,将磁盘中的数据加载到内存,然后再去更新对应的Page Table Entry
  • 每个虚拟内存片(vm_area_struct)包含的信息有:
    • 开始和结束的地址
    • vm_prot: 这个区域中所有页的读写权限
    • vm_flags: 这个区域中的页是共享的还是私有的
      • 共享:该区域中所有的页,是多个进程共享的,某个进程的写,对其他进程可见,而且会同步到磁盘中
      • 私有:进程写某个页,发生页保护异常时,发现是只读并且私有的,会在物理内存中创建该页的一个副本,并更新页表项,指向副本(写时复制:只有在写的时候才在物理内存中创建也表,极大节约物理内存)

Shell 运行一个新进程的原理

fork 函数:

  • 内核为新进程创建各种数据结构
  • 创建了当前进程的mm_struct、struct_area和page table的副本
  • 将两个进程的每个页面都标为只读,每个struct_area都标记私有的写时复制

execve 函数

  • 删除已存在的用户struct_area
  • 映射私有struct_area
  • 映射共享struct_area
  • 设置 程序计数器
  • 执行指令时候,通过缺页异常,将代码加载到内存中

有意思的点

  • 每个进程在内核都维护有一个page table,怎么理解书中:「虽然Core i7体系结构允许页表换进换出,但是与已分配了的页相关联的页表都是驻留在内存中的」?

     一级页表的所有页表项是不是应该都是常驻内存中??
    
  • 抖动:页面不断的从DRAM 到 硬盘间换进换出

  • 统计缺页次数:getrusage

所在小组
第四组

组内昵称
彳亍

你的心得体会

“物理地址”(physical address)很容易理解,就是主存中按顺序编排的字节单元序号。使用物理地址来访问主存也是最自然的一种方式,直到今天,像“数字信号处理器、嵌入式微控制器等系统依然使用这种方式访问主存”。但为了满足更加复杂的资源调度需求,现代处理器更多的使用的是虚拟寻址(virtual addressing0)的寻址形式。

虚拟地址的翻译(即将虚拟地址转换为实际对应的物理地址)需要硬件和软件密切配合。其中起关键作用的硬件是 CPU 上的内存管理单元(Memory Management Unit,MMU);“软件”部分则指的是存放在主存中的查询表。这个查询表由操作系统管理和维护。

地址空间又分为“虚拟地址空间”和“物理地址空间”,我们通常讨论的一般是虚拟地址空间。

所在小组
第一组

组内昵称
张仁杰

心得体会

Linux缺页异常处理:

  1. 虚拟地址A合法吗?如果合法,缺页处理程序搜索区域结构的链表,把A结构中的VM_Start和VM_End作比较;如果不合法那么将出发一个段错误,从而中止这个程序
  2. 试图加载的内存访问是否合法?此时是否对一个只读的区域经行写,或者说用户模式中的进程尝试从内核虚拟内存中读取字;如果不合法那么缺页会出发一个保护异常,从而中止这个程序
  3. 此时内核已经知道由于合法的虚拟地址进行合法的操作造成的,那么此时的处理办法是: 选择牺牲这个页面,如果牺牲的这个页面被修改过,那么将它更换出去,换新的页面并更新页表。
    缺页处理

现代系统通过虚拟内存片和磁盘上得文件关联起来,来初始化虚拟内存篇,这个过程称为内存映射

c++中的收集器叫做保守垃圾收集器,与Java不同,c++中的收集器可能存在某些不可达的节点被标为可达。

所在小组

第七组

组内昵称

高华

心得体会

  • 虚拟内存是有效的物理内存的保护工具,通过在 PTE 上添加一些额外的许可位来控制对一个虚拟页面的访问变得非常简单。

  • 地址翻译


  • 多级页表,从两个方面减少了内存的要求

    1. 如果一级页表中的一个 PTE 是空的,那么相应的二级页表就根本不会存在,这是对内存空间潜在的巨大节约
    2. 只有一级页表才需要总是在主存中,虚拟内存系统可以在需要时再创建、页面调入或调出二级页表,极大程度上减少了内存的压力。
  • 动态内存分配,在程序中使用和管理虚拟内存

  • 垃圾收集,垃圾收集器将内存视为一张有向可达图…

所在小组

第五组

昵称

张学广

学习内容

本周主要学习了下第九章-虚拟内存。

首先了解下虚拟内存出现的背景,在出现之前,我们直接使用的物理内存(主存),这样会出现几个问题:

  1. 系统内存不安全
  2. 内存不够用
  3. 地址有偏移量,程序复杂

虚拟内存的出现是对硬件异常、硬件地址翻译、主存、磁盘文件以及内核软件等内容的结合,虚拟内存为每个进程提供一个完整的虚拟内存空间使用,大家都有自己的内存空间,彼此之间不会互相影响。

其中,书上总结的虚拟内存三个能力如下:

  1. 主存作为高速缓存,只保留活动内容
  2. 每个程序有一致的内存空间
  3. 保护了每个地址的进程空间不被破坏

本章前一部分主要介绍了虚拟内存的工作原理,后一部分描述了程序是如何使用和管理虚拟内存的,总结内容如下:

工作原理

以一个内存命中流程为引子,来了解下虚拟内存的工作原理。

首先理解下内存地址是如何找到物理内存的,内存是以页的形式管理的,以32位系统为例,页的大小为4KB,内存地址分为3部分,第一部分为10位,标识1024个页表,第二部分的10位用来标识对应物理内存页的地址,最后12位用来标识物理页中的偏移值,这样就可以通过32位的地址,锁定到4GB内存中具体的数据了。之后我们再来看页表中的内容,因为每个页的大小都是4KB,所以页表中的低12位一定存着0,所以这里的空闲空间可以使用来存储对应物理页可以可写以及是否映射等信息,映射是我们的关键,如果这里没有映射,就会触发缺页中断,操作系统会找到对应的内存页存储的磁盘空间,替换到主存中。

程序使用

程序拥有的虚拟内存分为多个段,其中我们主要关心的是堆栈,栈中的数据是程序自动申请的临时空间,当前程序段执行结束后,对应的栈的空间就会被收回,这里要注意的是栈空间溢出问题。

堆是我们主要关心的,一些持久数据的存储,可以通过malloc这样的函数去申请堆空间,堆空间的数据是持久的,生命周期伴随这程序的结束,因此为了避免无效的内存使用,应该有意识的使用free去释放申请的资源。

所在小组

第四组

组内昵称

murphy

你的心得体会

Dynamic Memory Allocation

关于动态内存分配,其主要的生命周期其实可以概括成两步:

  1. 找到一块合适的内存,分配出去。
  2. 把这个内存回收回来。

而对于一个内存分配器来说,其分配结果是好还是坏,有两个重要的指标:

  1. 吞吐率。
  2. 内存利用率。

但是这两者通常情况下是需要进行取舍的,无法同时满足。因为在内存分配的时候,会产生内存碎片,来影响内存利用率,而如果对内存碎片进行处理的话,就会不可避免的增加分配内存的步骤从而降低分配的吞吐量。

碎片可以分成两种:

  1. 内部碎片:因为对齐之类的块分配规则而不得不产生于块内部的碎片。(导致了分配出去的内存比需要的内存大)
  2. 外部碎片:在内存不断的进行动态分配和回收的过程中,产生的大量的细小的未分配空间(会导致当你分配一个1MB的空间时,明明你未分配的空间远大于1MB,但由于未分配空间每个都不满1MB而导致无法分配)

内部碎片是由分配器的实现方式决定的,是可控的,可感知的。而外部碎片则难以追踪的,难以处理,不可预测。

分配器的数据结构

分配器想要找到一个合适的内存把他分配出去,然后将其回收之后,还能再分配出去,并且想办法减少碎片,需要一个数据结构来解决这个问题。

分配器的数据结构主要分为两种:

  1. 隐式空闲链表
  2. 显式空闲链表

隐式空闲链表本质上就是一个普通的链表,为每个分配的或者未分配的空间添加一个头部,记录着这块的大小和是否被分配,从而形成一个链表结构。当需要分配的时候,就顺序遍历整个链表找到自己想要分配的块,把他分配出去。

而等到需要回收的时候,就把这块内存标为未被使用即可。

但是这会导致假碎片的现象。为了减少假碎片,我们需要将连续的未分配的空闲块进行合并。

合并的时机有两种:

  1. 立即合并
  2. 推迟合并

举个例子,在隐式空闲列表中进行立即合并的话,我们可以为空闲列表添加脚部,记录块的大小,从而构成双向链表,就解决了这个问题。

所在小组

第六组

组内昵称

利健锋

你的心得体会

一个对象被映射到虚拟存储器的一个区域,这个区域要么是共享对象,要么是私有对象。

当当前进程调用fork函数的时候

  1. 内核为新进程创建各种数据结构
  2. 并分配PID

execve 函数如何加载和执行程序?

  1. 删除已存在的用户区域
  2. 映射私有区域
  3. 映射共享区域
  4. 设置程序计数器

一个堆中的块只有两种状态和两种分配方式

  • 状态

    1. 已分配的
    2. 空闲的
  • 分配方式

    1. 显式
    2. 隐式

为什么需要隐式的分配?
因为碎片的产生会降低存储空间的利用率

碎片分为两种

  • 内部碎片
  • 外部碎片

经常调用 leak 又不释放的情况下,时间一长,内存将会充满垃圾。

所在小组

第六组

组内昵称

黄永平

你的心得体会

从物理内存到虚拟内存

通过 MMU(Memory management unit)把虚拟地址(Virtual Address, VA)转换为物理地址(Physical Address, PA),再由此进行实际的数据传输

虚拟空间

每个进程都有自己的虚拟地址空间,这样一来,对于进程来说,它们看到的就是简单的线性空间.虚拟内存带来的另一个好处就是可以简化链接和载入的结构

动态内存分配

程序员通过动态内存分配(例如 malloc)来让程序在运行时得到虚拟内存。动态内存分配器会管理一个虚拟内存区域,称为堆(heap)。
分配器以块为单位来维护堆,可以进行分配或释放。有两种类型的分配器:
显式分配器:应用分配并且回收空间(C 语言中的 malloc 和 free)
隐式分配器:应用只负责分配,但是不负责回收(Java 中的垃圾收集)

内存碎片

内部碎片指的是对于给定的块,如果需要存储的数据(payload)小于块大小,就会因为对齐和维护堆所需的数据结构的缘故而出现无法利用的空间

所在小组

第七组

组内昵称

李佳

你的心得体会

  • 页面命中步骤:生成虚拟地址,发送给MMU,MMU查询得到PTE,从存储系统中得到他,MMU构造物理地址传送给存储系统,存储系统返回给处理器。
  • 处理缺页需要用到内核的异常处理程序。属于一次异常。
  • TLB是一个小的虚拟寻址的缓存,每一行为一个单个PTE组成的块,相当于加了一层虚拟->物理中间缓存。
  • 多级页表:将页表分层,减少主存压力,只有经常使用的二级页表才存储到主存中。
  • Linux将虚拟存储器组织成区域,每个虚拟页面都保存在某个区域中,区域使得允许虚拟地址空间有间隙。
  • 存储器映射:普通文件来说一个区域映射到连续部分,文件区被分成页的大小,每一片包含一个虚拟页面的内容。
  • 许多文件有相同的文本区域时,每个进程去复制浪费性能,于是一个对象可被映射到一个区域,作为共享对象/私有对象。
  • 动态存储器维护一个堆,每个块状态为已分配/空闲,由显式分配器/隐式分配器分配/释放。
  • 垃圾收集也为动态存储分配器。

所在小组

第七组

组内昵称

陈盛华

心得体会

在linux环境下管理虚拟内存是由一个叫mmu(内存管理待单元)的硬件模块来管理的,它缓存了最近被调用的页。用一个例子去理解内存,在一个1核心4GB内存20GB机械硬盘的机器上,开启一个读10GB大文件的python进程,这个进程会在3G~4G的内核地址空间里面占有一段空间,在系统中数据的读写是基于‘缓冲池页’的,有一页缓冲页的大小一般和硬盘相同普遍是16kb,缓冲池由多个16KB页组成,当缓冲池的页面满了,为了读取后面的文件数据,需要把当前的缓冲页移出到硬盘,再读取数据到缓冲页,而在页面移进和移出会涉及算法的调度。

高效并涉及硬件的算法有‘最近最少使用页面置换算法(LRU算法)’:将最近被调用过的页面缓冲到MMU中,在需要的时候再调用出来。

理论最优的算法:OPT(最佳置换算法),但是需要提前知道后面的页面地址,在计算机有太多进程和信息,对环境的需求太高了,所以不被常用。

还有一些算法:FIFO(先进先出算法)、Clock(时钟置换算法)、LFU(最不常用算法)、MFU(最常使用算法)等

所在小组

第二组

组内昵称

叶王

心得体会

  • 地址翻译是硬件支持虚机存储的关键步骤,通过常驻内存中的页表将虚拟地址翻译为物理地址。
    • 程序看到的内存地址都是虚拟的,分页是一种内存管理方案,它允许进程的物理地址空间不连续,将物理内存划分为称为帧的固定大小的块,将逻辑内存分成大小相同的块(称为页)。
    • 地址翻译从形式上来说就是建立一个虚拟地址空间到物理地址空间的映射关系,MMU 使用的是页表来实现这种映射,CPU 中有一个专门的页表基址寄存器(PTBR)指向当前页表。
    • 每个虚拟地址由两部分组成:虚拟页号(VPN)+ 虚拟页偏移量(VPO),当 CPU 生成一个虚拟地址并传递给 MMU 开始翻译的时候,MMU 利用虚拟地址的 VPN 来选择相应的 PTE,同时将页表中的物理页号(PPN)+ 虚拟地址的 VPO 就生成了相应的物理地址。
  • 动态存储器分配来管理虚拟存储更方便,通过维护一个进程的虚拟存储器区域,称为堆(heap)。
    • 显式分配:程序调用 malloc 和 free 函数。
    • 分配器的目标主要是找到吞吐量和利用率的契合点,因为碎片的产生会降低存储空间的利用率,所以需要隐式的分配。
  • 垃圾收集是一种很有用的方法,当使用了 malloc 分配了空间却忘记了释放,就会造成内存的极大浪费。垃圾收集就是使用特殊的方法,定期回收这部分不使用或者无效的空间。
    • 4 种最基本的垃圾回收策略:标记-清扫(mark-sweep)、标记-复制(mark-copy)、标记-整理 (mark-compact), 引用计数时。

所在小组

第六组

组内昵称

杨凯伟

心得体会

操作系统利用虚拟内存管理内存。

虚拟内存(Virtual Memory)可以看作磁盘与DRAM主存之间的缓存,也可以看作物理内存(Physical Memory)与进程/操作系统间的视图,起内存管理的作用。我们使用地址空间(Address space)来描述物理内存和虚拟内存两种不同的概念。现代处理使用虚拟寻址,虚拟地址转为物理地址的的任务叫地址翻译。虚拟内存的作用主要有下面三点:

  • 缓存作用,提高访问主存的效率

  • 简化内存管理

  • 内存读写保护,进程间内存空间相互隔离,进程无法访问内核内存

我们在操作系统中配置的虚拟内存一般是指“用磁盘空间来扩展物理内存”的。虚拟内存不只是“用磁盘空间来扩展物理内存”的意思,把内存扩展到磁盘只是使用虚拟内存技术的一个结果。

所在小组

第六组

组内昵称

钟荣荣

你的心得体会

地址翻译:N元素的虚拟地址空间和M元素的物理地址空间的元素之间的映射;使用页表实现映射:cpu中的控制寄存器,页表基址寄存器指向当前页表,n位的虚拟地址包含两部分:p位的虚拟页面偏移和n-p位的虚拟页号。MMU利用VPN来选择适当的PTE。将页表条目中的物理页号和虚拟地址的VPO串联起来,就得到相应的物理地址;
Linux虚拟内存系统:linux为每个进程单独维护了一个虚拟地址空间;内核虚拟内存包含内合作的代码和数据结构。内核虚拟内存的某些区域被映射到所以进程共享的物理页面。