Skip to content

Commit

Permalink
finish 10.6
Browse files Browse the repository at this point in the history
  • Loading branch information
liuchanglin committed Feb 19, 2020
1 parent 467f2f1 commit 5062482
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 4 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -392,11 +392,11 @@ LRU策略通常作为一种分页替换算法,被认为是合格的。主要
$$
S = Σsi
$$
那么,如果总的可用帧数为m,我们将ai个帧分配给了进程pi,则ai近似为
那么,如果总的可用帧数为m,我们将a<sub>i</sub>个帧分配给了进程p<sub>i</sub>,则a<sub>i</sub>近似为
$$
ai = si/S × m.
$$
当然,我们必须将每个ai调整为一个整数,并使计算结果能大于指令集需要的最小帧数,且不超过m。
当然,我们必须将每个a<sub>i</sub>调整为一个整数,并使计算结果能大于指令集需要的最小帧数,且不超过m。

使用按比例分配,我们将62个帧划分到两个进程中,(一个10个分页,另一个127个分页),各自分到4个帧,和57个帧:

Expand Down Expand Up @@ -451,6 +451,92 @@ $$
#### 10.5.4 Non-Uniform Memory Access
到目前为止,我们对虚拟内存的讨论中,假设创建的主存都相同,或是找访问的主存都相同。在非统一内存访问(NUMA)系统上有多个CPU
到目前为止对虚拟内存的讨论中,我们假设创建的主存都相同,或至少访问的主存都相同。在非统一内存访问(NUMA)系统上有多个CPU,情况不尽相同。在这类系统上,一个给定CPU访问主存中某些部分的速度要比访问其他部分的速度快。系统中CPU和内存的交互方式导致了这些性能差异。这种系统由多个CPU组成,每个CPU有其本都内存(图10.19)。多个CPU通过同一个系统互连,且一个CPU访问本地内存要快于访问其他CPU的本地内存。NUMA系统并不比之访问一个主存的系统慢。然而正如1.3.2章节描述的,NUMA系统可以适应更多的CPU,因此能够提高更大的吞吐量和并行。
![10.19](./images/10.19.png)
![10.19](./images/10.19.png)
分页帧存储位置的管理对NUMA系统性能的影响很大。如果将内存视为统一的,则CPU因为内存访问等待的时间可能会使用了修改过的内存分配算法的NUMA系统长。5.5.4章节中描述了一些修改的算法,这类算法的目的是将分配的内存尽可能靠近进程运行所在的CPU。("近"的定义是"最小延迟")。因此,当一个进程发生了分页错误,感知NUMA的虚拟内存系统会给该进程分配一个尽可能靠近该进程所在的CPU的帧。
为了处理NUMA系统,调度器必须跟踪每个进程上一次运行的CPU。如果调度器尝试将每个进程调度到它们之前运行的CPU上,且虚拟内存系统尝试给进程分配靠近其所在的CPU的帧,这样会提升缓存命中率并降低内存访问时间。
如果增加了线程会变得更加复杂。例如,一个包含很多线程的进程可能最终会被调度到不同的系统板上。这种过去看下如何分配内存呢?
正如5.7.1章节讨论的,Linux通过内核标识的调度域的层次结构来管理这种情况。Linux CFS调度器不允许跨域迁移线程(如果允许会导致内存访问惩罚)。Linux为每个NUMA节点划分了独立的空闲帧列表,因此保证一个线程会从其运行的节点分配到内存。Solaris通过在内核中创建类似lgroups(地区团体)的方式解决这种问题,并且该组中的每个CPU都可以在定义的时间间隔内访问该组中的任何内存。此外,根据各组之间的延迟量,有一个类似Linux调度域的lgroup的层次结构。Solaris尝试调度一个进程中的所有线程并分配同一个lgroup中的内存。如果不可行,它会选择附近的lgroup来获取所需的其余资源。这种方法减小了整体的内存延迟并最大化CPU缓存命中率。
#### 10.6 Thrashing
考虑如果一个进程没有足够的帧时(即没有支持进程的工作集的最小帧数)会发生什么?此时进程会立即产生分页错误,此时必须替换一些分页。然而,由于它的所有分页都在使用中,必须替换掉将再次需要的页面,结果,该进程很快会再次产生分页错误,并一直产生分页错误,被替换掉的分页必须很快被加载会内存。
这种高频率的分页活动称为抖动,如果一个进程花费在分页上的时间大于其执行的时间,则该进程正在抖动。抖动会导致严重的性能问题。
#### 10.6.1 Cause of Thrashing
考虑以下基于早期分页系统的行为,操作系统会监控CPU的利用率,如果CPU利用率过低,则会通过引入新的进程来增加多道程序的级别。如果使用了全局分页替换:它会替换分页而不考虑该分页所属的进程。假设一个进程进入一个新的程序执行阶段,该阶段需要更多的帧。该进程会产生分页错误并从其它进程获取帧。由于这些进程也需要被替换掉的分页,因此也会产生分页错误,并从其他进程获取帧,它们会使用分页设备执行换入和换出。由于这些进程会排队等待分页设备的响应,进而导致CPU的ready队列为空,此时CPU利用率会降低。
当CPU调度器检测到CPU利用率下降后会提高多道程序的级别,新引入的进程会从运行的进程中获取帧,进而导致更严重的分页错误以及(分页设备中的)更长的队列。最终,CPU利用率还会下降,CPU调度器会尝试提高更多的多道程序级别。此时便发生了抖动,系统吞吐量会大幅下降,分页错误率大幅提高。结果,提高了有效内存访问时间。由于进程将所有时间花费在了分页上,因而无法正常工作,
图10.20描绘了这种CPU利用率相对多道程序级别的变化现象。当多道程序级别增加时,CPU利用率也会增加,直到到达一个最大值。如果继续增加多道程序的级别,会引入抖动,此时CPU利用率会骤降。此时,为了提升CPU利用率并停止抖动,我们需要降低多道程序的级别。
我们可以使用本地替换算法(或优先级替换算法)来限制抖动造成的影响。正如之前提到的,本地替换需要每个进程从它自己分配到的帧中获取需要的帧。因此,当一个进程开始抖动时,它不能从其他进程中获取帧,也不会导致更多的抖动。然而,问题并没有完全解决。如果一个进程正在抖动,它会将大部分时间用来在队列中等待分页设备。因为分页设备的平均队列更长,分页错误的平均处理时间也会增加。这样也增加了不会发生抖动的进程的有效访问时间。
![10.20](./images/10.20.png)
为了防止抖动,我们必须给一个进程提供其需要的帧。但我们怎么直到一个进程需要多少帧呢?一种策略是查看一个进程使用了到底多少帧,这种方法定义了进程执行的局部模型。
局部模型声明为,当一个进程执行时,分页会从一个局部移动到另一个局部。一个局部表示一起使用的分页。一个运行的程序通常会包含多个不同的局部,各个局部可能会重叠。例如,当调用一个函数时,它会定义一个局部,在这个局部中,引用的内存用于函数调用中的指令,本地变量,以及全局变量子集等。当退出函数时,由于不会再使用到本地变量以及函数指令,因此会离开这个局部,但后续可以再次进入。
图10.21描绘了局部的概念以及一个进程的局部随时间的变化。在时间(a)时,局部的分页为{18, 19, 20, 21, 22, 23, 24, 29, 30, 33},而在时间(b)时变为了{18, 19, 20, 24, 25, 26, 27, 28, 29, 31, 32, 33},注意分页是有交叉的,例如分页18,19,20都存在两个局部中。
![10.21](./images/10.21.png)
因此可以看到局部由程序结构和数据结构所定义。局部性模型指出所有程序都将展示除这种基本的内存引用结构。注意到目前为止,本书对缓存的讨论并不涉及局部模型。如果访问数据是随机的,没有任何规则,则无需使用缓存。
假设我们给一个进程分配了足够的帧来满足其当前的局部,然后该进程会产生分页错误来从局部获取分页,直到所有的分页都被加载到内存中。然后在其再次变动局部前不会产生分页错误。如果没有为当前的局部分配足够的帧,由于无法将所有使用的分页加载到内存中,因此该进程会发生抖动,
#### 10.6.2 Working-Set Model
工作集模型基于局部性假设,这种模型使用一个参数Δ来定义工作集窗口,用于检查最近引用的Δ个分页。工作集等于最近引用的Δ个分页集(图10.22)。当一个分页正在被使用时,它会处于工作集中,如果不再使用该分页,则会在距离上一次被引用的Δ个时间单位后从工作集中移除,这样工作集近似为程序的局部。
![10.22](./images/10.22.png)
例如,图10.22中给出了一系列的内存引用,如果Δ=10个内存引用,那么t<sub>1</sub>时间的工作集为{1, 2, 5, 6, 7}。在时间t<sub>2</sub>时,工作集变为{3, 4}。
工作集的精确度取决于选择的Δ,如果Δ过小,该工作集可能不会包含整个局部;如果Δ过大,则可能包含多个局部。极端情况下,Δ取无限大,此时工作集等于进程执行中涉及到的分页集。
工作集最主要的属性是它的大小,如果为系统中的每个进程计算工作集大小WSS<sub>i</sub>,则:
$$
D = Σ WSSi,
$$
D为需要的总的帧数。每个进程都会使用其工作集中的分页。这样进程i会使用WSS<sub>i</sub>个帧。如果需要的总数大于可用的帧数(D>m),由于某些进程无法获得足够得内存,此时会发生抖动。
一旦选定了Δ,那么工作集模型就比较简单了。操作系统会监控每个进程的工作集,并给工作集分配等同于工作集大小的帧。如果还有额外的帧,则可以启动新的进程。如果工作集长度增加了,并超过总的可用帧,操作系统会选择挂起一个进程。该进程的分页会被交换出去,并将其拥有的帧重新分配给其他进程。被挂起的进程后续可以重启。
工作集策略可以防止抖动,并保持多道程序足够高的等级。通过这样来优化CPU利用率。工作集模型最大的难点是如何持续追踪工作集。工作集窗口是一个活动窗口,在每次内存访问时,会在窗口一端添加一个新的引用,并在另一端移除一个老的引用。如果一个分页在工作集窗口的任何位置被引用,则该分页会处于工作集中。
> 工作集和分页错误率
>
> 进程的工作集和分页错误率有直接关系。如图10.22所示,通常一个进程的工作集会由于数据的引用以及代码段从一个局部移动的另一个局部而随时间变化。假设有足够的内存来保存一个进程的工作集(即进程不会抖动),该进程的分页错误率随着时间在高峰和低谷间转变。如下图所示:
>
> ![Working-Set Model1](./images/Working-Set Model1.png)
>
> 当在一个新的局部开始按需分页时会出现分页错误率高峰。然而,一旦这个新局部的工作集被加载到内存中,此时分页错误率会下降。当进程移动到一个新的工作集时,分页错误率又会上升到高峰,而在新的工作集被加载到内存后优惠回到低谷。从一个高峰开始到另一个高分开始之间的时间跨度体现了工作集的切换。
我们可以使用一个固定间隔定时器中断和引用位来近似一个工作集模型。例如,假设Δ等于10,000个引用,且我们可以在每5,000个引用时触发定时器中断。当获取到定时器中断,我们会拷贝并清除每个分页上的引用位。这样,当发生一个分页错误时,就可以通过校验当前的引用位以及两个"位于内存"位来决定在先前的10,000到15,000个引用之间是否使用过该分页。如果使用过,则至少会将这些位的某一个置位。如果没有被使用,则会清除这些位。如果一个分页中至少有一个位置位,则认为该分页处于工作集中。
由于无法知道5000个引用的间隔在哪里,因此这种约定并不完全准确。我们可以通过增加历史位(history bits)和中断频率(如使用10个位以及1000个引用作为间隔)来减少不确定性。然而,处理这些更加频繁的中断的成本也会相应变高。
#### 10.6.3 Page-Fault Frequency
工作集模型是成功的,并且可以用于预分页(10.9.1章节),但使用该模型来控制抖动会显得比较笨拙,可以采用一种更直接的方法,分页错误频率(PFF)。
具体的问题是防止抖动,抖动时的分页错误率高。因此需要控制分页错误率。当该值过高时,我们会知道进程需要更多的帧,相反地,如果该值过低,则说明分配给进程过多的帧。我们可以确定期望的页面错误率的上限和下限(图10.23)。如果实际的分页错误率超过了上限,此时会将其他帧分配给该进程。如果分页错误率低于下限,则需要将这些帧从进程中移除。因此,我们可以直接测量并控制分页错误率来防止抖动。
![10.23](./images/10.23.png)
使用工作集测量可能需要换出一个进程。如果分页错误率上升到没有可用的空闲帧,此时必须选择一个进程将其交换到后端存储中。被释放的帧会分配给具有高分页错误率的进程。
#### 10.6.4 Current Practice
实际上,抖动和导致的交换对性能的影响很大。最好的实践是实现一个包含足够物理内存的计算机系统来防止抖动和交换。从智能手机到大型服务器都提供足够的内存来将所有的工作集同时保存在内存中(除非发生极端情况),保证了最佳的用户体验。
### 10.7 Memory Compression

0 comments on commit 5062482

Please sign in to comment.