IO管理和磁盘调度

IO管理和磁盘调度

一、IO功能的组织

​ 程序控制IO:处理器代表一个进程给IO模块发送一个命令,进程进入忙等待,直到操作完成

​ 中断控制IO:处理器代表一个进程给IO模块发送一个命令,若IO是非阻塞的,处理器继续执行进程,若是阻塞的,进程被置为阻塞态。

​ 直接内存访问(DMA):一个DMA模块控制内存和IO模块之间的数据交换。处理器给DMA发请求,且只有在整个数据块传输完毕后,它才被中断。

​ 在IO设备发展的历程中,cpu逐步从IO任务中解脱,提高了性能。现在IO模块有自己的处理器和局部存储器,本质上就是一个计算机,受cpu调度。

1.直接内存访问(DMA)

​ DMA单元能模拟cpu,且实际上能像cpu一样获得系统总线的控制权。

​ 工作流程:1.处理器通过读写控制线发送读或写信号

​ 2.通过数据线传输相关IO设备地址、读写的起始地址(存在地址寄存器里)、读写长度(存在数据计数寄存器里)

​ 3.处理器进行其他工作,DMA模块和IO设备进行交互,数据传输结束后,DMA给处理器发送中断信号。

​ 使用DMA,只有数据传送开始和结束的时候才需要cpu

​ ![屏幕截图 2023-12-14 193320](/../../images/屏幕截图 2023-12-14 193320.png)

DMA配置方式

​ ![屏幕截图 2023-12-14 193413](/../../images/屏幕截图 2023-12-14 193413.png)

二、IO缓冲

​ 缓冲 IO 也被成为标准 IO,大多数的文件系统系统默认都是以缓冲 IO 的方式来工作的。在Linux的缓冲I/O机制中,数据先从磁盘复制到内核空间的缓冲区,然后从内核空间缓冲区复制到应用程序的地址空间。

接下来我们看看缓冲 IO 下读写操作是如何进行?

  • 读操作:

操作系统检查内核的缓冲区有没有需要的数据,如果已经缓冲了,那么就直接从缓冲中返回;否则从磁盘中读取到内核缓冲中,然后再复制到用户空间缓冲中。

  • 写操作:

将数据从用户空间复制到内核空间的缓冲中。这时对用户程序来说写操作就已经完成,至于什么时候再写到磁盘中由操作系统决定,除非显示地调用了sync同步命令。

缓冲I/O的优点:

  1. 在一定程度上分离了内核空间和用户空间,保护系统本身的运行安全;
  2. 因为内核中有缓冲,可以减少读盘的次数,从而提高性能。
  3. 可以平滑IO需求的峰值

缓冲I/O的缺点:

​ 在缓冲 I/O 机制中,DMA 方式可以将数据直接从磁盘读到内核空间页缓冲中,或者将数据从内核空间页缓冲直接写回到磁盘上,而不能直接在用户地址空间和磁盘之间进行数据传输,这样数据在传输过程中需要在应用程序地址空间(用户空间)和内核缓冲(内核空间)之间进行多次数据拷贝操作,这些数据拷贝操作所带来的CPU以及内存开销是非常大的。

三、磁盘调度

1. FCFS 调度(先来先服务)

磁盘调度的最简单形式当然是先来先服务(FCFS)算法。虽然这种算法比较公平,但是它通常并不提供最快的服务。

例如,考虑一个磁盘队列,其 I/O 请求块的柱面的顺序如下:
98,183,37,122,14,124,65,67

​ 如果磁头开始位于柱面 53,那么它首先从 53 移到 98,接着再到 183、37、122、14、124、65,最后到 67,磁头移动柱面的总数为 640。这种调度如图 1 所示。
在这里插入图片描述
​ 从 122 到 14 再到 124 的大摆动说明了这种调度的问题。如果对柱面 37 和 14 的请求一起处理,不管是在 122 和 124 之前或之后,总的磁头移动会大大减少,并且性能也会因此得以改善。

2.SSTF调度(最短寻道时间优先)

​ 在移动磁头到别处以便处理其他请求之前,处理靠近当前磁头位置的所有请求可能较为合理。这个假设是最短寻道时间优先(SSTF)算法的基础。

​ SSTF 算法选择处理距离当前磁头位置的最短寻道时间的请求。换句话说,SSTF 选择最接近磁头位置的待处理请求。

对于上面请求队列的示例,与开始磁头位置(53)的最近请求位于柱面 65。一旦位于柱面 65,下个最近请求位于柱面 67。从那里,由于柱面 37 比 98 还要近,所以下次处理 37。如此,会处理位于柱面 14 的请求,接着 98,122,124,最后183(图 2)。
在这里插入图片描述

​ 这种调度算法的磁头移动只有 236 个柱面,约为 FCFS 调度算法的磁头移动总数的三分之一多一点。显然,这种算法大大提高了性能。

​ SSTF 调度本质上是一种最短作业优先(SJF)调度;与 SJF 调度一样,它可能会导致一些请求的饥饿。请记住,请求可能随时到达。假设在队列中有两个请求,分别针对柱面 14 和 186,而当处理来自 14 的请求时,另一个靠近 14 的请求来了,这个新的请求会下次处理,这样位于 186 的请求需要等待。当处理该请求时,另一个 14 附近的请求可能到达。

​ 理论上,相互接近的一些请求会连续不断地到达,这样位于 186 上的请求可能永远得不到服务。当等待处理请求队列较长时,这种情况就很可能出现了。

​ 虽然 SSTF 算法比 FCFS 算法有了相当改进,但是并非最优的。对于这个例子,还可以做得更好:移动磁头从 53 到 37(虽然 37 并不是最近的),再到 14,再到 65、67、98、122、124、183。这种策略的磁头移动的柱面总数为 208。

3. SCAN 调度(电梯算法)

​ 对于扫描算法,磁臂从磁盘的一端开始,向另一端移动;在移过每个柱面时,处理请求。当到达磁盘的另一端时,磁头移动方向反转,并继续处理。磁头连续来回扫描磁盘。SCAN 算法有时称为电梯算法,因为磁头的行为就像大楼里面的电梯,先处理所有向上的请求,然后再处理相反方向的请求。

