请大家以回帖的方式将你在共读第一周的心得体会用你自己的话表达出来。
样例:
所在小组
第一组
组内昵称
张三
你的心得体会
可以基于不同知识点进行,有更新请在原回贴更新,每人每周只发一个帖子
一段自己的阐述
第二段自己的阐述
…
请大家以回帖的方式将你在共读第一周的心得体会用你自己的话表达出来。
样例:
第一组
张三
可以基于不同知识点进行,有更新请在原回贴更新,每人每周只发一个帖子
…
第六组
蒋权
什么是虚拟内存?它的作用是什么?
为了更加有效地管理内存并且少出错,现代系统提供了一种对主存的抽象概念,叫做虚拟内存(VM)。
虚拟内存是硬件异常、硬件地址翻译、主存、磁盘文件和内核软件的完美交互,它为每个进程提供了一个大的、一致的和私有的地址空间。通过一个很清晰的机制,虚拟内存提供了三个重要的能力:(1)它将主内看成是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据,通过这种方式,它高效地使用了主存;(2)它为每个进程提供了一致的地址空间,从而简化了内存管理;(3)它保护了每个进程的地址空间不被其他进程破坏。
物理寻址
虚拟寻址
虚拟内存系统必须由某种方法来判定一个虚拟页是否缓存在 DRAM 中的某个地方。如果是,系统还必须确定这个虚拟页存放在哪个物理页中。如果不命中,系统必须判断这个虚拟页存放在磁盘的哪个位置,在物理内存职工选择一个牺牲也,并将虚拟页从磁盘复制到 DRAM 中,替换这个牺牲也。
这些功能是由软硬件联合提供的,包括操作系统软件、MMU(内存管理单元)中的地址翻译硬件和一个存放在物理内存中叫做页表(page table, pgd)的数据结构,页表将虚拟页映射到物理页。每次地址翻译硬件将一个虚拟地址转换为物理地址时,都会读取页表。操作系统负责维护页表的内容,以及在磁盘与 DRAM 之间来回传送页。
页表就是一个页表条目(Page Table Entry, PTE)的数组。
页命中
通过虚拟地址查询数据时,该数据正好被缓存在物理内存中(缓存命中),则直接返回相应数据
缺页
通过虚拟地址查询数据时,该数据没有被缓存在物理内存中(缓存不命中),此时,地址翻译硬件会触发一个缺页异常,缺页异常调用内核中的缺页处理程序,该程序会选择一个牺牲页(如果该牺牲页曾经被修改过,则会将其写回磁盘,持久化下来),将目标物理页复制到原来牺牲页的位置,接着异常程序处理结束,返回,它会重新启动导致缺页的指令,该指令会把导致缺页的虚拟地址发送到地址翻译硬件,此时,必然会导致一个页命中,返回相应数据给 CPU。
把 PTE 缓存在高速缓存中
尽管页表是虚拟内存使用的一个管理内存的工具,但是,页表本身也是数据,是数据,人们就会想到如何缓存它,以提高访问效率
最坎坷的一条路:
VA → MMU → PTEA → PTEA不命中 → PTEA → 内存 → PTE → PTEA命中 → PTE → MMU → PA → PA不命中 → PA → 内存 → 数据 → PA命中 → 数据 → 处理器
最顺畅的一条路:
VA → MMU → PTEA → PTEA命中 → PTE → MMU → PA → PA命中 → 数据 → 处理器
把 PTE 缓存在 TLB 中
利用 TLB 加速地址翻译
如图 9-14 所示,每次 CPU 产生一个虚拟地址,MMU 就必须查阅一个 PTE,以便将虚拟地址翻译为物理地址。在最糟糕的情况下,这回要求从内存多取一次数据,代价是几十到几百个周期。如果 PTE 碰巧缓存在 L1 中,那么开销及下降到 1 个或 2 个周期。然而,许多系统都视图消除即使是这样的开销,它们在 MMU 中包括了一个关于 PTE 的小的缓存,称为翻译后备缓冲器(Translation Lookaside Buffer, TLB)。
TLB 是一个小的、虚拟寻址的缓存,其中每一行都保存着一个由单个 PTE 组成的块。
当 TLB 命中时,所有的翻译步骤都是在芯片上的 MMU 中执行的,因此非常快。
多级页表
我认为,多级页面的核心作用是用时间换空间。也就是说,其实多级页表比一级页表运行起来要慢,但是可以减轻内存的压力。
关于空间,见本小节(9.6.3 多级页表)的开头
“到目前为止,我们一直假设系统只用一个单独的页表来进行地址翻译。但是如果我们有一个 32 位的地址空间、4KB 的页面和一个 4 字节的 PTE,那么即使应用所引用的只是虚拟地址空间中很小的一部分,也总是需要一个 4MB 的页表驻留在内存中。对于地址空间为 64 位的系统来说,问题将变得更为复杂。”
算一些这笔账:
32位地址空间,即共有2^32个虚拟地址
每个页面(即页)4KB,即2^12
那么虚拟地址空间中共有2^32除以2^12,即2^20个页面(即页)
每个页都需要对应一个页表条目(即PTE),故页面条目的个数也是2^20
而每个 PTE 的大小是 4 字节,即2^2,那么2^20页表条目的总大小就是 2^22 字节了,即 4MB
关于时间,见本小节的这两段:
“二级页表中的每个 PTE 都负责映射一个 4KB 的虚拟内存页面,就像我们查看只有一级的页表一样。注意,使用 4 字节的 PTE,每个一级和二级页表都是 4KB 字节,这刚好和一个页面的大小是一样的。
这种方法从这两个方面减少了内存要求。第一,如果一级页表中的一个 PTE 是空的,那么相应的二级页表就根本不会存在。这代表一种巨大的潜在节约,因为对于一个典型的程序,4GB 的虚拟地址空间的大部分都会使未分配的。第二,只有一级页表才需要总是在主存中;虚拟内存系统可以在需要时创建、页面调入或调出二级页面,这就减少了主存的压力;只有最经常使用的二级页表才需要缓存在主存中。
第七组
杨文
静默组
清风环佩
静默组
Tang_D
第二组
Joey
虽然页表可以提供快速的虚拟地址到物理地址的映射,但是如果每次都是去主存的某个位置查PTE,就太慢了,所以MMU内部有一个成为TLB的东西作为页表的缓存。当然TLB也会有不命中的情况,这时候就需要从主存里查PTE,更新TLB。PS:PTE查询是O(1),因为VM包含了VPN和VPO,可以直接定位到PTE的位置。
对于4G的VM,4K的page,一个进程的全部虚拟地址只需要4M的额外空间就可以存下页表。这样做的潜在浪费是,默认所有的VM都和主存有映射关系,而实际上一不是所有的进程都能用4G的内存。多级页表主要是为了解决这种潜在的浪费,特别是在64位环境下。
多级页表的设计思想是:
通过虚拟内存系统可以实现对象在进程之间的私有共享或私有映射。(.so是共享映射)
mmap函数可以为虚拟内存和对象(fd)之间创建映射关系,书上讲的比较简略,可以参考这篇文章:https://www.cnblogs.com/huxiao-tee/p/4660352.html
第五组
锦锐
There are three main reason .
第四组
Helios
当一个进程想要访问内存,如果访问的都是物理内存的话,这就容易乱套了,这样程序员和编译器都很难看。当然啦,现在现在只有几k内存的单片机就是这么做的
,关键是人家也不用那么复杂呀,就是可能是亮一个LED灯泡就行了。
所以就给进程安排个虚拟内存呗,让进程觉得自己的小盒子就是整个世界(用gcc编译出来的汇编代码的地址都是一样的,链接的时候去重定向),想怎么用怎么用
,当然他也能时常仰望星空(内核空间),但是一旦出去就会被打回来(报错)。如果想买点地盖个房(write)或者看点其他人的房产记录(read)咋办呢,和政
府去申请呗(系统调用),他是真的啥权限都用。
这个时候政府的压力就大了,还有管理一个个进程小盒子(虚拟内存管理)还要管理各个小盒子能用真实空间(物理内存管理),所以政府就负责两者之间的对应>关系(虚拟内存到物理内存的映射)。
盒子越来越多,但是他们问的常见问题就那个几个,所以把那些放在最快能给他们的地方(TLB),还有次常用的就放在大厅里面谁来看(放在内存中的虚拟内存页
),如果有比较便的问题就去咱们的仓库里面拿(放在磁盘上的虚拟内存页)
这对于政府来说是个难题了,有的要大块地,有的小块的地咋办呢。他们之间在政府(内核)中的分配不一样,政府分配最小单位(一页=4K),当大约一定量的时候用mmap,小量的时候用brk,还有些不到最小单位的,政府就把最小单位(4K)分了,等着给人用。如某个系统经常申请大块空间咋办勒,就把最小单位调大一些
(内存大页,4M或者更大),这样分配的最小单位就小了。
如果是线性分配,就会出现随便,那么空间合并是个重要问题(内存碎片)。
心得体会
虚拟内存作为内存管理的工具。
第二组
可可
每章的引言,表达这本章的根本用意
在了解了虚拟内存的各种使用和好处(例如内存保护管理)外,在想虚拟内存究竟为什么而提出,又如果在程序员无感知的情况下默默的付出。
程序总是贪婪的,他可以占着很多内存而不使用它。
不能保证每个程序都是好人,他可以去访问甚至修改本不属于他的东西。
而以上的这一切对于OS来说,都是不可饶恕,要去解决的。所以就有了VM。
VM 将主存(内存)看成存储在磁盘上的高速缓存
这样的好处?
- 通过类似扇入扇出和局部性来高效的使用内存。而不是交由程序去完成?
为每个进程提供了一致的地址空间
无论编码还是管理都方便了很多
保护每个进程的地址空间不被其他进程破坏
安全性保证
迷惑堆行为
go的内存分配,又是如何
合理利用再分配
所在小组
第二小组
组内昵称
梁广超
心得体会
1、地址翻译 虚拟地址组成 VPO + VPN,物理地址是组成是PPO + PPN。寻址过程是通过VPN在PTE中找到物理地址的PPN, 再于虚拟地址的VPO组成真实的物理地址
2、为了减少MMU查找PTE时,需要从内存中加载PTE的次数,根据内存局部性原理,MMU中加入了TLB技术
3、多级页表类似跳表,将虚拟地址的VPO分成多级,如果二级PTE没有被分配,那么一级PTE是空的,另外只有一级列表才需要一直在内存,如此可以节省内存
4、内存映射,linux通过将一个虚拟内存区域与一个磁盘对象关联起来,在io多路复用中,epoll相比于select的一个优势就是利用内存映射,减少了数据在用户态和内核态的拷贝
5、垃圾回收,将内存视为一张有向可大图,将不可达的内存碎片,视为垃圾
6、c程序常见内存有关错误
间接引用坏指针
读未初始化内存
允许栈缓冲区溢出
假设指针和她们指向的对象是相同大小的
造成错位错误
引用指针,而不是它所指向的对象
引用不存在变量
引用空闲堆块中的数据
引起内存泄漏
第五组
肖思成
所在小组
第七组
组内昵称
吴奇驴
你的心得体会
最近看了感觉有用的东西(因为没来的及看完)
感想:
第三组
kippa
虚拟存储器是硬件异常,硬件地址翻译,主存,磁盘文件和内核软件的完美交互。为每个进程提供一个大的,一致的和 私有的地址空间。提供了3个重要能力:
将主存看成磁盘地址空间的高速缓存
为每个进程提供一致的地址空间,简化存储器管理
保护了每个进程的地址空间不被其他进程破坏
CPU通过生成一个虚拟地址来访问主存,虚拟地址转换为物理地址叫做地址翻译。
虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。
第二组
李显良
虚拟存储器是由系统自动提供的,它也是一种有限的存储器资源,作为程序员需要深入了解原理才能写高效果程序
内存碎片:字节对齐
外部碎片: 没有满足的空闲块
free怎么知道块有多大: block size,返回有效负载p0, 一个word记录size和a(1;分配,0:free)4字节(64)
第六组
之昂
图a展示了当页面命中时,CPU硬件执行的步骤。
Corei7地址翻译过程
从CPU产生虚拟地址的时刻一直到来自内存的数据字到达CPU。Core i7采用四级页表层次结构。每个进程有它自己私有的页 表层次结构。当一个Linux进程在运行时,虽然Core i7体系结构允许页表换进换出,但是与已分配了的页相关联的页表都是驻留在内存中的。CR3控制寄存器指向第一级页表 (L1)的起始位置。CR3的值是每个进程上下文的- -部分,每次上下文切换时,CR3的值都会被恢复。
Linux虚拟内存系统
Linux为每个人进程维护了一个单独的虚拟地址空间。内核虚拟内存包含内存中的代码和数据结构。内核虚拟内存的某些区域映射到所有进程共享的物理页面。每个进程共享内核的代码和全局数据结构。有趣的是,Linux也将一组连续的虚拟页面(大小等于系统中DRAM的总量)映射到相应的一-组连续的物理页面。这就为内核提供了一种便利的方法来访问物理内存中任何特定的位置,例如,当它需要访问页表,或在一些设备,上执行内存映射的I/O操作,而这些设备被映射到特定的物理内存位置时。
第六组
吴斌
虚拟内存管理,需要一个类似索引或者叫目录的实体,这个实体叫就是页表(page table)
每个进程都有一个独立的虚拟地址空间,每个进程也都有自己的页表
目前的Linux(2.6) 页表如果采用单层,页表数据量会非常大,也会显著降低页表查询的效率,一般实现上是三级或者四级页表 具体参加下图
进程的虚拟地址空间分为用户区和内核区,以32位系统为例,0-3G虚拟地址空间为用户去,而3-4G空间为内核区,更加详细的可以参见下图
第三组
wyhqaq
地址翻译
页面命中时:
页面未命中
1-3 与命中时一样
内存映射
1)Linux文件系统中的普通文件
2)匿名文件
在任何时刻,交换空间都限制着当前运行的进程能够分配的虚拟页面的总数。
写时复制:保护私有对象被两个进程同时映射的读写一致问题。
fork函数:将两个进程中的每个区域结构都标记为私有的写时复制。
execve函数:删除原有的虚拟地址的用户部分,创建新的区域结构
mmap,munmap函数:创建固定大小的虚拟内存区域
动态内存分配
动态内存分配器,将堆作为大小不同的块集合来维护。每个块要么是已分配的,要么是空闲的。
分配器核心目标:1.最大化吞吐率 2.最大化内存利用率
垃圾收集
Mark&Sweep收集器:由标记和清楚阶段组成
第五组
孙恒
现在系统通过将虚拟内存片和磁盘上的文件片关联起来,来初始化虚拟内存片,这个过程称为内存映射。内存映射为共享数据、创建新的进程以及加载程序提供了一种高效的机制。应用可以使用mmap函数来手动地创建和删除虚拟地址空间的区域。然而,大多数程序依赖于动态内存分配器,例如malloc,它管理虚拟地址空间区域内一个称为堆的区域。分配器有两种类型。显示分配器要求应用显式地释放它们的内存块,隐式分配器(垃圾收集器)自动释放任何未使用的和不可达的块
虚拟内存不只是“用磁盘空间来扩展物理内存”的意思,把内存扩展到磁盘只是使用虚拟内存技术的一个结果,它的作用也可以通过覆盖或者把处于不活动状态的程序以及它们的数据全部交换到磁盘上等方式来实现。对虚拟内存的定义是基于对地址空间的重定义的,即把地址空间定义为“连续的虚拟内存地址”,以借此“欺骗”程序,使它们以为自己正在使用一大块的“连续”地址
Linux通过将一个虚拟内存区域与一个磁盘上的对象(object)关联起来,已初始化这个虚拟内存区域的内容,这个过程称为内存映射。虚拟内存区域可以映射到两种 类型的对象中的一种:
Linux 文件系统中的普通文件: 一个区域可以映射到一个普通磁盘文件的连续部分,例如一个可执行目标文件。
文件区(section)被分成页大小的片,每一片包含一个虚拟页面的初始内容。如果区域比文件区要大,那么就用零来填充这个区域的余下部分。
匿名文件:一个区域也可以映射到一个匿名文件,匿名文件是由内核创建的,包含的全是二进制零。
在任何时刻,交换空间都限制着当前运行着的进程能够分配的虚拟页面的总数
只要没有进程试图写自己的私有区域,它们就可以在继续共享物理内存中所存储的共享对象的一个单独副本。然而,只要有一个进程试图写私有区域内的某个页面,那么这个写操作就会触发一个保护故障(Protection failure). 当故障处理程序注意到保护异常是由于进程试图写私有的写时复制区域中的一个页面而引起的,它就会在物理内存中创建这个页面的一个新副本,更新页表条目指向这个新的副本,然后恢复这个页面的可写权限。当故障处理程序返回时,CPU重新执行这个写操作,现在在新创建的页面上这个写操作就可以正确执行了。
间接引用坏指针,读取未初始化的内存,允许栈缓冲区溢出,假设指针和它们指向的对象大小相同,引用指针而不是它所指向的对象,误解指针运算,引用不存在的变量,以及引起内存泄漏
指针的算术操作是以它们指向的对象的大小为单位来进行计算的。这个单位的大小并不一定是字节。例如,下面函数的目的是扫描一个int的数组,并返回一个指针。指向val首次出现的位置。
垃圾回收器将内存视为一张有向可达图(reachability graph)