持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
关于时间片轮转法,是规定一个较小的时间片。在就绪队列中,每个进程循环使用该时间片。当一个进程使用完时间片以后,就要回到就绪队列尾部,cpu交给下一个进程。使用时间片轮转法是抢占式的。
具体流程
如图,这里假设时间片是1。首先cpu本着先来先服务的原则(FCFS)把进程添加到就绪队列中,A,B,C,D,E按序到达。
就绪队列队首的进程A出队列,并把cpu分配给进程A,进程A可以使用cpu
进程A使用完一个时间片以后,交出cpu使用权,并回到就绪队列尾部。下一个进程B出队列,并把cpu分配给进程B
当一个进程需要的服务时间已经全部执行完了以后,它便不会回到就绪队列里面。因为它已经执行完了嘛。这样循环往复,直到所有进程执行结束。就会得到上面的答案。这里还有一个点需要注意的。如果一个时间片还未使用完毕,但是进程已经执行完了,不需要等到时间片全部消耗完,直接让出cpu使用权给队列的下一个进程。
go实现
package main
import (
"fmt"
"sort"
)
/**
* 进程控制块
*/
type Pcb struct {
Name string
//这里我没有用上这个
State int
/** 到达时间 */
ArriveTime int
/** 已使用cpu时间 */
RunTime int
/** 服务时间 */
ServiceTime int
/** 结束时间 */
FinishTime int
}
/* 就绪队列 */
var readyQueue []*Pcb
/* 用于记录结束的pcb */
var finishQueue []*Pcb
/** 用来记录当前时间 */
var currentTime int = 0
/** 时间片 */
var LimitTime int
//时间片轮转法
func Rr() {
//所有进程按到达时间到达
sort.Sort(SortPcbByArriveTime(readyQueue))
//当前时间块进到第一个到达的进程的时间
currentTime = readyQueue[0].ArriveTime
for len(readyQueue) > 0{
//就绪队列中取出第一个进程
nowProcess := readyQueue[0]
readyQueue = readyQueue[1:]
//使用时间片
if nowProcess.ServiceTime - nowProcess.RunTime > LimitTime {
//如果需要使用时间小于时间片,减小该进程运行时间
nowProcess.RunTime = nowProcess.RunTime + LimitTime
//更新当前时间
currentTime = currentTime + LimitTime
//把当前为执行完的进程插入到队列尾部
readyQueue = append(readyQueue, nowProcess)
} else {
//如果进程在没有使用完时间片以前已经结束
//更新当前时间
currentTime = currentTime + nowProcess.ServiceTime - nowProcess.RunTime
nowProcess.RunTime = nowProcess.ServiceTime
//更新结束时间
nowProcess.FinishTime = currentTime
finishQueue = append(finishQueue,nowProcess)
}
}
}
func FinishPrint() {
for i:=0;i<len(finishQueue);i++ {
// 周转时间
p := finishQueue[i]
turnaroundTime := p.FinishTime - p.ArriveTime
weightWaitTime := float64(turnaroundTime) / float64(p.ServiceTime)
t := fmt.Sprintf("进程名称:%s 到达时间:%d 结束时间:%d 周转时间:%d 带权周转时间:%f\n",
p.Name,p.ArriveTime,p.FinishTime,turnaroundTime,weightWaitTime)
fmt.Println(t)
}
}
/**
* 用于对pcb列表进行排序
* 根据开始时间
*/
type SortPcbByArriveTime []*Pcb
func (s SortPcbByArriveTime)Len()int {
return len(s)
}
func (s SortPcbByArriveTime)Less(a int, b int)bool {
return s[a].ArriveTime < s[b].ArriveTime
}
func (s SortPcbByArriveTime)Swap(a int, b int) {
tmp := s[a]
s[a] = s[b]
s[b] = tmp
}
func main() {
//时间片
LimitTime = 1
//进程的队列
readyQueue = []*Pcb{
{Name:"A", ArriveTime:0, ServiceTime:4,},
{Name:"B", ArriveTime:1, ServiceTime:3,},
{Name:"C", ArriveTime:2, ServiceTime:4,},
{Name:"D", ArriveTime:3, ServiceTime:2,},
{Name:"E", ArriveTime:4, ServiceTime:4,},
}
//时间片轮转
Rr()
FinishPrint()
}
附上一个运行结果,成功了捏。
最后进程E的带权周转时间很明显3.25,用周转时间除服务时间,得到的结果。书上给的3.33,我暂时不好说。
关于自己遇到的一个坑
这个时间片轮转法是我在操作系统实验课上写的。事实上,我一开始并不是这样想的。因为上述题目中A,B,C,D,E不是一下子全部都在就绪队列里面的嘛。 比如,A在使用时间片的时候,B还未在就绪队列里面,B应该是在时刻1的时候才进入就绪队列里面。如果这样想的话,计算出来的结果就会和书上的不一样。可能书上不要求这么复杂吧。
在时刻1-2之间,B在使用cpu,A回到了就绪队列尾部,然后C还未进入就绪队列。当到达时刻3的时候,C才会到就绪队列尾部。这样的话,并不向上面的那样简单。
附上这种情况的时间片轮转的go实现
package main
import (
"fmt"
"sort"
)
/**
* 进程控制块
*/
type Pcb struct {
Name string
State int
/** 到达时间 */
ArriveTime int
/** CPU使用时间 */
RunTime int
/** 还需要运行的时间 */
ServiceTime int
/** 结束时间 */
FinishTime int
}
/* 用于存储输入的pcb */
var pcbList []*Pcb
/* 就绪队列 */
var readyQueue []*Pcb
/* 用于记录结束的pcb */
var finishQueue []*Pcb
/** 用来记录当前时间 */
var currentTime int = 0
/** 时间片 */
var limitTime int
//时间片轮转法
func Rr() {
//所有pcb按到达时间到达
sort.Sort(SortPcbByArriveTime(pcbList))
for len(pcbList) > 0 || len(readyQueue) > 0{
if len(readyQueue) != 0 {
//就绪队列中取出第一个进程
nowPcb := readyQueue[0]
readyQueue = readyQueue[1:]
//使用时间片
if nowPcb.ServiceTime - nowPcb.RunTime > limitTime {
//如果需要使用时间小于时间片,减小该进程运行时间
nowPcb.RunTime = nowPcb.RunTime + limitTime
//更新当前时间
currentTime = currentTime + limitTime
//判断队列里面是否有进程需要进入就绪队列
toReadyList()
//把当前为执行完的进程插入到队列尾部
readyQueue = append(readyQueue, nowPcb)
} else {
//如果在未使用完时间片,进程已经结束
currentTime = currentTime + nowPcb.ServiceTime - nowPcb.RunTime
nowPcb.RunTime = nowPcb.ServiceTime
//更新结束时间
nowPcb.FinishTime = currentTime
finishQueue = append(finishQueue,nowPcb)
//判断队列里面是否有进程需要进入就绪队列
toReadyList()
}
} else {
//如果就绪队列为空,更新当前时间,并添加进程到就绪队列
currentTime = pcbList[0].ArriveTime
toReadyList()
}
}
}
func FinishPrint() {
for i:=0;i<len(finishQueue);i++ {
// 周转时间
p := finishQueue[i]
turnaroundTime := p.FinishTime - p.ArriveTime
weightWaitTime := float64(turnaroundTime) / float64(p.ServiceTime)
t := fmt.Sprintf("进程名称:%s 到达时间:%d 结束时间:%d 周转时间:%d 带权周转时间:%f\n",
p.Name,p.ArriveTime,p.FinishTime,turnaroundTime,weightWaitTime)
fmt.Println(t)
}
}
/**
* 把到达的pcb加入到就绪队列中
*/
func toReadyList() {
for len(pcbList) > 0{
if pcbList[0].ArriveTime <= currentTime {
readyQueue = append(readyQueue, pcbList[0])
pcbList = pcbList[1:]
} else {
break
}
}
}
/**
* 用于对pcb列表进行排序
* 根据开始时间
*/
type SortPcbByArriveTime []*Pcb
func (s SortPcbByArriveTime)Len()int {
return len(s)
}
func (s SortPcbByArriveTime)Less(a int, b int)bool {
return s[a].ArriveTime < s[b].ArriveTime
}
func (s SortPcbByArriveTime)Swap(a int, b int) {
tmp := s[a]
s[a] = s[b]
s[b] = tmp
}
func main() {
//时间片
limitTime = 1
//进程的队列
pcbList = []*Pcb{
{Name:"A", ArriveTime:0, ServiceTime:4,},
{Name:"B", ArriveTime:1, ServiceTime:3,},
{Name:"C", ArriveTime:2, ServiceTime:4,},
{Name:"D", ArriveTime:3, ServiceTime:2,},
{Name:"E", ArriveTime:4, ServiceTime:4,},
}
//时间片轮转
Rr()
FinishPrint()
}
运行结果
和书上的答案不一致,但是也不算错,就是复杂了一点