​ 下面回到前面的例子来说明。在采用 SCAN 来调度柱面 98、183、37、122、14、124、65 和 67 的请求之前,除了磁头的当前位置,还需知道磁头的移动方向。

在这里插入图片描述

​ 假设磁头朝 0 移动并且磁头初始位置还是 53,磁头接下来处理 37,然后 14。在柱面 0 时,磁头会反转,移向磁盘的另一端,并处理柱面 65、67、98、122、124、183(图 3)上的请求。如果请求刚好在磁头前方加入队列,则它几乎马上就会得到服务;如果请求刚好在磁头后方加入队列,则它必须等待,直到磁头移到磁盘的另一端,反转方向,并返回。

​ 假设请求柱面的分布是均匀的,考虑当磁头移到磁盘一端并且反转方向时的请求密度。这时,紧靠磁头前方的请求相对较少,因为最近处理过这些柱面。磁盘另一端的请求密度却是最多。这些请求的等待时间也最长,那么为什么不先去那里?这就是下一个算法的想法。

4. C-SCAN 调度(循环扫描)

​ 对于扫描算法,磁臂从磁盘的一端开始,向另一端移动;在移过每个柱面时,处理请求。当到达磁盘的另一端时,磁头移动方向反转,并继续处理。磁头连续来回扫描磁盘。SCAN 算法有时称为电梯算法,因为磁头的行为就像大楼里面的电梯,先处理所有向上的请求,然后再处理相反方向的请求。

​ 下面回到前面的例子来说明。在采用 SCAN 来调度柱面 98、183、37、122、14、124、65 和 67 的请求之前,除了磁头的当前位置,还需知道磁头的移动方向。

在这里插入图片描述
C-SCAN 调度算法基本上将这些柱面作为一个环链,将最后柱面连到首个柱面。

5. LOOK 调度

​ 正如以上所述,SCAN 和 C-SCAN 在磁盘的整个宽度内移动磁臂。实际上,这两种算法通常都不是按这种方式实施的。更常见的是,磁臂只需移到一个方向的最远请求为止。

​ 遵循这种模式的 SCAN 算法和 C-SCAN 算法分别称为 LOOK 和 C-LOOK 调度,因为它们在向特定方向移动时查看是否会有请求(图 5)。
在这里插入图片描述

单处理器调度

单处理器调度

一、调度类型

​ 长程调度:决定是否把进程加入当前活跃的进程集中。创建进程时发生。

​ 中程调度:它是交换过程的一部分,它决定是否把进程添加到就绪队列或阻塞、挂起队列中

​ 短程调度:决定处理器执行哪个可运行的进程

​ IO调度:决定可用IO设备处理哪个进程的IO请求

二、短程调度

1.调度规则

​ 面向用户:与单个用户或进程感知到的系统行为有关。如交互式程序的响应时间,这是用户可以感受到的,面向用户调度力求降低响应时间。

​ 面向系统:重点是提高处理器的使用效果和效率

一些名词

​ 周转时间:一个进程从提交到完成的时间。包括实际执行时间和等待资源(处理器资源、IO资源等)时间

​ 归一化周转时间:周转时间与实际执行时间的比值

​ 响应时间:交互式进程从提交请求到得到相应所经过的时间

​ 吞吐量:单位时间内完成的进程数量

​ 处理器利用率:处理器处于忙状态的百分比

2.优先级

​ 许多系统中,每个进程被指定一个优先级,调度程序优先选择优先级高的进程调度。系统通常维护一组优先级队列,一个优先级维护一个队列。纯优先级调度会出现优先级低的进程饥饿的问题、

3.决策模式

​ 抢占:当前中运行的进程可能被操作系统中断,并转为就绪态。如出现时间中断时,需要进行抢占决策

​ 非抢占:一旦进程处于运行态,它会一直执行到终止。除非出现等待IO或系统调用而阻塞。

三、调度算法

1.先来先服务(FCFS)

​ 这种方法十分简单,每个进程就绪后加入就绪队列,系统从队列中依次取出进程执行。FCFS更适用于长进程。

​ FCFS更有利于处理器密集型进程。若一个IO密集进程因等待IO被阻塞,调度算法选择了一个处理器密集型进程来执行。在执行过程中,可能IO完成了,但IO密集进程只能等待。导致大多IO设备空闲。

2.轮转

​ 轮转算法是基于时钟的抢占策略。系统给每个进程分配一个时间片,时间片结束,进执行中的进程被抢占,按FCFS在就绪队列中选择一个进程来执行。

​ 时间片长度最好略大于一个经典交互的时间,但不能过大。

​ 这种方式在分时系统或事务处理系统中很有效。但由于IO密集型进程短时间使用处理器后便会阻塞,而处理器密集型进程可以用完整个时间片。所以这种算法对IO密集进程不公平,使得进程性能 降低。

虚拟轮转法

​ 为了优化上述问题,虚拟轮转法加入了一个辅助队列。由于等待IO而阻塞的进程阻塞结束后会加入辅助队列,进行调度时,辅助队列中的进程优先于就绪队列

3.最短进程优先(SPN)

​ 这是一个非抢占策略,它的原则是下次选择预计处理时间最短的进程。

​ 对短作业有利,对长作业不利。可能产生饥饿现象。另外,进程/作业的运行时间都是由用户提供的,并不一定真实,不一定能做到真正的短作业优先。

4.最短剩余时间(SRT)

​ 在SPN算法中增加了抢占机制。调度程序总是选择预期剩余时间最短的进程。

​ 每当有进程加入就绪队列改变时就需要调度,如果新到达的进程剩余时间比当前运行的进程剩余时间更短,则由新进程抢占处理器,当前运行进程重新回到就绪队列。另外,当一个进程完成时也需要调度。

​ 同样有长进程饥饿风险

5.最高响应比优先(HRRN)

​ 响应比即归一化周转时间

​ 响应比R = (w+s)/ s

​ w:等待处理器的时间 s:预计服务时间

​ 当前进程完成或阻塞时,选择响应比最大的进程。它相对于前两种算法,比较公平。因为它反映了进程的年龄。偏向短进程时,长进程由于得不到服务,等待时间一直增加,R变大,最终赢过短进程。

6.反馈法

