操作系统-处理机调度详解(调度层次及FCFS、SPF、RR等算法)_短进程优先(spf)算法进行调度

176 阅读10分钟

有如下进程,根据到达时间和运行时间,计算进程的周转时间、带权周转时间、等待时间,及对应平均时间。

  • 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)
}



结果