有如下进程,根据到达时间和运行时间,计算进程的周转时间、带权周转时间、等待时间,及对应平均时间。
- P1:0到达,运行7,7结束
- P2:2到达,运行4,11结束
- P3:4到达,运行1,12结束
- P4:5到达,运行4,16结束
周转时间
- P1:7-0=7
- P2:11-2=9
- P3:12-4=8
- P4:16-5=11
平均周转时间:8.75
带权周转时间:
- P1:7/7=1
- P2:9/4=2.25
- P3:8/1=8
- P4:11/4=2.75
平均带权周转时间:3.5
等待时间:
- P1:7-7=0
- P2:9-4=5
- P3:8-1=7
- P4:11-4=7
平均等待时间:4.75
短进程优先(SPF)
由于FCFS的缺点,于是提出了SPF。shortest process first,运行时间短的进程,优先级高。运行时间相同,先来的先运行。
优点:几乎同时到达的情况下,可以得到最短的平均周转时间和平均等待时间
缺点:对短作业有利,最长作业不友好,可能造成饥饿现象
例题
还是上面那个,调度顺序P1->P3->P2->P4
- P1:0到达,运行7,7结束
- P2:2到达,运行4,12结束
- P3:4到达,运行1,8结束
- P4:5到达,运行4,16结束
周转时间
- P1:7-0=7
- P2:12-2=10
- P3:8-4=4
- P4:16-5=11
平均周转时间:8
带权周转时间:
- P1:7/7=1
- P2:10/4=2.5
- P3:4/1=4
- P4:11/4=2.75
平均带权周转时间:2.56
等待时间:
- P1:7-7=0
- P2:10-4=6
- P3:4-1=3
- P4:11-4=7
平均等待时间:4
响应比优先算法(HRRN)
考虑等待时间和运行时间,综合了FCFS和SPF,对于长作业,等待时间长,响应比增大,不会饥饿
响应比=(等待时间+运行时间)/运行时间,响应比>=1,越大越优先,关注进程结束时剩余各进程响应比
例题
时刻表如下,绿色运行,黄色结束,括号内为响应比:
0:P1(7/7,7)
7:P3((3+1)/1=4)、P2((5+4)/4=2.25)、P4((2+4)/4=1.5)、P1
8:P2((6+4)/4=2.5)、P4((3+4)/4=1.75)、P3、P1
12:P4((7+4)/4=2.75)、P2、P3、P1
16:P4、P2、P3、P1
调度顺序:P1->P3->P2->P4
周转时间
- P1:7-0=7
- P2:12-2=10
- P3:8-4=4
- P4:16-5=11
平均周转时间:8.0
带权周转时间:8
- P1:7/7=1
- P2:10/4=2.5
- P3:4/1=4
- P4:11/4=2.75
平均带权周转时间:2.56
等待时间:
- P1:7-7=0
- P2:10-4=6
- P3:4-1=3
- P4:11-4=7
平均等待时间:4.0
剥夺式/抢占式
适合分时、实时操作系统
最短剩余时间优先(SRTN)
例题
Shortest Remaining Time Next,最短剩余时间优先,剩余时间相同,先到达的先运行。关注有新进程到达时刻以及进程完成时刻。
时刻表如下,绿色代表运行的,黄色代表结束的
0:P1(7)
2:P2(4)、P1(5)
4:P3(1)、P2(2)、P1(5)
5:P2(2)、P4(4)、P1(5)、P3(0)
7:P4(4)、P1(5)、P2(0)、P3(0)
11:P1(5)、P4(0)、P2(0)、P3(0)
16:P1(0)、P4(0)、P2(0)、P3(0)
调度顺序:P1->P2->P3->P2->P4->P1
- P1:0到达,运行7,16结束
- P2:2到达,运行4,7结束
- P3:4到达,运行1,5结束
- P4:5到达,运行4,11结束
周转时间
- P1:16-0=16
- P2:7-2=5
- P3:5-4=1
- P4:11-5=6
平均周转时间:7
带权周转时间:
- P1:16/7=2.28
- P2:5/4=1.25
- P3:1/1=1
- P4:6/4=1.5
平均带权周转时间:1.5
等待时间:
- P1:16-7=9
- P2:5-4=1
- P3:1-1=0
- P4:6-4=2
平均等待时间:3
时间片轮转调度算法(RR)
Round-Robin,就绪队列中,新到达的进程排在同时刻下处理机的进程前面。时间片未使用完,进程结束,主动释放CPU。
如果时间片太大,使得每个进程都可以在一个时间片内就完成,则时间片轮转调度算法退化为先来先服务调度算法,并且会增大进程响应时间。因此时间片不能太大。
另一方面,进程调度、切换是有时间代价的(保存、恢复运行环境),因此如果时间片太小,会导致进程切换过于频繁,*系统会花大量的时间来处理进程切换,从而导致实际用于进程执行的时间比例减少。可见时间片也不能太小。
时间片为2
0:P1到达,P1运行
2:P2到达,P1运行一个时间片完毕,就绪队列为P2(4)、P1(5),P2运行
4:P3到达,P2运行一个时间片完毕,就绪队列为P1(5)、P3(1)、P2(2),P1运行
5:P4到达,就绪队列P3(1)、P2(2)、P4(4)
6:P1运行一个时间片完毕,就绪队列P3(1)、P2(2)、P4(4)、P1(3),P3运行
7:P3运行完毕,就绪队列P2(2)、P4(4)、P1(3),P2运行
9:P2运行完毕,就绪队列P4(4)、P1(3),P4运行
11:P4运行一个时间片完毕,就绪队列P1(3)、P4(2),P1运行
13:P1运行一个时间片完毕,就绪队列P4(2)、P1(1),P4运行
15:P4运行完毕,就绪队列P1(1),P1运行
16:P1运行完毕
调度顺序:P1->P2->P1->P3->P2->P4->P1->P4->P1
- P1:0到达,运行7,首次运行时间0,16结束
- P2:2到达,运行4,首次运行时间2,9结束
- P3:4到达,运行1,首次运行时间6,7结束
- P4:5到达,运行4,首次运行时间9,15结束
响应时间:
- P1:0-0=0
- P2:2-2=0
- P3:6-4=2
- P4:9-5=4
抢占式优先级调度算法
实时操作系统用的更多,关注就绪队列改变(新到达进程优先级可能比正在运行的更高)和进程主动释放CPU的时刻
优点:优先级可实时动态调整,适用于实时操作系统,很灵活
缺点:有源源不断的高优先级进程到达,造成饥饿现象
- 系统进程优先级高于用户进程
- 前台进程优先级高于后台进程
- 操作系统更偏好I/O型进程(或称I/O繁忙型进程)
例题
调度顺序:P1->P2->P3->P2->P4->P1
- P1:0到达,运行7,16结束
- P2:2到达,运行4,7结束
- P3:4到达,运行1,5结束
- P4:5到达,运行4,11结束
时刻表如下:
0:P1到达,就绪队列:P1(7,1),P1运行
2:P2到达,就绪队列:P2(4,2),P2运行,P1(5,1)进入就绪队列
4:P3到达,就绪队列:P3(1,3)、P1(5,1),P3运行,P2(2,2)进入就绪队列
5:P3运行完毕,P4到达,就绪队列:P2(2,2)、P4(4,2)、P1(5,1),P2运行
7:P2运行完毕,就绪队列:P4(4,2)、P1(5,1),P4运行
11:P4运行完毕,就绪队列:P1(5,1),P1运行
16:P1运行完毕
响应时间:
- P1:0-0=0
- P2:2-2=0
- P3:4-4=0
- P4:7-5=2
多级反馈队列
前面的几个
设置多级就绪队列,各级队列优先级从高到低,时间片从小到大。新进程到达时先进入第1级队列,按FCFS原则排队等待被分配时间片。若用完时间片进程还未结束,则进程进入下一级队列队尾。如果此时已经在最下级的队列,则重新放回最下级队列队尾。只有第k级队列为空时,才会为k+1级队头的进程分配时间片。被抢占处理机的进程重新放回原队列队尾。
FCFS和SPF太过简单,我们就实现一个HRRN
HRRN的Go实现
设计
进程结构体的设计
// 进程
type Process struct{
Pid int // 进程id
Pname string // 进程名
Runtime int // 运行时间
Priority int // 优先数
}
优先数没有用到,是为了以后想再实现其他算法时能够兼容一下。
就绪队列节点的设计
// 就绪队列节点
type Node struct {
p *Process // 进程
Arrtime int // 到达时间
Waittime int // 等待时间
}
就绪队列清楚进程什么时候到达,等待了多久。队列就使用切片,如果使用C++实现,可以使用vector等stl。
处理机的设计
// 处理机
type Processor struct {
p *Process
}
// 运行
func (p *Processor) Run() bool {
println("running", p.p.Pname)
p.p.Runtime --
if p.p.Runtime == 0{
println(p.p.Pname,"finish")
p.p = nil // 进程移除内存
return true
}
return false
}
处理机上面就是进程,有运行进程的功能,运行完毕将其移出内存。
全部代码
package main
import (
"fmt"
)
// 进程
type Process struct{
Pid int // 进程id
Pname string // 进程名
Runtime int // 运行时间
Priority int // 优先数
}
// 固定优先数进程创建
func New(pid,runtime int,pname string) *Process {
return &Process{
Pid: pid,
Pname: pname,
Runtime: runtime,
Priority: 0,
}
}
// 就绪队列节点
type Node struct {
p *Process // 进程
Arrtime int // 到达时间
Waittime int // 等待时间
}
// 进程移除就绪队列
func Pop(b *[]Node,index int){
//-----索引越界------
if index<0 || index>=len(*b){
return
}
//------前删-------
if index == 0{
*b = (*b)[1:]
return
}
//------尾删-------
if index == len(*b)-1{
*b = (*b)[:len(*b)-1]
return
}
//------中间删------
*b = append((*b)[0:index],(*b)[index+1:]...)
return
}
// 处理机
type Processor struct {
p *Process
}
// 运行
func (p *Processor) Run() bool {
println("running", p.p.Pname)
p.p.Runtime --
if p.p.Runtime == 0{
println(p.p.Pname,"finish")
p.p = nil // 进程移除内存
return true
}
return false
}
// 进程到达
func ProcessCome(queue []Node, time int) []Node {
// 进程到达
if time == 0{
queue = append(queue, Node{
p: New(0,7,"P1"),
Arrtime: 0,
})
fmt.Printf("%d %s 到达\n",time,"P1")
}
if time == 2{
queue = append(queue, Node{
p: New(1,4,"P2"),
Arrtime: 2,
})
fmt.Printf("%d %s 到达\n",time,"P2")
}
if time == 4{
queue = append(queue,Node{
p: New(2,1,"P3"),
Arrtime: 4,
})
fmt.Printf("%d %s 到达\n",time,"P3")
}
if time == 5{
queue = append(queue, Node{
p: New(3,4,"P4"),
Arrtime: 5,
})
fmt.Printf("%d %s 到达\n",time,"P4")
}
return queue
}
// 从就绪队列选择进程分配给处理机
func HRRN(queue []Node,p *Processor) {
// 处理机上进程完成标志
finish := true
for time:=0;;time++{
queue = ProcessCome(queue,time)
println("queue 长度:",len(queue))
// 就绪队列和处理机中都没有进程,结束
if len(queue) == 0 && p.p == nil{
break
}
// 处理机上需要进程
if finish == true{
max := 0.0
index := -1
var runP Node
// 遍历就绪队列
for i,p := range queue{
//println(p.p.Pname,p.Waittime)
ratio := 1.0 + float64(p.Waittime)/float64(p.p.Runtime)
fmt.Printf("%s响应比%f\n",p.p.Pname,ratio)
if ratio > max{
max = ratio
index = i
runP = p
}else if ratio < max{
}else {
// 响应比相同,先到达的先运行
if runP.Arrtime > p.Arrtime{
max = ratio
index = i
runP = p
}
}
}
// 就绪队列的进程放入处理机
p.p = runP.p
//就绪队列进程移除
Pop(&queue,index)
}
// 处理机运行进程
if p.p != nil{
finish = p.Run()
}
// 就绪队列进程等待时间增加
for i := range queue{
queue[i].Waittime ++
}
}
fmt.Println("就绪队列没有进程,处理机处于监听状态")
}
func main() {
// 就绪队列
queue := make([]Node,0)
// 处理机
p := Processor{}
HRRN(queue,&p)
}