​ 反馈法触发运行时间较长的进程。调度基于抢占原则并使用动态优先级机制。一个进程刚进入系统时,优先级最高,之后每被抢占一次,优先级下降一级。短进程会很快执行完毕,长进程会 多次降级。因此新进程和短进程会优于老进程和长进程。

优先级降到最低后不会再降,会重复返回到最低优先级队列。

​ 为了避免老进程和长进程饥饿,当一个进程在当前队列中等待服务的时间超过一定时间后,就把它的优先级提高一级

7.总结

![屏幕截图 2023-12-14 141059](/../../images/屏幕截图 2023-12-14 141059.png)

虚拟内存三

虚拟内存三

一、内存映射

​ Linux通过将一个虚拟内存区域和一个磁盘上的对象关联起来,以初始化这个虚拟内存区域的内容,这个过程叫内存映射。

1.映射对象

Linux文件系统中的普通文件:一个区域可以映射到一个普通磁盘文件的连续部分,如一个可执行文件。文件区被分成页大小的片,每一片初始化一个虚拟页的内容。(不发生数据传输)按需进行页面调度,文件并不进入物理内存中。直到cpu第一次引用到页面。(即发射一个虚拟地址,落在地址空间这个页面的范围之内)

匿名文件:一个区域页可以映射到一个匿名文件。匿名文件是由内核创建的,包含的全是二进制0。cpu第一次引用这样的页面时,内核在物理内存中找一个合适的牺牲页面,若它被修改过,就将它换出,并用二进制零覆盖并更新页表。注意磁盘和内存之间没有数据传输。因此,有时映射到匿名文件区域中的页面也叫请求二进制零的页

​ 无论哪种情况,一旦一个虚拟页面被初始化了,它就在内核维护的一个交换文件之间换来换去。

2.共享对象和私有对象

​ 一个对象可以被映射到虚拟内存的一个区域,要么作为共享对象,要么作为私有对象。如果一个进程将共享对象映射到它的虚拟地址空间,那它对该对象的写操作会反映到其他引用该对象的进程上和磁盘原始文件上。

​ 因为每个共享文件只有一个文件名,所以内核可以很清楚地知道这个文件是否被共享。所以即使对象被映射到了多个共享区域,物理内存中也只有共享对象的一个副本。

​ 对私有对象的写操作不会反映到其他引用该对象的进程,也不会反映到磁盘原始文件上。

​ 私有对象一开始的管理行为和共享文件相同,即使对象被映射到了多个私有区域,物理内存中也只有私有对象的一个副本。但它采用写时复制技术。即内存中的相应区域是只读的,当有进程对其进行写操作时,发生保护故障,异常处理程序发现这块内存是写时复制的,它会在物理内存中创建这个页面的一个副本,更新页表条目执行这个新的副本,然后恢复可写权限。避免了不必要的复制。

应用

​ 许多程序运行时需要访问相同的代码副本,例如标准C库,我们将这些共享库作为共享对象映射到进程虚拟地址空间中,内存中就只需要保留一份相关代码副本,节约空间。

3.用户级内存映射—-mmap

​ Linux进程可以使用mmap函数来创建新的虚拟内存区域,并将这些对象映射到这些区域中。

1
void* mmap(void* start,size_t length,int prot,int flags.int fd,off_t offset);

​ mmap要求内核创建一个新的虚拟内存区域,从地址start开始。并将fd指定的对象的一个连续的片映射到这个区域,片大小为length,从距文件偏移量offset字节开始。注意:start只是一个暗示,通常为NULL,由内核选定地址。

​ 参数prot:映射区域的保护方式。可以为以下几种方式的组合:

​ PROT_EXEC 映射区域可被执行
​ PROT_READ 映射区域可被读取
​ PROT_WRITE 映射区域可被写入
​ PROT_NONE 映射区域不能存取

​ 参数flags:影响映射区域的各种特性。在调用mmap()时必须要指定MAP_SHARED 或MAP_PRIVATE。

​ MAP_FIXED 如果参数start所指的地址无法成功建立映射时,则放弃映射,不对地址做修正。通常不鼓励用此旗标。
​ MAP_SHARED对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享。
​ MAP_PRIVATE 对映射区域的写入操作会产生一个映射文件的复制,即私人的“写入时复制”(copy on write)对此区域作的任何修改都不会写回原来的文件内容。
​ MAP_ANONYMOUS建立匿名映射。此时会忽略参数fd,不涉及文件,而且映射区域无法和其他进程共享。
​ MAP_DENYWRITE只允许对映射区域的写入操作,其他对文件直接写入的操作将会被拒绝。
​ MAP_LOCKED 将映射区域锁定住,这表示该区域不会被置换(swap)

4.mmap和普通文件读写的区别

​ 常规文件系统操作(调用read/fread等类函数)中,函数的调用过程:

​ 1、进程发起读文件请求。

​ 2、内核通过查找进程文件符表,定位到内核已打开文件集上的文件信息,从而找到此文件的inode。

​ 3、inode在address_space上查找要请求的文件页是否已经缓存在页缓存中。如果存在,则直接返回这片文件页的内容。

​ 4、如果不存在,则通过inode定位到文件磁盘地址,将数据从磁盘复制到页缓存。之后再次发起读页面过程,进而将页缓存中的数据发给用户进程。

​ 总结来说,常规文件操作为了提高读写效率和保护磁盘,使用了页缓存机制。这样造成读文件时需要先将文件页从磁盘拷贝到页缓存中,由于页缓存处在内核空间,不能被用户进程直接寻址,所以还需要将页缓存中数据页再次拷贝到内存对应的用户空间中。这样,通过了两次数据拷贝过程,才能完成进程对文件内容的获取任务。写操作也是一样,待写入的buffer在内核空间不能直接访问,必须要先拷贝至内核空间对应的主存,再写回磁盘中(延迟写回),也是需要两次数据拷贝。

​ 而使用mmap操作文件中,创建新的虚拟内存区域和建立文件磁盘地址和虚拟内存区域映射这两步,没有任何文件拷贝操作。而之后访问数据时发现内存中并无数据而发起的缺页异常过程,可以通过已经建立好的映射关系,只使用一次数据拷贝,就从磁盘中将数据传入内存的用户空间中,供进程使用。

