一、导论
· 三个模块:
虚拟化 (虚拟化CPU、虚拟化内存)、并发、持久化
· 程序运行的流程:
1. CPU获取一条指令
2. 对其解码 (弄清楚这是哪条指令)
3. 执行它
完成这条指令之后,处理器继续执行下一条指令,直到程序最终完成;
· 虚拟化:
1. 操作系统将物理资源 (如CPU、内存、磁盘)转换为更通用、更强大且易于使用的虚拟形式;
2. 虚拟化可以让许多程序运行 (共享资源),操作系统又称资源管理器。
· 虚拟化CPU:
在单核计算机上,为什么可以同时运行多个程序?
操作系统将单个CPU (或其中的一小部分)转换成看似无限数量的CPU;
从而让许多程序看似是同时运行的,这就是所谓的虚拟化CPU
· 虚拟化内存:
内存:内存就是一个字节数组;要读取内存,必须指定一个地址,才能访问存储在那里的数据;
要写入或更新内存,还必须指定要写入给定地址的数据。
虚拟化内存:
每个进程访问自己的私有空间 (地址空间),操作系统以某种方式映射到机器的物理内存上;
一个正在运行的程序中的内存引用不会影响其他进程 (或操作系统本身)的地址空间。
· 并发:
多个线程共用一个内存空间,可能会导致运算结果不符合任何一个程序的预想;
原因:
每执行一条语句,实际上需要执行多条指令;
1. 将值从内存加载到寄存器中,
2. 对其进行运算,
3. 将值放回内存中;
因为这三条指令不是以原子方式 (一次性执行所有指令)执行的,所以奇怪的事情就会发生。
· 持久性:
操作系统不像对CPU、内存一样会给磁盘虚拟化,相反它假设用户会共享文件的信息。
文件系统的工作流程:
1. 首先确定新数据将驻留在磁盘的哪个位置;
2. 在文件系统所维护的各种结构中对齐进行记录;
这样做需要对底层存储设备发出I/O请求,以读取现有结构或更新(写入)它们.
· 操作系统的设计理念:
1. 操作系统取得CPU、内存或磁盘等物理资源,并对他们虚拟化;
它持久的存储文件,从而使它们长期安全。
2. 高性能:操作系统的目标是最小化操作系统的开销;
3. 安全性:能在应用程序之间提供保护,使它们不能随意地影响对方,让进程间彼此隔离是保护的关键。
· 操作系统的发展:
1. 早期的库:人为调用程序运行顺序;
2. 保护 (system call):限制了程序直接对硬件访问,只能通过OS间接访问;
在内核模式下,OS可以完全访问系统硬件。
3. 多道程序时代:在I/O进行和任务中断时,切换线程运行,提高CPU利用率;
内存保护、处理并发问题成为新的挑战。
二、虚拟化CPU(一)----进程
· 程序本本身是没有生命周期的,他只是存储在磁盘上的一些指令;
时操作系统让这些字节运行起来,让程序发挥作用。
· 什么是进程?
进程就是运行中的程序;
· 在少量CPU可用的情况下,怎么提供几乎有无数个CPU可用的现象?(虚拟化内存)
1. 时分共享CPU技术:
通过让一个进程只允许一个时间片,然后切换到其他进程,就能实现存在多个CPU的假象。
2. 低级机制+高级智能 (上下文切换、调度策略)
· 时分共享和空分共享:
通过 允许资源由一个线程使用一段时间,然后由另一个线程使用一段时间; (线程不是无时无刻都在计算的)
所谓的资源就能被许多人共享。时分共享对应的是空分共享,
资源在空间上被划分给希望使用它的人
· 进程的组成
构建进程需要理解它的 状态机,即程序在运行时可以读取和更新的内容;
1. 内存: 指令在内存中,需要读取和写入的数据也在内存中;
因此进程可以访问的内存 (称为地址空间)是该进程的一部分
2. 寄存器: 如 程序计数器PC告诉我们程序即将执行那个指令 (有时称为指令指针 IP);
栈指针和相关的帧指针用于管理函数参数栈、局部变量和返回地址
· 基础进程API:
1. 创建 (create): 创建进程的方法
2. 销毁 (destory): 强制销毁进程的接口,用于停止时空的进程
3. 等待 (wait): 等待进程停止运行
4. 其他控制 (miscellaneous control): 挂起、唤醒
5. 状态 (status): 查看进程运行状态信息
· 进程创建的过程:
1. 将代码和所有静态数据加载到内存中,加载到进程的地址空间中; (栈、堆、数据段、代码段)
2. 早期的OS中,是一次性加载到内存中;现代的OS,是惰性加载的,仅加载程序执行期间需要加载代码和数据片段;
3. 代码和静态数据加载到内存后,必须为程序的运行时栈 (run-time stack)分配一些内存;
用于存放局部变量、函数参数、返回地址; (函数栈帧)
4. 还会为程序的堆 (heap)分配一些内存,堆用于显示请求的动态分配数据;
通过malloc()和free() 分配和释放空间;数据结构需要存放到堆。
· 进程状态 (status)
1. 就绪 (ready): 程序已经准备好运行;
2. 运行 (running): 程序正在处理器上运行
3. 阻塞 (blocked):程序A因为某个条件没有实现,直到事件条件发生才会继续运行
· 数据结构
OS有一些关键数据用于跟踪程序的运行状态,用来确保中断进程之后的唤醒工作顺利;
上下文切换:
当一个进程停止,寄存器上下文将保存在寄存器中,
可以通过恢复这些寄存器 (将它们的值放回实际的寄存器),恢复运行该进程。
其他的进程状态:
1. 初始状态 (initial): 进程在创建时的状态
2. 最终状态 (final/僵尸进程):
已经退出,但是还没垃圾清理,这时候运行其他进程可以查看它的运行结果和是否执行成功;(成功为0,echo $0)
进程列表:
进程列表用于存储所有就绪的进程。
· 文章最后会介绍一些常见进程API和具体的实现方法;
三、虚拟化CPU(二)----机制
之前提到了虚拟化内存主要是由 低级机制+高级智能实现,这里将主要介绍上下文切换的相关知识;
· 如何高效、可控地虚拟化?
操作系统必须以高性能的方式虚拟化CPU,同时保持对系统的控制;
· 基本技巧:受限直接执行
· 什么是直接执行:
1. 直接在CPU上运行程序,当OS希望启动程序运行时,
2. 在进程列表为其创建一个进程条目,为其分配一些内存,
3. 将码加载到内存中,找到入口函数 (main)开始执行用户代码
问题:1. 如何确保程序不执行我们不希望他做的事呢?
2. 当运行一个程序时,应该如何将它停下来切换到另一个进程实现时分共享呢?
· 问题1:受限制的操作
1. 一个进程必须能够执行I/O和其他一些受限制的操作,
但又不能让进程完全控制系统;
2. 引入用户态和内核态的概念;
用户模式的进程 不能直接访问硬件;
内核模式的进程 可以做受限操作。
· 问题2:进程间的切换
1. 协作方式:等待系统调用来切换进程;
即,当进程执行了某种非法操作或者系统捕获到下一步需要切换进程,OS重新获取控制权完成进程调用;
这样会出现一个新的问题,当程序进入一个合法的无限循环中,系统就奔溃了;
2. 非协作方式:OS进行控制
利用时钟中断重新获取控制权;
心跳机制每几毫秒产生一次中断,中断时,进程停止,OS预先配置的中断处理程序运行,重新获取CPU控制权;
· 保存和恢复上下文
当OS重新获取了控制权就要决定下一步是切换进程还是继续运行,这是由调度策略决定的;
如果是切换进程,就必须要进行上下文切换的过程;
即,OS要将当前正在执行的程序保存一些寄存器的值,并为即将执行的进程恢复一些寄存器的值。
四、虚拟化CPU(三)----调度
· 工作负载假设
一个完全可操作的调度准则,我们对操作系统中运行的进程做出如下假设:
1. 每一个工作运行相同的时间
2. 所有工作同时到达
3. 一旦开始,每个工作保持运行到完成
4. 所有工作只使用CPU (没有I/O的操作)
5. 每个工作的运行时间是已知的
这些假设是不现实的,但是先抽象地做这些假设是必要的
· 调度指标
T(周转时间)=T(完成时间)-T(到达时间)
如果所有任务在同一时间到达,那么 T(周转时间) == T(完成时间)
然而周转时间是一个性能指标,除此之外,还有一个指标是公平;
性能和公平往往是矛盾的,比如:调度程序可以优化性能,但是代价是阻止一些程序运行
· 先进先出 (FIFO)
先进先出是最基本的方法,也就是先到先服务 (简单、易于实现)
假设三个线程A、B、C几乎轮流到达了CPU,运行时间都是10,那么A周转时间==10,B周转时间==20,C周转时间==30;
所以平均周转时间为 (10+20+30)/3==20
再假设A运行时间为100,B、C运行时间==10,那么A、B、C周转时间分别为100、110、120;
所示平均周转时间为 (100+110+120)/3==110
可以发现,先到先服务的调度策略虽然易于实现,但是再面对这种情况的时候会导致后续的程序周转时间过长;
这种问题被称为护航效应:一些耗时较少的潜在资源消费者被排在重量级资源消费者之后
· 最短任务优先 (SJF)
最短任务优先代表一个总体调度原则,可以应用于所有系统,其中平均客户周转时间很重要;
例如超市中的"零散购物"通道,以确保仅购买几件东西的购物者不会堵在大量购物的家庭后面。
按照这个方法,上面的例子中平均周转时间就会变为 (10+20+120)/3==50;
所以在所有工作同时到达的假设上,我们可以证明SJF确实是一个最优调度算法;
但是实际情况中,工作并不是同时到达的,而是随时到达的;
假设A运行时间还是100,B、C运行时间还是10;
B、C在A运行了10秒之后到达,即使B、C是在A不久后到达,它们仍然被迫等到A完成,从而遭遇护航问题;
平均周转时间:(100+110-10+120-10)/3==103.33秒
· 最短完成时间优先 (STCF)
为了解决上面SJF的问题,需要放宽假设条件 (工作必须保持运行直到结束);
当B、C到达时,调度程序可以做其他事:抢占A工作执行B工作,稍后再执行A工作;
STCF:每当新工作进入系统时,就会确定剩余工作和新工作中,谁的剩余时间最少,然后执行该工作;
平均周转时间:(120+10+20+)/3==50;
· 新的度量指标:响应时间
T(响应时间)=T(首次运行)-T(到达时间)
还是上面的例子,STCF虽然能获得很好的周转时间,但是对于响应时间和交互性处理不好;
· 轮转 (RR)
RR在一个时间片内运行一个工作,然后切换到运行队列中的下一个任务,而不是运行一个任务直到结束;
它反复执行直到所有任务都完成;时间片长度必须是时钟中断周期的倍数,如果时钟中断是每10ms中断一次,则时间片长度可以为10ms、20ms...
即,在每次 (或者多次)中断当前进程判断是否需要切换进程的时候,切换进程。
例如:A、B、C三个任务同时到达,每个任务都需要运行5s;
SJF的响应时间:(0+5+10)/3==5
时间片长度为1的RR的响应时间:(0+1+2)/3==1
可以发现时间片长度对于RR是很重要的,时间片越短,RR在响应时间上面表现越好;
但是上下文切换会浪费性能,所以时间片过短,会造成大量的上下文切换,浪费大量资源;
· RR和STCF
如果响应时间作为我们唯一的指标,那么带有合理的时间片的RR就是很好的调度程序;
但是来看看它的周转时间,RR为时间片长度为1的调度程序,A、B、C的运行时间都是5,平均周转时间为 (13+14+15)/3==14;
RR所做的是延展没饿过工作,只运行每个工作一段时间,就转向下一个工作,所以它在周转时间上甚至比FIFO更差;
一般来说,任何公平的政策,即在小规模的时间内将CPU均匀分配给活动进程,在周转时间这类指标表现不佳;
到现在,我们开发出了两套调度程序:
1. 优化周转时间 (STCF、SJF)
2. 优化响应时间 (RR)
我们还有两个假设需要放宽:作业是没有I/O的、每个作业的运行时间是已知的
· 结合I/O
调度程序需要在工作发起I/O请求时做决定,因为当前运行的作业在I/O期间不会使用CPU,他被阻塞等待I/O完成;
这时调度程序应该带CPU上安排另一项工作;调度程序还必须在I/O结束后做出决定;
假设A、B两个进程的运行时间都是50ms,都是A每运行10ms还需要执行I/O请求10ms;
STCF的总时间:50+40+50=140ms
重叠后:50+50=100ms (在A程序I/O的过程中,间断的执行B)
调度程序通过将每个CPU突发作为一项工作,调度程序确保"交互"的进程经常运行;
当这些交互式作业正在执行I/O时,其他CPU密集型作业运行,从而更好的利用处理器。
· 无法预知
前面所有的调度方法都是建立在已知每个任务的执行时间的情况下,但是实际情况并非如此;
我们需要建立一个没有这种先验知识的SJF/STCF,以及更进一步的结合RR构建一个响应时间也不错的调度程序。
五、调度(一)----多级反馈队列 (MLFQ)
多级反馈队列需要解决的问题:在没有工作长度的先验知识的情况下,设计出一个能同时减少响应时间和周转时间的调度程序;
MLFQ中有多个独立的队列,每个队列有不同的优先级,MLFQ总是优先执行较高优先级的工作(在较高级队列工作);
MLFQ没有给每个工作指定不变的优先顺序,而是根据观察到的行为调整他的优先级;
例如,A进程不断放弃CPU请求键盘的输入,就会认为它是交互性程序,给他较高的优先级;
B进程长时间占用CPU,MLFQ会降低它的优先级;
因此得到了以下规则:
规则1:如果A的优先级>B的优先级,运行A,不运行B
规则2:如果A优先级==B优先级,RR轮转运行A和B
· 如何改变优先级 (尝试一)
我们先假设每个任务启动时都更希望快速得到响应,所以有较高的权限,后续根据实际情况调整权限;
因此我们得到了以下规则:
规则3:工作进入系统时,放在最高优先级 (参与RR轮转)
规则4a:工作用完整个时间片之后降低优先级
规则4b:如果工作在其时间片内主动释放CPU,则优先级不变
· 实例 (尝试一)
1. 单个长任务
在最高权限层开始执行一个时间片,然后降低一个优先级,最终停留在某个优先级;
2. 又来了一个短工作 (近似SJF)
之前的长任务经过几次降级停留在了最下级,这时候来了一个短工作,长任务开始阻塞,直到短任务执行完或者降级到同一级长任务继续运行;
3. 如果有I/O呢?
在之前一个长任务A的情况下,这时候加入了一个大量I/O的交互性工作B,B执行不到一个时间片的情况下放弃CPU开始执行A,不降级,I/O结束后因为A的优先级更高继续执行B;
· 尝试一中MLFQ的问题
1. 如果有太多的交互性工作,就会因为不降级优先级高不断占用CPU,导致长工作无法得到CPU (饥饿问题,程序长时间得不到CPU)
2. 一个程序在不同时间的表现可能不同,一个长任务可能会在某个时间段表现为一个频繁交互的进程
· 提升优先级 (尝试二)
为了避免长任务陷入饥饿问题,一个简单思路就是周期性的提升所有工作的优先级,
最简单的方法就是将所有的工作都丢到最高优先级:
所以我们能得出新规则:
规则5:结果一段时间S,就将系统中所有工作重新加入最高优先级队列;
新规则解决了两个问题:
1. 进程不会进入饥饿问题,在最高队列中,进程会以轮转的方式与其他进程共享CPU;
2. 如果一个进程变成交互性,当它优先级提升时,调度程序会正确的对待他;
问题出现了,S应该如何设置?
如果S设置的太高,长工作会饥饿;
如果设置的太低,交互性工作又得不到合适的CPU时间比例。 (频繁让长任务获得高权限,自然会让本来可以长时间获取高权限的交互性进程减少CPU时间)
· 更好的计时方式 (尝试三)
现在还剩下的问题是,在规则4a和4b中,可以利用中间的漏洞让长任务获取过多的调度:
如果一个长任务在时间片快结束的时候,加入了一个无用的I/O就可以通过恶意的设置长时间获取高权限;
为了避免这种恶意设置让长任务永远获得最高优先级,我们需要重新定制规则4;
只要进程完成了自己的配额,就将它降低到第一优先级的队列中,记录每个进程的工作总时间,而不是记录每个时间片中是否占用完全;
所以我们重写新规则:
规则4:一旦工作完了在其中一层的时间配额 (无论中间主动放弃了多少次CPU),就降低其优先度;
· MLFQ调优及其他问题
MLFQ调度算法还有一些问题,其中一个问题就是如何配置一个调度程序,
例如:配置多少队列、每一层队列的时间片配置多大、应该多久提升一次所有进程的优先级?
一般MLFQ都支持不同队列拥有可变的时间片长度,高优先级队列拥有较短时间片 (10ms或者更少),因而这一层的交互工作 (长时间保持在这一层)可以更快的切换;
相反,低优先级队列更多的是CPU密集型工作,配置更长的时间片能让他们持续执行会取得更好的结果。
· 总结
规则1:如果A的优先级>B的优先级,运行A,不运行B;
规则2:如果A的优先级==B的优先级,RR轮转A、B程序;
规则3:工作进入系统时,放在最高的优先级
规则4:一旦工作用完了其在某一层中的时间配额 (无论中间放弃主动放弃了多少次CPU),就降低其优先度 (移入低一级队列);
规则5:经过一段时间S,就将系统中所有工作都重新加入最高优先级队列。
· MLFQ的特点:
1. 不需要对于工作的运行方式有先验知识,而是通过观察工作的运行给出对应的优先级;
2. 对应短时间运行的交互型工作,获得类似 SJF/STCF的很好的全局性能;
3. 对于长时间运行的CPU密集型负载也可以公平的、不断的稳步向前。
六、调度(二)----比例份额 (proportional-share/fair-share)
比例份额苏纳法基于一个简单的想法:
调度程序的最终目标是确保每个工作获得一定比例的CPU时间,而不是优化周转时间和响应时间;
· 基本概念:彩票表示份额
ticket表示进程占用某个资源的份额;一个进程拥有的彩票数占总彩票数的百分比就是它占有资源的份额;
假设有两个进程A、B,A拥有75张彩排,B拥有25张彩排,因此我们希望A占用75%的资源;
我们设定0~99中0~74属于A进程,然后每个时间片随机在0~99中抽取,如果是在0~74就运行A程序;
这个方法运行的时间越长,时间片越多,抽取的次数越多,分配的概率越接近期望值。
· 彩票机制
彩票调度还提供了一些机制,用于不同且有效的调度彩票;
彩票货币:允许一组彩票的用户以他们喜欢的某种货币将彩票分给不同的工作,之后OS在将货币转化为彩票抽取;
(即,允许每个用户给自己的工作分配,再按照比例换算之后和其他用户一起参与抽取)
彩票转让:通过转让,一个进程可以临时将自己的彩票交给另一个进程;
(例如在server/client场景中,client想server发送消息,请求按照需求执行工作,为了加速server工作,client将自己的ticket转让给server)
彩票通胀:一个进程可以临时提升或者降低自己的ticket;
(这个机制不适合在互不信任的环境,因为一个贪婪的进程可能会尽可能的给自己多的ticket;但是在互相信任的环境比较合适,如果一个进程知道它需要更多的CPU时间,就可以增加自己的ticket)
· 实现
彩票调度的实现只需要一个不错的随机数生成器来选择中奖彩票和一个记录系统所有进程的数据结构,以及彩票的总数;
例: A、B、C所拥有的ticket为100、100、200,所以ticket总数为400;
假设随机数生成器抽取到的ticket是300,只需要从前往后遍历进程列表,100<300/ 100+100<300/ 100+100+200>300;
所以抽取到的进程是C进程;
我们还可以实现按照拥有的ticket数量递减排序,可以减少抽取到的迭代次数;
虽然在这次抽取中结果会改变,但是在多次抽取是依然是按照期望发布的;
· 为什么不是确定的
从之前的内容来看,虽然随即方式可以是的调度程序的实现简单且大致正确,但是在工作时间很短的情况下不能产生正确的比例;
所以基于这一点,提出了步长调度,一个公平的分配算法;
步长调度:
假设A、B、C所拥有的ticket为100、200、500,我们通过一个大数 (假设为10000)来除以他们计算步长;
A、B、B的步长分别为100、50、20;每次执行行程数最小的进程
最开始A、B、C的线程为0、0、0;执行A,更新A行程数为100;
最开始A、B、C的线程为100、0、0;执行B,更新B行程数为50;
最开始A、B、C的线程为100、50、0;执行C,更新C行程数为20;
最开始A、B、C的线程为100、50、20;执行C,更新C行程数为40;
一直重复下去。
当A、B、C行程数一样时,A、B、C分别执行了1、2、5次,正好是票数比例;
彩票调度只能一段时间后,大致实现比例;但是步长调度可以在每个调度周期后做到完全正确;
但是这不代表彩票调度完全不如步长调度;
彩票调度不需要每个进程记录全局变量,当新加入一个进程时,步长调度就显得无能为力了 (不知道如何设置新进程的初始值,如果设置为0就会导致新进程独占CPU)
· 总结
彩票调度:
彩票调度通过随机值,可以大致按照比例调度;
步长调度:
能够确定的获取需要的比例;
但是两种方法都不适合I/O;且很难确定每个进程应该分配多少ticket;
七、调度(三)----多处理器调度
重写应用程序,使它可以并行的执行多个任务;将工作分散到多个CPU,因此CPU资源越多运行就越快;
· 多处理器架构
多处理器和单处理器的核心差别就是对硬件缓存 (cache)的应用,以及数据共享的方式;
在单处理器中,因为CPU运行数据较快,内存运行速度较慢,所以引进了缓存的概念;
在一次读取数据的时候,将其他可能会读取的数据放到运行快的缓存中;
如果之后程序需要再次使用这些数据,就会提前在缓存中查找,提升了运行速度;
缓存是居于局部性的概念,即时间局部性和空间局部性
时间局部性:当一个数据被访问,他很有可能会在不久之后再次被访问;
空间局部性:当一个数据被访问,他周边地址空间的数据也很有可能被访问;
但是当处理器变为多处理器,共享一个内存的时候问题就来了;
当CPU1的程序从内存中读取数据并保存到缓存中,然后程序修改了缓存中的值;
这时候CPU2也从内存中读取数据,但是因为CPU1缓存中的数据还没来得及写回内存,就会导致CPU2读取到的仍然是没有修改的旧值;
这个问题被称为缓存一致性;
硬件通过监控内存访问解决了这个问题,保证了共享内存的唯一性:
在基于总线的系统中,使用总线窥探,每个缓存都监听链接所有缓存和内存的总线,来发现内存访问;
如果CPU发现对它放在缓存中的数据的更新,会作废缓存中的副本或者更新内存中的值;
在这里暂时不细说实际操作了
· 同步
既然缓存已经做了这么多工作来保证一致性,OS还需要关心共享数据的访问吗?
答案是需要的,之前的操作并不能保证并发程序的数据一致性,所以我们需要使用互斥语句(比如锁)
例如两个CPU同时进入(银行扣款)的函数,因为cache中的值在读取时没有被修改,所以可以被读取到同一个值,就会导致数据出错;
导致本来只要执行一次的程序被多次执行;
加锁的方法虽然可以解决数据一致性,但是当CPU数量变高,加锁解锁的性能消耗就会变得很大;
· 缓存亲和度
以一个京城在运行程序时,会将数据存入缓存中,这样可以加快下一次运行的速度,但是在不同的CPU中由于需要重新加载数据会变得很慢;
· 单队列调度 (SQMS)
SQMS有几个明显的缺点:
1. 缺乏可扩张性:为了保证在多处理器上正常运行,调度程序需要通过锁来保证原子性;
然而锁可能会带来巨大的性能损耗,尤其是当系统中的CPU数增加,大部分时间都用于单个锁的争用,较小的时间用于处理事务;
2. 缓存亲和度:由于每个数据都简单的从全局队列中选取下一个工作执行,因此都不断在CPU之间转移,与缓存亲和度背道而驰。
· 多队列调度 (MQMS)
MQMS中包含多个调度队列,每个队列都可以使用不同的调度规则,比如轮转或其他可用算法;
当一个进程进入系统之后,系统会依照使用不同的调度规则 (随机或选择较空的队列)将其放入调度队列;
这样每个CPU调度之间就相互独立,就避免了单队列中数据共享和同步可能带来的问题。
MQMS的优势:
天生可扩展性,队列的数量会随着CPU的数量增加,因此锁的开销会变得很小;
MQMS还具有良好的缓存亲和度,所有工作都保持在固定的CPU上,因此可以很好的利用缓存数据;
MQMS的问题----负载不均:
当一个CPU中的工作因为执行时间短已经执行完,另一个CPU中的工作还在执行,这样因为进程还没结束,这段时间白白浪费了一个CPU的性能;
如何解决负载不均:
1. 当一个CPU空闲时,另一个CPU有工作,应该将有工作的CPU中尚未执行的工作迁移一部分给空闲的CPU
2. 如果工作中的CPU采用的是RR轮转调度机制,那么工作CPU上就不会有未执行的工作,也就不能简单的迁移任务;
可以采用不断切换工作的方法实现负载均衡,CPU之间间断性交换执行任务。
· Linux的多处理器调度
1. O(1)调度程序
多对列的基于优先级调度,随时间按推移改变进程的优先级,调度最高级优先进程
2. 完全公平调度程序 (CFS)
多队列的比例调度方法(类似于步长调度)
3. BF调度程序 (BFS)
单队列的基于比例调度
八、总结
· 陷阱和陷阱处理程序: 用于进入内核模式执行权限指令 (比如I/O)和返回用户模式;
· 时钟中断: 定时查看是中断当前进程执行还是跳转到其他进程;
· 操作系统和硬件在进程间的切换(1): 将一些用于跟踪程序运行状态的数据保存在寄存器中,
通过将程序寄存器数据恢复到实际寄存器中来恢复运行进程;
· 操作系统和硬件在进程间的切换(2):
1. 协作方式:当程序panic时,OS获取控制权完成进程调用;
2. 非写作方式:利用时钟中断来决定下一步;
· 操作系统基本机制的核心: 操作系统希望程序能够高效的运行,但是对于那些恶意的程序希望他们停下来;
· 调度策略:
1. 从实际触发,不希望买早餐时,前面排队的在给全班买早餐(笑)
2. MLFQ:多级反馈调度,经常需要交互的程序被认为是追求响应速度的程序拥有高优先级:
规则1:如果A的优先级>B的优先级,运行A,不运行B;
规则2:如果A的优先级==B的优先级,RR轮转A、B程序;
规则3:工作进入系统时,放在最高的优先级
规则4:一旦工作用完了其在某一层中的时间配额,降优先级
规则5:经过一段时间S,就将系统中所有工作都重新加入最高优先级队列。
3. CFS、O(1)、BFS:
O(1):多队列的基于优先级的调度
CFS:多队列的基于比例的调度
BFS:单队列的基于比例的调度
cmd命令打开CPU虚拟化
转载本文章为转载内容,我们尊重原作者对文章享有的著作权。如有内容错误或侵权问题,欢迎原作者联系我们进行内容更正或删除文章。
上一篇:查找nginx执行文件路径
提问和评论都可以,用心的回复会被更多人看到
评论
发布评论
相关文章
-
VMware Ubuntu虚拟机打开报错问题
解决VMware Workstation模块“Disk”启动失败问题
VMware 模块“Disk”启动失败 -
ubuntu cpu开启虚拟化 怎么打开ubuntu虚拟机的命令
&n
ubuntu cpu开启虚拟化 linux ubuntu 文件名 命令行 -
yarn使用虚拟CPU 打开虚拟cpu
QEMU 是一个完整的系统仿真器,它可与 KVM 协同工作,允许你使用硬件和外部设备创建虚拟机。libvirt 能让你管理基础设施的 API 层,即创建和运行虚拟机。-- Marco Sarti 在早些年,在同一台笔记本中运行多个操作系统只能双启动。当时,这些操作系统很难同时运行或彼此交互。许多年过去了,在普通的 PC 上,可以通过虚拟化在一个系统中运行另一个系统。最近的 PC 或笔记本
yarn使用虚拟CPU 打开虚拟机说客户端已禁用cpu 禁用硬件虚拟化才能启动虚拟机 虚拟机centos8.0扩展磁盘 虚拟化