|
@@ -2,7 +2,7 @@
|
|
|
|
|
|
**虚拟内存**是逻辑存在的内存,他的主要作用的简化内存管理。
|
|
|
|
|
|
-总的来说虚拟内存提供了一下几个功能
|
|
|
+总的来说虚拟内存提供了以下几个功能
|
|
|
|
|
|
1. **隔离进程**:物理内存通过虚拟地址空间访问,虚拟地址空间与进程一一对应。每个进程都认为自己拥有了整个物理内存,进程之间彼此隔离,一个进程中的代码无法更改正在由另一进程或操作系统使用的物理内存。
|
|
|
2. **提升物理内存利用率**:有了虚拟地址空间后,操作系统只需要将进程当前正在使用的部分数据或指令加载入物理内存。
|
|
@@ -83,11 +83,21 @@ TLB即为页表缓存、转址旁路缓存、快表等。有了 TLB 后,那么
|
|
|
|
|
|
## 在 4GB 物理内存的机器上,申请 8G 内存会怎么样?
|
|
|
|
|
|
+32 位操作系统和 64 位操作系统的虚拟地址空间大小是不同的,在 Linux 操作系统中,虚拟地址空间的内部又被分为**内核空间和用户空间**两部分,如下所示:
|
|
|
+
|
|
|
+![img](assets/3a6cb4e3f27241d3b09b4766bb0b1124.png)
|
|
|
+
|
|
|
- 在 32 位操作系统,因为进程理论上最大能申请 3 GB 大小的虚拟内存,所以直接申请 8G 内存,会申请失败。
|
|
|
-- 在 64位 位操作系统,因为进程理论上最大能申请 128 TB 大小的虚拟内存,即使物理内存只有 4GB,申请 8G 内存也是没问题,因为申请的内存是虚拟内存。如果这块虚拟内存被访问了,要看系统有没有 Swap 分区:
|
|
|
+- 在 64 位操作系统,因为进程理论上最大能申请 128 TB 大小的虚拟内存(没有被访问的前提下),即使物理内存只有 4GB,申请 8G 内存也是没问题,因为申请的内存是虚拟内存。如果这块虚拟内存被访问了,要看系统有没有 Swap 分区
|
|
|
- 如果没有 Swap 分区,因为物理空间不够,进程会被操作系统杀掉,原因是 OOM(内存溢出);
|
|
|
- 如果有 Swap 分区,即使物理内存只有 4GB,程序也能正常使用 8GB 的内存,进程可以正常运行;
|
|
|
|
|
|
+## swap
|
|
|
+
|
|
|
+这种,将内存数据换出磁盘,又从磁盘中恢复数据到内存的过程,就是 Swap 机制负责的。
|
|
|
+
|
|
|
+Swap 就是把一块磁盘空间或者本地文件,当成内存来使用,它包含换出和换入两个过程
|
|
|
+
|
|
|
## 进程和线程
|
|
|
|
|
|
* **进程(Process)** 是指计算机中正在运行的一个程序实例。操作系统的资源分配单位,进程是资源(包括内存、打开的文件等)分配的单位,
|
|
@@ -165,8 +175,8 @@ TLB即为页表缓存、转址旁路缓存、快表等。有了 TLB 后,那么
|
|
|
- 为了保证所有进程可以得到公平调度,CPU 时间被划分为一段段的时间片,这些时间片再被轮流分配给各个进程。这样,当某个进程的时间片耗尽了,进程就从运行状态变为就绪状态,系统从就绪队列选择另外一个进程运行;
|
|
|
- 进程在系统资源不足(比如内存不足)时,要等到资源满足后才可以运行,这个时候进程也会被挂起,并由系统调度其他进程运行;
|
|
|
- 当进程通过睡眠函数 sleep 这样的方法将自己主动挂起时,自然也会重新调度;
|
|
|
-- 当有优先级更高的进程运行时,为了保证高优先级进程的运行,当前进程会被挂起,由高优先级进程来运行;
|
|
|
-- 发生硬件中断时,CPU 上的进程会被中断挂起,转而执行内核中的中断服务程序;
|
|
|
+- 当有优先级更高的进程运行时,为了保证高优先级进程的运行,当前进程会被挂起,由高优先级进程来运行;软件中断
|
|
|
+- 发生硬件中断时,CPU 上的进程会被中断挂起,转而执行内核中的中断服务程序;硬件中断
|
|
|
|
|
|
### 线程
|
|
|
|
|
@@ -272,6 +282,10 @@ DMA(Direct Memory Access)是一种硬件实现的技术,能够直接将数
|
|
|
- 第一步,通过 DMA 将磁盘上的数据拷贝到内核缓冲区里;
|
|
|
- 第二步,缓冲区描述符和数据长度传到 socket 缓冲区,这样网卡的 SG-DMA 控制器就可以直接将内核缓存中的数据拷贝到网卡的缓冲区里,此过程不需要将数据从操作系统内核缓冲区拷贝到 socket 缓冲区中,这样就减少了一次数据拷贝;
|
|
|
|
|
|
+如果系统支持SG-DMA
|
|
|
+
|
|
|
+![img](assets/senfile-零拷贝.png)
|
|
|
+
|
|
|
## PageCache 有什么作用?
|
|
|
|
|
|
**零拷贝使用了 PageCache 技术**,**PageCache其实本质上就是一种磁盘高速缓存**,通过 DMA 把磁盘里的数据搬运到内存里,这样就可以用读内存替换读磁盘。
|
|
@@ -311,3 +325,254 @@ DMA(Direct Memory Access)是一种硬件实现的技术,能够直接将数
|
|
|
|
|
|
- 内核的 I/O 调度算法会缓存尽可能多的 I/O 请求在 PageCache 中,最后「**合并**」成一个更大的 I/O 请求再发给磁盘,这样做是为了减少磁盘的寻址操作;
|
|
|
- 内核也会「**预读**」后续的 I/O 请求放在 PageCache 中,一样是为了减少对磁盘的操作;
|
|
|
+
|
|
|
+## 什么是一致性哈希?
|
|
|
+
|
|
|
+### 如何处理分配请求?
|
|
|
+
|
|
|
+直接使用Kafka的轮询分配策略,使用权重对其进行轮询。但是加权轮询算法是无法应对「分布式系统(数据分片的系统)」的,因为分布式系统中,每个节点存储的数据是不同的。
|
|
|
+
|
|
|
+当我们想提高系统的容量,就会将数据水平切分到不同的节点来存储,也就是将数据分布到了不同的节点。比如**一个分布式 KV(key-valu) 缓存系统,某个 key 应该到哪个或者哪些节点上获得,应该是确定的**,不是说任意访问一个节点都可以得到缓存结果的。
|
|
|
+
|
|
|
+### 使用哈希算法有什么问题?
|
|
|
+
|
|
|
+主要有两个问题?
|
|
|
+
|
|
|
+1. 数据分布不均匀,高频词的点,该节点数据可能过大
|
|
|
+2. 扩容时会发生再哈希,导致数据迁移问题
|
|
|
+
|
|
|
+### 一致性哈希
|
|
|
+
|
|
|
+![img](assets/30c2c70721c12f9c140358fbdc5f2282.png)
|
|
|
+
|
|
|
+- 首先,对 key 进行哈希计算,确定此 key 在环上的位置;
|
|
|
+- 然后,从这个位置沿着顺时针方向走,遇到的第一节点就是存储 key 的节点。
|
|
|
+
|
|
|
+> 缺点:
|
|
|
+>
|
|
|
+> **一致性哈希算法虽然减少了数据迁移量,但是存在节点分布不均匀的问题**。
|
|
|
+
|
|
|
+![img](assets/d528bae6fcec2357ba2eb8f324ad9fd5.png)
|
|
|
+
|
|
|
+### 如何通过虚拟节点提高均衡度?
|
|
|
+
|
|
|
+想要解决上述问题,就需要采用虚拟节点
|
|
|
+
|
|
|
+具体做法是,**不再将真实节点映射到哈希环上,而是将虚拟节点映射到哈希环上,并将虚拟节点映射到实际节点,所以这里有「两层」映射关系。**
|
|
|
+
|
|
|
+比如对每个节点分别设置 3 个虚拟节点:
|
|
|
+
|
|
|
+- 对节点 A 加上编号来作为虚拟节点:A-01、A-02、A-03
|
|
|
+- 对节点 B 加上编号来作为虚拟节点:B-01、B-02、B-03
|
|
|
+- 对节点 C 加上编号来作为虚拟节点:C-01、C-02、C-03
|
|
|
+
|
|
|
+引入虚拟节点后,原本哈希环上只有 3 个节点的情况,就会变成有 9 个虚拟节点映射到哈希环上,哈希环上的节点数量多了 3 倍。
|
|
|
+
|
|
|
+![img](assets/dbb57b8d6071d011d05eeadd93269e13.png)
|
|
|
+
|
|
|
+## 内核优化
|
|
|
+
|
|
|
+* **文件描述符**,默认是1024,改到最大值65535
|
|
|
+
|
|
|
+## I/O多路复用
|
|
|
+
|
|
|
+### select/poll
|
|
|
+
|
|
|
+select 实现多路复用的方式是,将已连接的 Socket 都放到一个**文件描述符集合**,然后调用 select 函数将文件描述符集合**拷贝**到内核里,让内核来检查是否有网络事件产生,检查的方式很粗暴,就是通过**遍历**文件描述符集合的方式,当检查到有事件产生后,将此 Socket 标记为可读或可写, 接着再把整个文件描述符集合**拷贝**回用户态里,然后用户态还需要再通过**遍历**的方法找到可读或可写的 Socket,然后再对其处理。
|
|
|
+
|
|
|
+所以,对于 select 这种方式,需要进行 **2 次「遍历」文件描述符集合**,一次是在内核态里,一个次是在用户态里 ,而且还会发生 **2 次「拷贝」文件描述符集合**,先从用户空间传入内核空间,由内核修改后,再传出到用户空间中。
|
|
|
+
|
|
|
+select 使用固定长度的 BitsMap,表示文件描述符集合,而且所支持的文件描述符的个数是有限制的,在 Linux 系统中,由内核中的 FD_SETSIZE 限制, 默认最大值为 `1024`,只能监听 0~1023 的文件描述符。
|
|
|
+
|
|
|
+poll 不再用 BitsMap 来存储所关注的文件描述符,取而代之用动态数组,以链表形式来组织,突破了 select 的文件描述符个数限制,当然还会受到系统文件描述符限制。
|
|
|
+
|
|
|
+但是 poll 和 select 并没有太大的本质区别,**都是使用「线性结构」存储进程关注的 Socket 集合,因此都需要遍历文件描述符集合来找到可读或可写的 Socket,时间复杂度为 O(n),而且也需要在用户态与内核态之间拷贝文件描述符集合**,这种方式随着并发数上来,性能的损耗会呈指数级增长。
|
|
|
+
|
|
|
+### epoll
|
|
|
+
|
|
|
+epoll 通过两个方面,很好解决了 select/poll 的问题。
|
|
|
+
|
|
|
+*第一点*,epoll 在内核里使用**红黑树来跟踪进程所有待检测的文件描述字**,把需要监控的 socket 通过 `epoll_ctl()` 函数加入内核中的红黑树里,红黑树是个高效的数据结构,增删改一般时间复杂度是 `O(logn)`。而 select/poll 内核里没有类似 epoll 红黑树这种保存所有待检测的 socket 的数据结构,所以 select/poll 每次操作时都传入整个 socket 集合给内核,而 epoll 因为在内核维护了红黑树,可以保存所有待检测的 socket ,所以只需要传入一个待检测的 socket,减少了内核和用户空间大量的数据拷贝和内存分配。
|
|
|
+
|
|
|
+*第二点*, epoll 使用**事件驱动**的机制,内核里**维护了一个链表来记录就绪事件**,当某个 socket 有事件发生时,通过**回调函数**内核会将其加入到这个就绪事件列表中,当用户调用 `epoll_wait()` 函数时,只会返回有事件发生的文件描述符的个数,不需要像 select/poll 那样轮询扫描整个 socket 集合,大大提高了检测的效率。
|
|
|
+
|
|
|
+epoll 支持两种事件触发模式,分别是**边缘触发(\*edge-triggered,ET\*)\**和\**水平触发(\*level-triggered,LT\*)**。
|
|
|
+
|
|
|
+这两个术语还挺抽象的,其实它们的区别还是很好理解的。
|
|
|
+
|
|
|
+- 使用边缘触发模式时,当被监控的 Socket 描述符上有可读事件发生时,**服务器端只会从 epoll_wait 中苏醒一次**,即使进程没有调用 read 函数从内核读取数据,也依然只苏醒一次,因此我们程序要保证一次性将内核缓冲区的数据读取完;
|
|
|
+- 使用水平触发模式时,当被监控的 Socket 上有可读事件发生时,**服务器端不断地从 epoll_wait 中苏醒,直到内核缓冲区数据被 read 函数读完才结束**,目的是告诉我们有数据需要读取;
|
|
|
+
|
|
|
+> **select/poll 只有水平触发模式,epoll 默认的触发模式是水平触发,但是可以根据应用场景设置为边缘触发模式。**
|
|
|
+
|
|
|
+## 进程调度算法
|
|
|
+
|
|
|
+### 先来先服务调度算法
|
|
|
+
|
|
|
+最简单的一个调度算法,就是非抢占式的**先来先服务(\*First Come First Severd, FCFS\*)算法**了。
|
|
|
+
|
|
|
+![FCFS 调度算法](assets/24-先来先服务.jpg)
|
|
|
+
|
|
|
+### 最短作业优先调度算法
|
|
|
+
|
|
|
+**最短作业优先(\*Shortest Job First, SJF\*)调度算法**同样也是顾名思义,它会**优先选择运行时间最短的进程来运行**,这有助于提高系统的吞吐量。
|
|
|
+
|
|
|
+![SJF 调度算法](assets/25-最短作业优先算法.jpg)
|
|
|
+
|
|
|
+### 高响应比优先调度算法
|
|
|
+
|
|
|
+前面的「先来先服务调度算法」和「最短作业优先调度算法」都没有很好的权衡短作业和长作业。
|
|
|
+
|
|
|
+那么,**高响应比优先 (Highest Response Ratio Next, HRRN)调度算法**主要是权衡了短作业和长作业。
|
|
|
+
|
|
|
+**每次进行进程调度时,先计算「响应比优先级」,然后把「响应比优先级」最高的进程投入运行**,「响应比优先级」的计算公式:
|
|
|
+
|
|
|
+![img](assets/26-响应比公式.jpg)
|
|
|
+
|
|
|
+从上面的公式,可以发现:
|
|
|
+
|
|
|
+- 如果两个进程的「等待时间」相同时,「要求的服务时间」越短,「响应比」就越高,这样短作业的进程容易被选中运行;
|
|
|
+- 如果两个进程「要求的服务时间」相同时,「等待时间」越长,「响应比」就越高,这就兼顾到了长作业进程,因为进程的响应比可以随时间等待的增加而提高,当其等待时间足够长时,其响应比便可以升到很高,从而获得运行的机会;
|
|
|
+
|
|
|
+### 时间片轮转调度算法
|
|
|
+
|
|
|
+最古老、最简单、最公平且使用最广的算法就是**时间片轮转(\*Round Robin, RR\*)调度算法**。
|
|
|
+
|
|
|
+![RR 调度算法](assets/27-时间片轮询.jpg)
|
|
|
+
|
|
|
+**每个进程被分配一个时间段,称为时间片(\*Quantum\*),即允许该进程在该时间段中运行。**
|
|
|
+
|
|
|
+- 如果时间片用完,进程还在运行,那么将会把此进程从 CPU 释放出来,并把 CPU 分配另外一个进程;
|
|
|
+- 如果该进程在时间片结束前阻塞或结束,则 CPU 立即进行切换;
|
|
|
+
|
|
|
+另外,时间片的长度就是一个很关键的点:
|
|
|
+
|
|
|
+- 如果时间片设得太短会导致过多的进程上下文切换,降低了 CPU 效率;
|
|
|
+- 如果设得太长又可能引起对短作业进程的响应时间变长。将
|
|
|
+
|
|
|
+通常时间片设为 `20ms~50ms` 通常是一个比较合理的折中值。
|
|
|
+
|
|
|
+### 最高优先级调度算法
|
|
|
+
|
|
|
+前面的「时间片轮转算法」做了个假设,即让所有的进程同等重要,也不偏袒谁,大家的运行时间都一样。
|
|
|
+
|
|
|
+但是,对于多用户计算机系统就有不同的看法了,它们希望调度是有优先级的,即希望调度程序能**从就绪队列中选择最高优先级的进程进行运行,这称为最高优先级(Highest Priority First,HPF)调度算法**。
|
|
|
+
|
|
|
+进程的优先级可以分为,静态优先级或动态优先级:
|
|
|
+
|
|
|
+- 静态优先级:创建进程时候,就已经确定了优先级了,然后整个运行时间优先级都不会变化;
|
|
|
+- 动态优先级:根据进程的动态变化调整优先级,比如如果进程运行时间增加,则降低其优先级,如果进程等待时间(就绪队列的等待时间)增加,则升高其优先级,也就是**随着时间的推移增加等待进程的优先级**。
|
|
|
+
|
|
|
+该算法也有两种处理优先级高的方法,非抢占式和抢占式:
|
|
|
+
|
|
|
+- 非抢占式:当就绪队列中出现优先级高的进程,运行完当前进程,再选择优先级高的进程。
|
|
|
+- 抢占式:当就绪队列中出现优先级高的进程,当前进程挂起,调度优先级高的进程运行。
|
|
|
+
|
|
|
+但是依然有缺点,可能会导致低优先级的进程永远不会运行。
|
|
|
+
|
|
|
+### 多级反馈队列调度算法
|
|
|
+
|
|
|
+**多级反馈队列(Multilevel Feedback Queue)调度算法**是「时间片轮转算法」和「最高优先级算法」的综合和发展。
|
|
|
+
|
|
|
+顾名思义:
|
|
|
+
|
|
|
+- 「多级」表示有多个队列,每个队列优先级从高到低,同时优先级越高时间片越短。
|
|
|
+- 「反馈」表示如果有新的进程加入优先级高的队列时,立刻停止当前正在运行的进程,转而去运行优先级高的队列;
|
|
|
+
|
|
|
+![多级反馈队列](assets/28-多级队列.jpg)
|
|
|
+
|
|
|
+来看看,它是如何工作的:
|
|
|
+
|
|
|
+- 设置了多个队列,赋予每个队列不同的优先级,每个**队列优先级从高到低**,同时**优先级越高时间片越短**;
|
|
|
+- 新的进程会被放入到第一级队列的末尾,按先来先服务的原则排队等待被调度,如果在第一级队列规定的时间片没运行完成,则将其转入到第二级队列的末尾,以此类推,直至完成;
|
|
|
+- 当较高优先级的队列为空,才调度较低优先级的队列中的进程运行。如果进程运行时,有新进程进入较高优先级的队列,则停止当前运行的进程并将其移入到原队列末尾,接着让较高优先级的进程运行;
|
|
|
+
|
|
|
+可以发现,对于短作业可能可以在第一级队列很快被处理完。对于长作业,如果在第一级队列处理不完,可以移入下次队列等待被执行,虽然等待的时间变长了,但是运行时间也会更长了,所以该算法很好的**兼顾了长短作业,同时有较好的响应时间。**
|
|
|
+
|
|
|
+## 磁盘调度算法
|
|
|
+
|
|
|
+### 先来先服务
|
|
|
+
|
|
|
+先来先服务(*First-Come,First-Served,FCFS*),顾名思义,先到来的请求,先被服务。
|
|
|
+
|
|
|
+那按照这个序列的话:
|
|
|
+
|
|
|
+98,183,37,122,14,124,65,67
|
|
|
+
|
|
|
+那么,磁盘的写入顺序是从左到右,如下图:
|
|
|
+
|
|
|
+![先来先服务](assets/磁盘调度-先来先服务.png)
|
|
|
+
|
|
|
+先来先服务算法总共移动了 `640` 个磁道的距离,这么一看这种算法,比较简单粗暴,但是如果大量进程竞争使用磁盘,请求访问的磁道可能会很分散,那先来先服务算法在性能上就会显得很差,因为寻道时间过长。
|
|
|
+
|
|
|
+### 最短寻道时间优先
|
|
|
+
|
|
|
+最短寻道时间优先(*Shortest Seek First,SSF*)算法的工作方式是,优先选择从当前磁头位置所需寻道时间最短的请求,还是以这个序列为例子:
|
|
|
+
|
|
|
+98,183,37,122,14,124,65,67
|
|
|
+
|
|
|
+那么,那么根据距离磁头( 53 位置)最近的请求的算法,具体的请求则会是下列从左到右的顺序:
|
|
|
+
|
|
|
+65,67,37,14,98,122,124,183
|
|
|
+
|
|
|
+![最短寻道时间优先](assets/磁盘调度-最短寻道时间优先.png)
|
|
|
+
|
|
|
+磁头移动的总距离是 `236` 磁道,相比先来先服务性能提高了不少。
|
|
|
+
|
|
|
+但这个算法可能存在某些请求的**饥饿**,因为本次例子我们是静态的序列,看不出问题,假设是一个动态的请求,如果后续来的请求都是小于 183 磁道的,那么 183 磁道可能永远不会被响应,于是就产生了饥饿现象,这里**产生饥饿的原因是磁头在一小块区域来回移动**。
|
|
|
+
|
|
|
+### 扫描算法
|
|
|
+
|
|
|
+最短寻道时间优先算法会产生饥饿的原因在于:磁头有可能再一个小区域内来回得移动。
|
|
|
+
|
|
|
+为了防止这个问题,可以规定:**磁头在一个方向上移动,访问所有未完成的请求,直到磁头到达该方向上的最后的磁道,才调换方向,这就是扫描(\*Scan\*)算法**。
|
|
|
+
|
|
|
+这种算法也叫做电梯算法,比如电梯保持按一个方向移动,直到在那个方向上没有请求为止,然后改变方向。
|
|
|
+
|
|
|
+还是以这个序列为例子,磁头的初始位置是 53:
|
|
|
+
|
|
|
+98,183,37,122,14,124,65,67
|
|
|
+
|
|
|
+那么,假设扫描调度算先朝磁道号减少的方向移动,具体请求则会是下列从左到右的顺序:
|
|
|
+
|
|
|
+37,14,`0`,65,67,98,122,124,183
|
|
|
+
|
|
|
+![扫描算法](assets/磁盘调度-扫描算法.png)
|
|
|
+
|
|
|
+磁头先响应左边的请求,直到到达最左端( 0 磁道)后,才开始反向移动,响应右边的请求。
|
|
|
+
|
|
|
+扫描调度算法性能较好,不会产生饥饿现象,但是存在这样的问题,中间部分的磁道会比较占便宜,中间部分相比其他部分响应的频率会比较多,也就是说每个磁道的响应频率存在差异。
|
|
|
+
|
|
|
+### 循环扫描算法
|
|
|
+
|
|
|
+扫描算法使得每个磁道响应的频率存在差异,那么要优化这个问题的话,可以总是按相同的方向进行扫描,使得每个磁道的响应频率基本一致。
|
|
|
+
|
|
|
+循环扫描(*Circular Scan, CSCAN* )规定:只有磁头朝某个特定方向移动时,才处理磁道访问请求,而返回时直接快速移动至最靠边缘的磁道,也就是复位磁头,这个过程是很快的,并且**返回中途不处理任何请求**,该算法的特点,就是**磁道只响应一个方向上的请求**。
|
|
|
+
|
|
|
+还是以这个序列为例子,磁头的初始位置是 53:
|
|
|
+
|
|
|
+98,183,37,122,14,124,65,67
|
|
|
+
|
|
|
+那么,假设循环扫描调度算先朝磁道增加的方向移动,具体请求会是下列从左到右的顺序:
|
|
|
+
|
|
|
+65,67,98,122,124,183,`199`,`0`,14,37
|
|
|
+
|
|
|
+![循环扫描算法](assets/磁盘调度-C-SCAN算法.png)
|
|
|
+
|
|
|
+磁头先响应了右边的请求,直到碰到了最右端的磁道 199,就立即回到磁盘的开始处(磁道 0),但这个返回的途中是不响应任何请求的,直到到达最开始的磁道后,才继续顺序响应右边的请求。
|
|
|
+
|
|
|
+循环扫描算法相比于扫描算法,对于各个位置磁道响应频率相对比较平均。
|
|
|
+
|
|
|
+### LOOK 与 C-LOOK算法
|
|
|
+
|
|
|
+我们前面说到的扫描算法和循环扫描算法,都是磁头移动到磁盘「最始端或最末端」才开始调换方向。
|
|
|
+
|
|
|
+那这其实是可以优化的,优化的思路就是**磁头在移动到「最远的请求」位置,然后立即反向移动。**
|
|
|
+
|
|
|
+那针对 SCAN 算法的优化则叫 LOOK 算法,它的工作方式,磁头在每个方向上仅仅移动到最远的请求位置,然后立即反向移动,而不需要移动到磁盘的最始端或最末端,**反向移动的途中会响应请求**。
|
|
|
+
|
|
|
+![LOOK 算法](assets/磁盘调度-LOOK算法.png)
|
|
|
+
|
|
|
+而针 C-SCAN 算法的优化则叫 C-LOOK,它的工作方式,磁头在每个方向上仅仅移动到最远的请求位置,然后立即反向移动,而不需要移动到磁盘的最始端或最末端,**反向移动的途中不会响应请求**。
|
|
|
+
|
|
|
+![C-LOOK 算法](assets/磁盘调度-C-LOOK算法.png)
|