​ 总而言之,常规文件操作需要从磁盘到页缓存再到用户主存的两次数据拷贝。而mmap操控文件,只需要从磁盘到用户主存的一次数据拷贝过程。说白了,mmap的关键点是实现了用户空间和内核空间的数据直接交互而省去了空间不同数据不通的繁琐过程。因此mmap效率更高。

5.内存映射的好处

​ 1、对文件的读取操作跨过了页缓存,减少了数据的拷贝次数,用内存读写取代I/O读写,提高了文件读取效率。

​ 2、实现了用户空间和内核空间的高效交互方式。两空间的各自修改操作可以直接反映在映射的区域内,从而被对方空间及时捕捉。

​ 3、提供进程间共享内存及相互通信的方式。不管是父子进程还是无亲缘关系的进程,都可以将自身用户空间映射到同一个文件或匿名映射到同一片区域。从而通过各自对映射区域的改动,达到进程间通信和进程间共享的目的。

​ 同时,如果进程A和进程B都映射了区域C,当A第一次读取C时通过缺页从磁盘复制文件页到内存中;但当B再读C的相同页面时,虽然也会产生缺页异常,但是不再需要从磁盘中复制文件过来,而可直接使用已经保存在内存中的文件数据。

​ 4、可用于实现高效的大规模数据传输。内存空间不足,是制约大数据操作的一个方面,解决方案往往是借助硬盘空间协助操作,补充内存的不足。但是进一步会造成大量的文件I/O操作,极大影响效率。这个问题可以通过mmap映射很好的解决。换句话说,但凡是需要用磁盘空间代替内存的时候,mmap都可以发挥其功效。

虚拟内存二

虚拟内存二

一、读取策略

​ 读取策略决定某页合适读入内存

1.请求分页

​ 请求分页是只有当访问到某页中的一个单元时才将页读入内存。进程开始时会出现大量缺页中断,随着读入页的增加,根据局部性原理,要访问的数据多数是最近读取的页,缺页中断率会减少到一个稳定水平。

2.预先分页

​ 预先分页是一次读取包含请求页的多个页。因为通常磁盘中一个进程的页是连续存储的,连续读取比单次读取效率高。但如果大多额外读取的页没被cpu使用,那就是低效的。

​ 通常进程启动时使用预先分页,读取一定的页。发生缺页中断时使用请求分页。

二、置换策略

​ 置换策略在决定置换的页集中,选择换出哪一页。所有策略的目标都是换出最近最不可能访问的页。

​ 页框锁定:内存中某些页框被锁定,无法置换。如内核页框

1.最佳(OPT)策略

​ OPT策略选择置换下次访问距离当前时间最长的那些页,这种算法造成的缺页中断最少,但由于它需要预知未来,因此不可实现。但它可以作为衡量标准。

2.最近最少使用(LRU)策略

​ LRU策略置换内存中最长时间未被引用的页。这种方法实现起来比较困难,开销大。

​ 一种是给每页添加一个最后一次访问的时间戳,每次访问的时候更新;另一个是维护访问页的栈。

3.先进先出(FIFO)策略

​ FIFO策略把分配给进程的页框视为一个循环缓冲区,并按循环的方式移动页。

​ 它使用一个指针,每次对页框进行读入或置换的时候指针都往下移动,当移动到边界时循环到开头。每次读入或置换页面都对指针所指的页框进行操作。

​ 它的隐含的逻辑是置换驻留在内存中时间最长的页。

​ 这个方法性能最差。

4.时钟(Clock)策略

​ Clock策略给每个页框增加一个称为使用位的附件位。每次cpu读取其中内容时,附加位置为1。对于用于置换的页框集,它被视为一个循环缓冲区,并有一个指针与其关联。需要置换一页时,操作系统扫描缓冲区,查找一个使用位为0的页框并置换。若途中遇到使用位为1的页框,则将其使用位置为0。若所有页框使用位都为1,则算法把所有使用位都置为0,并停留在初始位置上,置换该页框中的页。

​ 这个策略优于FIFO,差于LRU,若增加使用位的位数,可以提高效率。

三、页缓冲

​ 置换一个修改过的页,比置换未修改过的页的代价更大。因为修改过的页要写回给磁盘。

​ 为了提高效率,页缓冲算法不丢弃置换出的页。而是将它们分配到两个表中。

​ 若未被修改,则分配到空闲页链表中,若被修改,则分配到修改页链表中。注意:内存中的页不会物理移动,移动的是页表项。

​ 页缓冲技术在任何时候保留一小部分空闲页框。

​ 空闲页链表里包含一系列可以读入的页框。需要从虚存读入一页时,使用位于空闲页链表头部的页框,置换原本在那个位置的页。

​ 由于被置换的页仍然留在内存中,当进程再次访问时,效率很高。当修改队列达到一定长度时,集体写回修改页,它一次性写回多个页,减少了IO操作的次数,进而减少了磁盘访问的时间。

四、驻留集管理

​ 驻留集管理考虑两个问题:驻留集大小和置换范围。

驻留集大小

​ 驻留集大小即分配给进程的内存空间。分配的内存越少,驻留在内存中的进程越多,增加了操作系统至少找到一个就绪进程的可能性,减少了由于交换而消耗的处理器时间,但缺页率高。

​ 固定分配策略:为一个进程分配固定数量的页框。一旦发生缺页中断,该进程的一页就被它所需要的页面置换

​ 可变分配策略:运行分配给进程的页框不断变化。

置换范围

​ 局部置换:仅在发生缺页中断的进程的驻留集中选择页来置换。

​ 全局置换:把内存中所有未锁定的页都作为置换的候选页。

1.固定分配,局部范围置换

​ 缺点:一个进程的总页数分配过少时,产生很高缺页率。分配过多时,内存中进程太少,cpu把大量时间浪费在交换上。

2.可变分配,全局范围

​ 这种方式最容易实现。每个进程分配到一定页框,操作系统维护一个空闲页框列表。发生一次缺页中断时,一个空闲页框被添加进进程的驻留集,并读入该页。

​ 这种方式的难点在于如何选择置换页。引入页缓冲技术可以解决这个问题。

3.可变分配,局部范围

​ 这种方法的关键是不停地重新评估进程页框的分配情况,增加或减少分配该它的页框,以此来提高性能。

​ 比较常见方案的是工作集策略。但是它的开销比较大,一般使用近似算法替代。

​ 通过监视缺页率来评估进程的运行情况。若一个进程的缺页率低于某个最小阈值,则可以给进程分配一个较小的驻留集,但并不降低进程的性能(缺页率增加),使系统中其他进程受益;若进程的缺页率高于某个最高阈值,则在不降低整个系统性能的情况下,增大进程的驻留集。

​ 遵循该策略的一种算法是缺页中断频率算法

缺页中断频率算法

​ 某页被访问时,使用位置为1。发生一次缺页中断时,操作系统记录该进程从上次缺页中断到现在的虚拟时间,这通过维护一个页访问计数器实现。定义阈值F,若从上一次缺页中断到这次缺页中断时间小于F,则把该页加到进程的驻留集中;否则淘汰所有使用位为0的页,缩减驻留集大小。同时,把其余页的使用位重新置为0。

五、清除策略

​ 清除策略用于确定何时将已修改的一页写回辅存。

1.请求式清除

​ 只有当一页被选择用于置换的时候才被写回辅存。

​ 缺点:写回一页和读入一页是成对出现的,虽然这样可以减少写回操作。但发生缺页中断的进程在接触阻塞之前必须等待两次页传送(写回一次,读入一次),降低处理器利用率。

2.预约式请除

​ 将已经修改的多页在需要使用它们所占据的页框之前成批写回辅存。

​ 缺点:写回的页可能仍然在驻留集中,可能之后又被修改,会造成无意义的磁盘IO。

​ 一种较好的方式是结合使用页缓冲技术。只清除用于置换的页,但去除了清除和置换操作的成对关系,即异步写回。被置换的页可以放在两个表(队列)中:修改表和未修改表。修改表中的页周期性地成批写出,并移到未修改表中。未修改表中的一页要么因为被访问而被回收到驻留集,要么在其页框分配给另一页时被淘汰。

内存管理

内存管理

一、内存管理的需求

​ 内存管理的需求包括:重定位、逻辑组织、保护、物理组织、共享

​ 页框:内存中固定长度的块

​ 页:固定长度的数据块,存储在磁盘中。数据页可以临时复制到内存的页框中

​ 段:变长数据块,存储在磁盘中。可以将整个段临时复制到内存的一个可用分区中(分段),或将一个段分成许多页再载入内存(分页、分段结合)

重定位

​ 重定位就是把程序的逻辑地址空间变换成内存中的实际物理地址空间的过程。加载一个进程时,代码中相对内存访问被绝对内存访问地址替代,这个绝对地址由进程被加载到的基地址确定。

​ 重定位通过基址寄存器和界限寄存器实现。基址寄存器存储进程在内存中的起始地址,界限寄存器存储终止地址。由基址寄存器的地址加上相对地址产生绝对地址,即物理地址,然后和界限寄存器比较,如果超出界限就发出一份中断。

二、内存分区

1.固定分区

​ 固定分区将内存分为一系列大小固定的块。小于等于分区大小的进程都可以装入任何可用分区中。

​ 若程序太大不能放入一个分区,必须使用覆盖技术。即先将程序的一部分放入分区,当需要的模块不在内存中时,从外存中读入并覆盖当前分区。

缺点:产生大量内部碎片。内部碎片就是一个分区没用完,所剩下的内存。

放置算法:1.把每个进程分配到能容纳它的最小分区。此时每个分区要维护一个调度队列,用于保存从该分区换出的进程

​ 这方法现在基本没人用,太捞了

2.动态分区

​ 动态分区的分区长度和数量是可变动的。当进程装入内存,系统分配一块和它大小相等的内存区。

​ 这种方式没有内部碎片,但整个内存中会出现越来越多小的空洞,它们小而分散,无法被载入,形成了外部碎片。

​ 克服外部碎片的一种方式是压缩,即重新整理内存布局,将分散的碎片整合到一起,但它很耗时。

放置算法

​ 1.最佳适配:选择与大小要求最接近的块。性能最差,因为很耗时且会很快地形成很多外部碎片

​ 2.首次适配:从头扫描内存,选择大小足够的第一个。简单,最好,最快

​ 3.下次适配:从上一次放置的位置开始扫描,选择大小足够第一个。

三、分页

​ 分页就是将内存分为大小固定相等的小块,成为页框。每个进程也被分为同样大小的小块,成为页。进程中的页可以分配到内存中的页框中。

​ 这种方法只会产生少量内部碎片。

​ 此时为了能重定位,进程维护一个页表,页表给出了该进程每页对应页框的位置。如此,逻辑地址就包含一个页号和在该页中的偏移量。

​ 操作系统维护一个页框表,来保存可供使用的空闲页框。

​ 页和页框的大小必须是2的幂。

四、分段

​ 分段技术就是把程序和其相关数据划分到几个段中。段长可变。分段的逻辑地址由段号+偏移量组成。与动态分区不同的是,一个程序的不同段可以装入不同分区中,这样消除了内部碎片,由于进程被分成多个小块,外部碎片也很少。

虚拟内存一

虚拟内存一

一、什么是虚拟内存

​ 虚拟内存是对主存的抽象概念。它将主存看做是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要再主存和磁盘之间传输数据。

有了虚拟内存,进程不必将所有数据都装入主存,只需要将一部分活动的块装入内存即可。这加大了系统的并发度、

任何时候都在内存中的部分称为进程的常驻集。

​ 根据局部性原理,在任何一段很短的时间里,执行可能会限制在很小的一段程序中,仅可能访问少数数据数组。因此,虚拟内存将进程的一部分装入主存的做法是可行的。当cpu需要的数据不在主存中,会触发缺页中断,中断处理程序会将所需的页读入主存。

二、虚拟内存的实现

1.分页

​ 分页是虚拟内存实现的前提。磁盘上的虚拟地址空间同样被分成一系列和页框大小相同的页,进程在执行时将一部分页缓存到主存中。

​ 每个进程同样维护一个页表,页表项上需要有一位来表示它对应的页是否被装入主存。还需要一个控制位是修改位,来表示相应页的内容从上次装入内存到现在是否被修改。若被修改,则该页换出时需要将数据写回磁盘中。

地址转换

​ 虚拟地址的页号用于检索页表,查询相应页框号,并与虚拟地址的偏移量结合起来形成物理地址

​ ![屏幕截图 2023-12-13 144934](/../../images/屏幕截图 2023-12-13 144934.png)

转换检测缓冲区

​ 由于每次地址转换都会发生两次主存访问,一次读,一次取。所以大多虚存方案为页表提供了一个特殊的高速缓存,称为转换检测缓冲区(TLB)。给定一个虚拟地址,cpu先检查TLB,若所需的页表项在TLB中(TLB命中),则检索页框号并形成物理地址。否则访问主存,并将相应页表项载入TLB中。

​ ![屏幕截图 2023-12-13 145504](/../../images/屏幕截图 2023-12-13 145504.png)

多级页表

​ 由于每个进程都有一个页表,所以页表可能会很大,所以大多虚存系统在虚存中保存页表。一个进程在运行时,它的页表至少有一部分在主存中。为了减少从磁盘中读页表锁耗费的时间,通常组织多级页表,即在页表上组织页目录,每个页目录指向一个页表(可以理解为页表的稀疏索引)。

​ 这样一来可以将比较小的页目录装入主存中,来减少读磁盘的次数。

​ ![屏幕截图 2023-12-13 150105](/../../images/屏幕截图 2023-12-13 150105.png)

倒排页表

​ 上述页表的缺陷是,页表大小与虚拟地址空间大小成正比。倒排页表,顾名思义,它与常规页表相反,存储的是有关每个物理页框的信息,所以倒排页表项与物理内存页框有一一对应关系,它所包含的表项数量较少。

​ 在这种方法中,虚拟地址的页号部分使用一个简单的散列函数映射到散列表中。散列表包含一个指向倒排表的指针,而倒排表中含有页表项.通过这个结构,散列表和倒排表中各有一项对应于一个实存页.因此,不论有多少个进程、支持多少虚拟页,页表的大小都是固定的。

​ 地址转换时,虚拟地址的页号散列到倒排页表的页框号。然后通过配合进程位来确定这个页框中存的页是否是该进程的数据。通过链地址法来消除冲突。

​ ![屏幕截图 2023-12-13 152131](/../../images/屏幕截图 2023-12-13 152131.png)

二、分段

​ 分段允许程序员把内存视为由多个地址空间或段组成,段大小不等,内存访问以短号和偏移量构成

1.地址转换

​ ![屏幕截图 2023-12-13 152424](/../../images/屏幕截图 2023-12-13 152424.png)

2.优点

​ 1.简化了对不断增长的数据结构的处理

​ 2.允许程序独立地改变或重新编译,而不要求整个程序集重新链接或加载

​ 3.有助于进程间的共享

​ 4.有助于保护

三、段页式

​ 段页式系统中,用户地址空间被程序员划分成多个段,每段依次划分为多个页,页长度等于页框大小。使用一个寄存器记录该进程段表的起始地址。对于每个虚拟地址,处理器用段号部分来检索进程段表以寻找该段的页表。

​ ![屏幕截图 2023-12-13 152827](/../../images/屏幕截图 2023-12-13 152827.png)

线程一

线程一

一、什么是线程

​ 线程是进程轨迹的不同分支。进程拥有两个特点,一是资源所有权,即存放进程映像的虚拟地址空间;二是调度、分派的实体。线程将二者分离。我们称分派的单位称为线程,将资源所有权的单位成为进程。

二、多线程

​ 在多线程环境中,进程定义为资源分配单元和一个保护单元。

​ 一个进程中有一个或多个线程,每个线程拥有单独的栈和控制块。控制块中包含寄存器值、优先级和其他与线程相关的控制信息。进程中的线程共享该进程的状态和资源。

线程的优点

​ 1.同一进程内切换线程的时间要小于进程间切换的时间

​ 原因:此我们可以形象的认为线程是处在同一个屋檐下的,这里的屋檐就是虚拟地址空间,因此线程间切换无需虚拟地址空间的切换;而进程则不同,两个不同进程位于不同的屋檐下,即进程位于不同的虚拟地址空间,因此进程切换涉及到虚拟地址空间的切换,同时,线程切换需要保存的上下文内容要小于进程切换。这也是为什么进程切换要比线程切换慢

​ 2、线程间通信效率高

​ 原因:线程可以使用条件变量和锁来通信,这种通信都在同一进程的用户地址空间中,共享内存和文件。不需要内核介入。

三、线程分类

​ 线程根据调度者的不同,可分为三类:用户级线程、内核级线程、混合调度线程。

1.用户级线程

​ 用户级线程的管理工作都由应用程序完成,内核意识不到线程存在。当线程切换的时候,由线程库保存上下文,上下文包括:用户寄存器的内容、程序计数器、栈指针。

​ 当操作系统调度进程的时候,若进程阻塞,则其所有线程实际上是阻塞的。但在线程库看来,线程是运行态的。

优点

​ 1.线程切换不需要内核模式的特权,减少了两次状态转换(用户态->内核态,内核态->用户态),切换开销少

​ 2.可以为应用 程序量身定做调度算法,灵活度高

​ 3.跨平台性好,可以在任何操作系统中运行。

缺点

​ 1.当进程执行系统调用的时候,所有线程都会被阻塞。

​ 解决办法:使用套管技术。把一个阻塞的系统调用变成非阻塞的系统调用。如使用一个应用级的IO套管例程,它先检查IO设备是否忙,若忙,则阻塞当前线程,切换另一个线程。这个线程重新获得控制权后,套管例程会再次检查IO设备

​ 2.内核一次只能把一个进程分配给一个处理器。所以单个进程的线程无法做到并行执行。

2.内核级线程

​ 内核级线程所以管理工作均由操作系统完成。内核未进程和进程中的每个线程维护上下文信息。它克服了用户级线程的缺点,同时,内核例程也可以是多线程的。但用户级线程的优点变成了内核级线程的缺点。

四、一些面试题

1.线程和进程之前共享那些资源

​ 1.进程空间内开辟的,所以被共享

​ 2.全局变量。与某一函数无关,与特定线程无关

​ 3.静态变量。静态变量存放位置和全局变量一样,都存在于堆中开辟的.bss和.data段,是共享的

​ 4.其他一些共用资源,比如文件。

​ 同一进程的所有线程独享以下资源:

​ 1.栈。

​ 2.寄存器。

​ 3.程序计数器

线程运行的本质就是函数的执行,而函数的执行总会有一个源头,这个源头叫做入口函数,cup从入口函数开始一步一步向下执行,这个过程就叫做线程。由于函数运行时信息是保存在栈中的,比如返回值,参数,局部变量等等,所以栈是私有的。

​ cpu执行指令的信息会保存在寄存器中,这个寄存器叫做程序计数器。由于操作系统可以随时终止线程的运行,所以保存和恢复程序计数器的值就知道线程从哪里暂停的以及从哪里开始运行。

2.进程和线程的联系与区别

根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位

​ 资源开销:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。

​ 包含关系:如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。

​ 内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的

​ 影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。

​ 执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行

进程应用

进程应用

一、进程创建

1.获取进程id

1
2
pit_t getpid()	//当前进程id
pit_t getppid()//父进程id

2.创建和终止进程

1
pit_t fork() //创建子进程

​ 新创建的子进程与父进程几乎但不完全相同。子进程拥有父进程用户级虚拟地址空间的副本和全部打开的文件描述符。

fork函数调用一次,却返回两次。一次在父进程中返回子进程的PID,在子进程中返回0。

写时复制

​ fork创建出的子进程,与父进程共享内存空间。也就是说,如果子进程不对内存空间进行写入操作的话,内存空间中的数据并不会复制给子进程,这样创建子进程的速度就很快了!。在fork之后exec之前两个进程 用的是相同的物理空间(内存区),子进程的代码段、数据段、堆栈都是指向父进程的物理空间,也就是说,两者的虚拟空间不同,但其对应的 物理空间是同一个。当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间。这便是写时复制

​ 原理:fork()之后,kernel把父进程中所有的内存页的权限都设为read-only,然后子进程的地址空间指向父进程。当父子进程都只读内存时,相安无事。当其中某个进程写内存时,CPU硬件检测到内存页是read-only的,于是触发页异常中断(page-fault),陷入kernel的一个中断例程。中断例程中,kernel就会 把触发的异常的页复制一份,于是父子进程各自持有独立的一份。

​ 优点:

​ 1.可减少分配和复制大量资源时带来的瞬间延时

​ 2.可减少不必要的资源分配。比如fork进程时,并不是所有的页面都需要复制,父进程的代码段和只读数据段都不被允许修改,所以无需复制

4.回收子进程

​ 当一个进程由于某种原因终止时,内核并不立刻把它清除。而是让它保持在已终止的状态,知道父进程回收它。

​ 父进程回收时,内核将子进程的退出状态传给父进程,然后抛弃已终止的进程。

一个终止了但未回收的进程称为僵死进程

如果父进程终止了,子进程未回收,这样的子进程叫孤儿进程。内核会让init进程更为它的养父,并回收它们。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
			pid_t waitpid(pit_t pid,int* statusp,int options);
//pid:等待集合的成员。pid>0,等待集合是一个子进程,进程id就是pid
//pid = -1 ,等待集合是所有子进程
/*
status:这个参数将保存子进程的状态信息,有了这个信息父进程就可以了解子进程为什么会退出,是正常退出还是出了什么错误。

WIFEXITED(status) 如果子进程正常结束,它就返回真;否则返回假。
WEXITSTATUS(status) 如果WIFEXITED(status)为真,则可以用该宏取得子进程exit()返回的结束代码。
WIFSIGNALED(status) 如果子进程因为一个未捕获的信号而终止,它就返回真;否则返回假。
WTERMSIG(status) 如果WIFSIGNALED(status)为真,则可以用该宏获得导致子进程终止的信号代码。
WIFSTOPPED(status) 如果当前子进程被暂停了,则返回真;否则返回假。
WSTOPSIG(status) 如果WIFSTOPPED(status)为真,则可以使用该宏获得导致子进程暂停的信号代码。

options提供了一些另外的选项来控制waitpid()函数的行为。如果不想使用这些选项,则可以把这个参数设为0。
WNOHANG 如果pid指定的子进程没有结束,则waitpid()函数立即返回0,而不是阻塞在这个函数上等待;如果结束 了,则返回该子进程的进程号。
WUNTRACED 如果子进程进入暂停状态,则马上返回。
*/

5.加载并运行程序

1
int execve(const char* filename,const char* argv[],const char* envp[])

​ execve函数加载并运行可执行目标文件filename,且带参数列表argv和环境变量envp。

这个函数在当前进程的上下文中加载并运行一个新的程序,它会覆盖当前进程的地址空间,但并没有创建一个新进程。它同时继承了调用时已经打开的所有文件描述符。

进程二

进程二

一、进程控制

​ 进程控制是指对进程的创建、切换、中止等行为的管理和控制。

1.异常

​ 异常是允许操作系统内核提供进程概念的基本构造块。异常就是控制流中的突变,用来相应处理器状态的某些变化。

​ 处理器中的状态被编码为不同的位和信号,状态变化称为事件。当有事件发生时,处理器会通过异常表来间接过程调用异常处理程序。

​ 通常包含四种异常:中断、陷阱、故障、终止

中断

​ 中断是异步发生的,它是来自外部的信号产生的结果。如某个进程的IO操作完成了,IO设备会发送信号给处理器,处理器产生中断。

时钟中断:操作系统确定当前进程的执行时间是否超过分配的时间片,若超过则将进程切换到就绪态。这会引发时钟 断。

IO中断:操作系统确定是否发生IO活动

缺页中断:当处理器需要的数据不在内存中,而在虚存中时,操作系统必须把包含数据的内存块载入到内存中。这通过缺页中断的异常处理程序完成。

陷阱

​ 陷阱是同步发生的,它是有意的异常,是执行指令的结果。它的最重要的用途是在用户程序和内核之间提供一个像过程意义的接口,即系统调用。系统调用运行在内核模式中。

故障

​ 故障 由错误引起,它可能被故障处理程序修正。如果无法被修正,处理器就返回内核中的abort例程,这会终止引起故障的进程。

终止

​ 终止是不可恢复的致命错误造成的结果。当它发生时,进程会立刻被abort例程关闭。

2.执行模式

                 为了保护操作系统内部不被用户进程损坏,操作系统提供了模式机制来限制进程可执行的指令和能访问的地址空间。

​ 用户模式:正常情况下进程处于用户模式,此时它不允许执行特权指令。如IO操作,引用内核区的代码和数据等。用户模式的进程必须通过异常来进入内核模式。

​ 内核模式:内核模式中的进程可以执行指令集中的任何指令,访问系统中的任意位置。异常处理是在内核模式下完成的。

模式切换的过程

​ 先将程序计数器置为中断处理程序的开始地址,再从用户模式切换为内核模式,保存进程控制块中的进程状态信息,以便将来恢复进程的运行,之后就运行中断处理程序。

3.进程创建

何时创建一个进程

​ 从磁盘中载入一个批处理程序、终端用户登录到系统(如shell)、为提供服务而由操作系统创建(如打印服务)、从父进程派生。

如何创建

​ (1)为新进程分配一个唯一的标识符。

​ (2)为进程分配空间并初始化进程控制块

​ (3)设置正确的链接。如操作系统为每个调度队列维护一个链表,新进程必须放入就绪或就绪/挂起链表中

​ (4)创建或 扩充其他数据结构

4.进程切换

​ 1.保存处理器的上下文,包括程序计数器和其他寄存器

​ 2.更新当前运行进程的程序控制块,包括改变状态、退出运行态的原因和记账信息。

​ 3.把该进程的程序控制块移动到相应的队列(状态队列、等待事件队列)

​ 4.选择另一个进程执行

​ 5.当调度器又选择了这个进程运行,更新所选进程的进程控制块,包括改变状态

​ 6.更新内存管理数据结构。是否需要更新取决于管理地址转换的方式。

​ 7.载入程序计数器和其他计数器先前的值,将处理器上下文恢复为退出运行时的状态

二、进程间通信

一共有五种方式:管道,FIFO,消息队列,信号量,共享内存
管道:通常指无名管道
1)半双工的(数据只能在一个方向上流动),有固定的读端和写端
2)只能在具有亲缘关系的进程中通信(父子进程或兄弟进程)
FIFO:命名管道,也是一种文件类型
1)先进先出机制
2)与无名管道不同,可以在无关的进程中进行数据交互
3)与路径名相关,以一种特殊的文件形式存在于文件系统中
消息队列:
1)消息队列即消息的链接表,存放在内核中,一个消息队列有其对应的ID标识符
2)消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
3)消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
4)消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
信号量:
1)信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
2)信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。
3)每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。
4)支持信号量组。
共享内存:
1)共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
2)因为多个进程可以同时操作,所以需要进行同步。
3)信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。

进程一

进程一

一、什么是进程

​ 要说什么是进程,先要明白进程是用来解决什么矛盾的。当代计算机中最大的矛盾就是拥有极高运算速度的cpu和较慢速度的io操作的矛盾。为了尽可能地利用cpu资源,提出了多道程序设计的概念,即让多个任务在计算机中并发地运行。当某个任务进行耗时较长的io操作或运行一段时间后,切换另一个进程来运行,以此最大化地利用cpu。进程就是为了深刻描述程序动态执行过程的性质,乃至更好的支持和管理多道程序的并发执行

二、进程和进程描述

​ 进程的两个基本元素是程序代码和与代码相关的数据集,当系统开始执行程序代码,我们把这个执行实体称为进程。

​ 进程映像:用户数据、用户程序、栈、进程控制块组成一个进程映像,这在内存中描述了一个进程。

1.进程控制结构

​ 操作系统控制和管理进程时,首先需要知道进程的位置,其次要知道进程的属性。进程的位置保存在主进程表中,其中每一个表项至少包含一个指向进程映像的指针。属性保存在进程控制块中

进程控制块

​ 进程控制块包括进程标识信息。进程状态信息、进程控制信息三大部分。

​ ![屏幕截图 2023-12-09 102149](/../../images/屏幕截图 2023-12-09 102149.png)

2.操作系统的控制结构

​ 操作系统通过构建并维护每个资源实体的信息表来管理资源。它可以分为4类:内存表、文件表、io表、进程表。

​ ![屏幕截图 2023-12-09 102525](/../../images/屏幕截图 2023-12-09 102525.png)

​ 注意,内存、IO和文件是代表进程而被管理的,因此进程表中必须有对这些资源的直接或间接访问。同时其他的表中也可以使用进程标识符来交叉引用进程表。如内存表可以提供一个内存映射,来说明每个区域分配给了哪个进程等。

三、进程状态

​ ![屏幕截图 2023-12-09 102856](/../../images/屏幕截图 2023-12-09 102856.png)

​ 新建态:系统刚创建的进程,还未加入可执行组。通常是进程控制块结构已经创建,但未加载到内存中。

​ 就绪态:进程等待处理器调度,可以运行

​ 运行态:正在运行

​ 退出态:从可执行组中退出的进程。可能是进程执行完毕或者是它因某种原因被取消

​ 阻塞态:等待某个事件而暂停执行,如发生了IO操作,需要等到IO完成事件才能继续执行

​ 阻塞/挂起态:被置换到外存的阻塞进程

​ 就绪/挂机态:在外存中,但只要载入内存就可以执行

状态转换

​ 新建态—->就绪态:操作系统准备好再接纳一个进程时,将进程加入可执行组。

​ 新建态—->就绪/挂起态:内存不足,无法将空间分配给新进程,所以暂时先存在外存中,有空间了再载入内存。

​ 就绪/挂起态—->就绪态:若内存中没有就绪态进程,则操作系统就需要调入一个进程继续执行。此外,如果就绪/挂起态中的进程优先级高于所有就绪态进程时,这种转换也可以发生。

​ 阻塞—–>阻塞/挂起:若没有就绪进程,则至少换出一个阻塞进程,来为另一个未阻塞进程腾出空间。操作系统也会为了当前进程的性能,而将阻塞进程挂起,来腾出空间,让运行中的进程获得更好的性能。

​ 阻塞/挂机—->阻塞:这种转换很少见。但当阻塞/挂起队列中有一个进程的优先级比就绪/挂起队列中的任何进程都高,而且操作系统有理由相信等待事件很快会发生,这时会把阻塞进程调入内存。

​ 运行态—->就绪/挂起:当阻塞/挂起队列中优先级较高的进程不再阻塞后,操作系统会抢占当前进程,让优先级较高的进程执行。