From f6b6d6299a8370e9078785f960f81bff7ba41d6e Mon Sep 17 00:00:00 2001 From: yuchen Date: Mon, 18 May 2015 01:08:08 +0800 Subject: [PATCH] add lec23 io-device spoc homework --- related_info/lab8/disksim-homework.md | 81 +++ related_info/lab8/disksim-homework.py | 719 ++++++++++++++++++++++++++ 2 files changed, 800 insertions(+) create mode 100644 related_info/lab8/disksim-homework.md create mode 100755 related_info/lab8/disksim-homework.py diff --git a/related_info/lab8/disksim-homework.md b/related_info/lab8/disksim-homework.md new file mode 100644 index 0000000..4889ce8 --- /dev/null +++ b/related_info/lab8/disksim-homework.md @@ -0,0 +1,81 @@ +# 磁盘访问 练习 + +## 磁盘抽象模型 + +一个磁盘反指针旋转,有3个磁道和一个磁头,每个磁道有12个扇区。 +完成一个磁盘访问请求的时间包括:寻道时间(seek time)+旋转时间(rotational time)+传输时间(transfer time) + +执行如下 + +``` +$ ./disksim.py -a 10 -G +``` + +`-a 10`表示访问扇区10, `-G`表示显示图形动画。 +可以看到磁头在外侧磁道的扇区6的中间位置, 扇区10与扇区6在一个磁道上。在图形界面上按`s`键,将启动模拟执行过程。并在执行结束后,按`q`键,则退出图形,并显示统计结果如下: + + +``` +REQUESTS ['10'] + +Block: 10 Seek: 0 Rotate:105 Transfer: 30 Total: 135 + +TOTALS Seek: 0 Rotate:105 Transfer: 30 Total: 135 +``` + +表示寻道时间是0个时间单位,旋转时间是105个时间单位,传输时间是30个时间单位,总共的磁盘访问请求的时间是135.注意从扇区6到扇区9,旋转了90度,而为了进行传输,需要从扇区9~10的中间位置开始,从扇区10~11的中间位置结束。所以需要再旋转15度,即旋转了105度,而每旋转1度花费1个时间单位,所以旋转花费了105个时间单位。 + +如果执行 + +``` +$ ./disksim.py -a 10,11 -G +``` +表明发出了2个磁盘访问请求,可得到如下的结果 +``` +REQUESTS ['10', '11'] + +Block: 10 Seek: 0 Rotate:105 Transfer: 30 Total: 135 +Block: 11 Seek: 0 Rotate: 0 Transfer: 30 Total: 30 + +TOTALS Seek: 0 Rotate:105 Transfer: 60 Total: 165 + +``` +由于访问完扇区10后,紧接着立刻访问扇区11,所以寻道和旋转时间都是0,总的访问时间是165. + +如果需要寻道,比如执行 + +``` +$ ./disksim.py -a 10,18 -G +``` +REQUESTS ['10', '18’] +Sector: 10 Seek: 0 Rotate:105 Transfer: 30 Total: 135 +Sector: 18 Seek: 40 Rotate:170 Transfer: 30 Total: 240 +TOTALS Seek: 40 Rotate:275 Transfer: 60 Total: 375 +``` +这里一个寻道的时间是40个时间单位。且假设采用FIFO(FCFS)磁盘调度算法。当访问完扇区10后,磁头需要寻道到中间磁道处(包括扇区18),扇区10对应的中间磁道的扇区是22号扇区,从扇区22扇区18需要旋转7个扇区的距离(23,12,13,14,15,16,17),花费210个时间单位,注意这里面包含了40个寻道的时间单位,所以,旋转所化时间为210-40=170个时间单位。这样,总体的访问时间为375 + + +请回答如下问题: +问题 1:请执行 FIFO磁盘调度策略 +``` +./disksim.py 采用FIFO -a 0 +./disksim.py -a 6 +./disksim.py -a 30 +./disksim.py -a 7,30,8 +./disksim.py -a 10,11,12,13,24,1 +``` +请回答每个磁盘请求序列的IO访问时间 + +问题 2:请执行 SSTF磁盘调度策略 +``` +./disksim.py -a 10,11,12,13,24,1 +``` +请回答每个磁盘请求序列的IO访问时间 + + +问题 3:请执行 SCAN, C-SCAN磁盘调度策略 +``` +./disksim.py -a 10,11,12,13,24,1 +``` +请回答每个磁盘请求序列的IO访问时间 + diff --git a/related_info/lab8/disksim-homework.py b/related_info/lab8/disksim-homework.py new file mode 100755 index 0000000..3531506 --- /dev/null +++ b/related_info/lab8/disksim-homework.py @@ -0,0 +1,719 @@ +#! /usr/bin/env python + +from Tkinter import * +from types import * +import math, random, time, sys, os +from optparse import OptionParser +from decimal import * + +MAXTRACKS = 1000 + +# states that a request/disk go through +STATE_NULL = 0 +STATE_SEEK = 1 +STATE_ROTATE = 2 +STATE_XFER = 3 +STATE_DONE = 4 + +# +# TODO +# XXX transfer time +# XXX satf +# XXX skew +# XXX scheduling window +# XXX sstf +# XXX specify requests vs. random requests in range +# XXX add new requests as old ones complete (starvation) +# XXX run in non-graphical mode +# XXX better graphical display (show key, long lists of requests, more timings on screen) +# XXX be able to do "pure" sequential +# XXX add more blocks around outer tracks (zoning) +# XXX simple flag to make scheduling window a fairness window (-F) +# new algs to scan and c-scan the disk? +# + +class Disk: + def __init__(self, addr, addrDesc, lateAddr, lateAddrDesc, + policy, seekSpeed, rotateSpeed, skew, window, compute, + graphics, zoning): + self.addr = addr + self.addrDesc = addrDesc + self.lateAddr = lateAddr + self.lateAddrDesc = lateAddrDesc + self.policy = policy + self.seekSpeed = Decimal(seekSpeed) + self.rotateSpeed = Decimal(rotateSpeed) + self.skew = skew + self.window = window + self.compute = compute + self.graphics = graphics + self.zoning = zoning + + # figure out zones first, to figure out the max possible request + self.InitBlockLayout() + + # figure out requests + random.seed(options.seed) + self.requests = self.MakeRequests(self.addr, self.addrDesc) + self.lateRequests = self.MakeRequests(self.lateAddr, self.lateAddrDesc) + + # graphical startup + self.width = 500 + if self.graphics: + self.root = Tk() + tmpLen = len(self.requests) + if len(self.lateRequests) > 0: + tmpLen += len(self.lateRequests) + self.canvas = Canvas(self.root, width=410, height=460 + ((tmpLen / 20) * 20)) + self.canvas.pack() + + # fairness stuff + if self.policy == 'BSATF' and self.window != -1: + self.fairWindow = self.window + else: + self.fairWindow = -1 + + print 'REQUESTS', self.requests + print '' + + # for late requests + self.lateCount = 0 + if len(self.lateRequests) > 0: + print 'LATE REQUESTS', self.lateRequests + print '' + + if self.compute == False: + print '' + print 'For the requests above, compute the seek, rotate, and transfer times.' + print 'Use -c or the graphical mode (-G) to see the answers.' + print '' + + # BINDINGS + if self.graphics: + self.root.bind('s', self.Start) + self.root.bind('p', self.Pause) + self.root.bind('q', self.Exit) + + # TRACK INFO + self.tracks = {} + self.trackWidth = 40 + self.tracks[0] = 140 + self.tracks[1] = self.tracks[0] - self.trackWidth + self.tracks[2] = self.tracks[1] - self.trackWidth + + if (self.seekSpeed > 1 and self.trackWidth % self.seekSpeed != 0): + print 'Seek speed (%d) must divide evenly into track width (%d)' % (self.seekSpeed, self.trackWidth) + sys.exit(1) + if self.seekSpeed < 1: + x = (self.trackWidth / self.seekSpeed) + y = int(float(self.trackWidth) / float(self.seekSpeed)) + if float(x) != float(y): + print 'Seek speed (%d) must divide evenly into track width (%d)' % (self.seekSpeed, self.trackWidth) + sys.exit(1) + + # DISK SURFACE + self.cx = self.width/2.0 + self.cy = self.width/2.0 + if self.graphics: + self.canvas.create_rectangle(self.cx-175, 30, self.cx - 20, 80, fill='gray', outline='black') + self.platterSize = 320 + ps2 = self.platterSize / 2.0 + if self.graphics: + self.canvas.create_oval(self.cx-ps2, self.cy-ps2, self.cx+ps2, self.cy + ps2, fill='darkgray', outline='black') + for i in range(len(self.tracks)): + t = self.tracks[i] - (self.trackWidth / 2.0) + if self.graphics: + self.canvas.create_oval(self.cx - t, self.cy - t, self.cx + t, self.cy + t, fill='', outline='black', width=1.0) + + # SPINDLE + self.spindleX = self.cx + self.spindleY = self.cy + if self.graphics: + self.spindleID = self.canvas.create_oval(self.spindleX-3, self.spindleY-3, self.spindleX+3, self.spindleY+3, fill='orange', outline='black') + + # DISK ARM + self.armTrack = 0 + self.armSpeedBase = float(seekSpeed) + self.armSpeed = float(seekSpeed) + + distFromSpindle = self.tracks[self.armTrack] + self.armWidth = 20 + self.headWidth = 10 + + self.armX = self.spindleX - (distFromSpindle * math.cos(math.radians(0))) + self.armX1 = self.armX - self.armWidth + self.armX2 = self.armX + self.armWidth + self.armY1 = 50.0 + self.armY2 = self.width / 2.0 + + self.headX1 = self.armX - self.headWidth + self.headX2 = self.armX + self.headWidth + self.headY1 = (self.width/2.0) - self.headWidth + self.headY2 = (self.width/2.0) + self.headWidth + + if self.graphics: + self.armID = self.canvas.create_rectangle(self.armX1, self.armY1, self.armX2, self.armY2, fill='gray', outline='black') + self.headID = self.canvas.create_rectangle(self.headX1, self.headY1, self.headX2, self.headY2, fill='gray', outline='black') + + self.targetSize = 10.0 + if self.graphics: + sz = self.targetSize + self.targetID = self.canvas.create_oval(self.armX1-sz, self.armY1-sz, self.armX1+sz, self.armY1+sz, fill='orange', outline='') + + # IO QUEUE + self.queueX = 20 + self.queueY = 450 + + self.requestCount = 0 + self.requestQueue = [] + self.requestState = [] + self.queueBoxSize = 20 + self.queueBoxID = {} + self.queueTxtID = {} + + # draw each box + for index in range(len(self.requests)): + self.AddQueueEntry(int(self.requests[index]), index) + if self.graphics: + self.canvas.create_text(self.queueX - 5, self.queueY - 20, anchor='w', text='Queue:') + + # scheduling window + self.currWindow = self.window + + # draw current limits of queue + if self.graphics: + self.windowID = -1 + self.DrawWindow() + + # initial scheduling info + self.currentIndex = -1 + self.currentBlock = -1 + + # initial state of disk (vs seeking, rotating, transferring) + self.state = STATE_NULL + + # DRAW BLOCKS on the TRACKS + for bid in range(len(self.blockInfoList)): + (track, angle, name) = self.blockInfoList[bid] + if self.graphics: + distFromSpindle = self.tracks[track] + xc = self.spindleX + (distFromSpindle * math.cos(math.radians(angle))) + yc = self.spindleY + (distFromSpindle * math.sin(math.radians(angle))) + cid = self.canvas.create_text(xc, yc, text=name, anchor='center') + else: + cid = -1 + self.blockInfoList[bid] = (track, angle, name, cid) + + # angle of rotation + self.angle = Decimal(0.0) + + # TIME INFO + if self.graphics: + self.timeID = self.canvas.create_text(10, 10, text='Time: 0.00', anchor='w') + self.canvas.create_rectangle(95,0,200,18, fill='orange', outline='orange') + self.seekID = self.canvas.create_text(100, 10, text='Seek: 0.00', anchor='w') + self.canvas.create_rectangle(195,0,300,18, fill='lightblue', outline='lightblue') + self.rotID = self.canvas.create_text(200, 10, text='Rotate: 0.00', anchor='w') + self.canvas.create_rectangle(295,0,400,18, fill='green', outline='green') + self.xferID = self.canvas.create_text(300, 10, text='Transfer: 0.00', anchor='w') + self.canvas.create_text(320, 40, text='"s" to start', anchor='w') + self.canvas.create_text(320, 60, text='"p" to pause', anchor='w') + self.canvas.create_text(320, 80, text='"q" to quit', anchor='w') + self.timer = 0 + + # STATS + self.seekTotal = 0.0 + self.rotTotal = 0.0 + self.xferTotal = 0.0 + + # set up animation loop + if self.graphics: + self.doAnimate = True + else: + self.doAnimate = False + self.isDone = False + + # call this to start simulation + def Go(self): + if options.graphics: + self.root.mainloop() + else: + self.GetNextIO() + while self.isDone == False: + self.Animate() + + # crappy error message + def PrintAddrDescMessage(self, value): + print 'Bad address description (%s)' % value + print 'The address description must be a comma-separated list of length three, without spaces.' + print 'For example, "10,100,0" would indicate that 10 addresses should be generated, with' + print '100 as the maximum value, and 0 as the minumum. A max of -1 means just use the highest' + print 'possible value as the max address to generate.' + sys.exit(1) + + # + # ZONES AND BLOCK LAYOUT + # + def InitBlockLayout(self): + self.blockInfoList = [] + self.blockToTrackMap = {} + self.blockToAngleMap = {} + self.tracksBeginEnd = {} + self.blockAngleOffset = [] + + zones = self.zoning.split(',') + assert(len(zones) == 3) + for i in range(len(zones)): + self.blockAngleOffset.append(int(zones[i]) / 2) + + track = 0 # outer track + angleOffset = 2 * self.blockAngleOffset[track] + for angle in range(0, 360, angleOffset): + block = angle / angleOffset + self.blockToTrackMap[block] = track + self.blockToAngleMap[block] = angle + self.blockInfoList.append((track, angle, block)) + self.tracksBeginEnd[track] = (0, block) + pblock = block + 1 + + track = 1 # middle track + skew = self.skew + angleOffset = 2 * self.blockAngleOffset[track] + for angle in range(0, 360, angleOffset): + block = (angle / angleOffset) + pblock + self.blockToTrackMap[block] = track + self.blockToAngleMap[block] = angle + (angleOffset * skew) + self.blockInfoList.append((track, angle + (angleOffset * skew), block)) + self.tracksBeginEnd[track] = (pblock, block) + pblock = block + 1 + + track = 2 # inner track + skew = 2 * self.skew + angleOffset = 2 * self.blockAngleOffset[track] + for angle in range(0, 360, angleOffset): + block = (angle / angleOffset) + pblock + self.blockToTrackMap[block] = track + self.blockToAngleMap[block] = angle + (angleOffset * skew) + self.blockInfoList.append((track, angle + (angleOffset * skew), block)) + self.tracksBeginEnd[track] = (pblock, block) + self.maxBlock = pblock + # print 'MAX BLOCK:', self.maxBlock + + # adjust angle to starting position relative + for i in self.blockToAngleMap: + self.blockToAngleMap[i] = (self.blockToAngleMap[i] + 180) % 360 + + # print 'btoa map', self.blockToAngleMap + # print 'btot map', self.blockToTrackMap + # print 'bao', self.blockAngleOffset + + def MakeRequests(self, addr, addrDesc): + (numRequests, maxRequest, minRequest) = (0, 0, 0) + if addr == '-1': + # first extract values from descriptor + desc = addrDesc.split(',') + if len(desc) != 3: + self.PrintAddrDescMessage(addrDesc) + (numRequests, maxRequest, minRequest) = (int(desc[0]), int(desc[1]), int(desc[2])) + if maxRequest == -1: + maxRequest = self.maxBlock + # now make list + tmpList = [] + for i in range(numRequests): + tmpList.append(int(random.random() * maxRequest) + minRequest) + return tmpList + else: + return addr.split(',') + + # + # BUTTONS + # + def Start(self, event): + self.GetNextIO() + self.doAnimate = True + self.Animate() + + def Pause(self, event): + if self.doAnimate == False: + self.doAnimate = True + else: + self.doAnimate = False + + def Exit(self, event): + sys.exit(0) + + # + # CORE SIMULATION and ANIMATION + # + def UpdateTime(self): + if self.graphics: + self.canvas.itemconfig(self.timeID, text='Time: ' + str(self.timer)) + self.canvas.itemconfig(self.seekID, text='Seek: ' + str(self.seekTotal)) + self.canvas.itemconfig(self.rotID, text='Rotate: ' + str(self.rotTotal)) + self.canvas.itemconfig(self.xferID, text='Transfer: ' + str(self.xferTotal)) + + def AddRequest(self, block): + self.AddQueueEntry(block, len(self.requestQueue)) + + def QueueMap(self, index): + numPerRow = 400 / self.queueBoxSize + return (index % numPerRow, index / numPerRow) + + def DrawWindow(self): + if self.window == -1: + return + (col, row) = self.QueueMap(self.currWindow) + if col == 0: + (col, row) = (20, row - 1) + if self.windowID != -1: + self.canvas.delete(self.windowID) + self.windowID = self.canvas.create_line(self.queueX + (col * 20) - 10, self.queueY - 13 + (row * 20), + self.queueX + (col * 20) - 10, self.queueY + 13 + (row * 20), width=2) + + def AddQueueEntry(self, block, index): + self.requestQueue.append((block, index)) + self.requestState.append(STATE_NULL) + if self.graphics: + (col, row) = self.QueueMap(index) + sizeHalf = self.queueBoxSize / 2.0 + (cx, cy) = (self.queueX + (col * self.queueBoxSize), self.queueY + (row * self.queueBoxSize)) + self.queueBoxID[index] = self.canvas.create_rectangle(cx - sizeHalf, cy - sizeHalf, cx + sizeHalf, cy + sizeHalf, fill='white') + self.queueTxtID[index] = self.canvas.create_text(cx, cy, anchor='center', text=str(block)) + + def SwitchColors(self, c): + if self.graphics: + self.canvas.itemconfig(self.queueBoxID[self.currentIndex], fill=c) + self.canvas.itemconfig(self.targetID, fill=c) + + def SwitchState(self, newState): + self.state = newState + self.requestState[self.currentIndex] = newState + + def RadiallyCloseTo(self, a1, a2): + if a1 > a2: + v = a1 - a2 + else: + v = a2 - a1 + if v < self.rotateSpeed: + return True + return False + + def DoneWithTransfer(self): + angleOffset = self.blockAngleOffset[self.armTrack] + # if int(self.angle) == (self.blockToAngleMap[self.currentBlock] + angleOffset) % 360: + if self.RadiallyCloseTo(self.angle, Decimal((self.blockToAngleMap[self.currentBlock] + angleOffset) % 360)): + # print 'END TRANSFER', self.angle, self.timer + self.SwitchState(STATE_DONE) + self.requestCount += 1 + return True + return False + + def DoneWithRotation(self): + angleOffset = self.blockAngleOffset[self.armTrack] + # XXX there is a weird bug in here + # print self.timer, 'ROTATE:: ', self.currentBlock, 'currangle: ', self.angle, ' - mapangle: ', self.blockToAngleMap[self.currentBlock] + # print ' angleOffset ', angleOffset + # print ' blockMap ', (self.blockToAngleMap[self.currentBlock] - angleOffset) % 360 + # print ' self.angle ', self.angle, int(self.angle) + # if int(self.angle) == (self.blockToAngleMap[self.currentBlock] - angleOffset) % 360: + if self.RadiallyCloseTo(self.angle, Decimal((self.blockToAngleMap[self.currentBlock] - angleOffset) % 360)): + self.SwitchState(STATE_XFER) + # print ' --> DONE WITH ROTATION!', self.timer + return True + return False + + def PlanSeek(self, track): + self.seekBegin = self.timer + self.SwitchColors('orange') + self.SwitchState(STATE_SEEK) + if track == self.armTrack: + self.rotBegin = self.timer + self.SwitchColors('lightblue') + self.SwitchState(STATE_ROTATE) + return + self.armTarget = track + self.armTargetX1 = self.spindleX - self.tracks[track] - (self.trackWidth / 2.0) + if track >= self.armTrack: + self.armSpeed = self.armSpeedBase + else: + self.armSpeed = - self.armSpeedBase + + def DoneWithSeek(self): + # move the disk arm + self.armX1 += self.armSpeed + self.armX2 += self.armSpeed + self.headX1 += self.armSpeed + self.headX2 += self.armSpeed + # update it on screen + if self.graphics: + self.canvas.coords(self.armID, self.armX1, self.armY1, self.armX2, self.armY2) + self.canvas.coords(self.headID, self.headX1, self.headY1, self.headX2, self.headY2) + # check if done + if (self.armSpeed > 0.0 and self.armX1 >= self.armTargetX1) or (self.armSpeed < 0.0 and self.armX1 <= self.armTargetX1): + self.armTrack = self.armTarget + return True + return False + + def DoSATF(self, rList): + minBlock = -1 + minIndex = -1 + minEst = -1 + + # print '**** DoSATF ****', rList + for (block, index) in rList: + if self.requestState[index] == STATE_DONE: + continue + track = self.blockToTrackMap[block] + angle = self.blockToAngleMap[block] + # print 'track', track, 'angle', angle + + # estimate seek time + dist = int(math.fabs(self.armTrack - track)) + seekEst = Decimal(self.trackWidth / self.armSpeedBase) * dist + + # estimate rotate time + angleOffset = self.blockAngleOffset[track] + angleAtArrival = (Decimal(self.angle) + (seekEst * self.rotateSpeed)) + while angleAtArrival > 360.0: + angleAtArrival -= 360.0 + rotDist = Decimal((angle - angleOffset) - angleAtArrival) + while rotDist > 360.0: + rotDist -= Decimal(360.0) + while rotDist < 0.0: + rotDist += Decimal(360.0) + rotEst = rotDist / self.rotateSpeed + + # finally, transfer + xferEst = (Decimal(angleOffset) * Decimal(2.0)) / self.rotateSpeed + + totalEst = seekEst + rotEst + xferEst + + # should probably pick one on same track in case of a TIE + if minEst == -1 or totalEst < minEst: + minEst = totalEst + minBlock = block + minIndex = index + # END loop + + # when done + self.totalEst = minEst + assert(minBlock != -1) + assert(minIndex != -1) + return (minBlock, minIndex) + + # + # actually doesn't quite do SSTF + # just finds all the blocks on the nearest track + # (whatever that may be) and returns it as a list + # + def DoSSTF(self, rList): + minDist = MAXTRACKS + minBlock = -1 + trackList = [] # all the blocks on a track + + for (block, index) in rList: + if self.requestState[index] == STATE_DONE: + continue + track = self.blockToTrackMap[block] + dist = int(math.fabs(self.armTrack - track)) + if dist < minDist: + trackList = [] + trackList.append((block, index)) + minDist = dist + elif dist == minDist: + trackList.append((block, index)) + assert(trackList != []) + return trackList + + def UpdateWindow(self): + if self.fairWindow == -1 and self.currWindow > 0 and self.currWindow < len(self.requestQueue): + self.currWindow += 1 + if self.graphics: + self.DrawWindow() + + def GetWindow(self): + if self.currWindow <= -1: + return len(self.requestQueue) + else: + if self.fairWindow != -1: + if self.requestCount > 0 and (self.requestCount % self.fairWindow == 0): + self.currWindow = self.currWindow + self.fairWindow + if self.currWindow > len(self.requestQueue): + self.currWindow = len(self.requestQueue) + if self.graphics: + self.DrawWindow() + return self.currWindow + else: + return self.currWindow + + def GetNextIO(self): + # check if done: if so, print stats and end animation + if self.requestCount == len(self.requestQueue): + self.UpdateTime() + self.PrintStats() + self.doAnimate = False + self.isDone = True + return + + # do policy: should set currentBlock, + if self.policy == 'FIFO': + (self.currentBlock, self.currentIndex) = self.requestQueue[self.requestCount] + self.DoSATF(self.requestQueue[self.requestCount:self.requestCount+1]) + elif self.policy == 'SATF' or self.policy == 'BSATF': + (self.currentBlock, self.currentIndex) = self.DoSATF(self.requestQueue[0:self.GetWindow()]) + elif self.policy == 'SSTF': + # first, find all the blocks on a given track (given window constraints) + trackList = self.DoSSTF(self.requestQueue[0:self.GetWindow()]) + # then, do SATF on those blocks (otherwise, will not do them in obvious order) + (self.currentBlock, self.currentIndex) = self.DoSATF(trackList) + else: + print 'policy (%s) not implemented' % self.policy + sys.exit(1) + + # once best block is decided, go ahead and do the seek + self.PlanSeek(self.blockToTrackMap[self.currentBlock]) + + # add another block? + if len(self.lateRequests) > 0 and self.lateCount < len(self.lateRequests): + self.AddRequest(self.lateRequests[self.lateCount]) + self.lateCount += 1 + + def Animate(self): + if self.graphics == True and self.doAnimate == False: + self.root.after(20, self.Animate) + return + + # timer + self.timer += 1 + self.UpdateTime() + + # see which blocks are rotating on the disk + # print 'SELF ANGLE', self.angle + self.angle = Decimal(self.angle + self.rotateSpeed) + if self.angle >= 360.0: + self.angle = Decimal(0.0) + + # move the blocks + if self.graphics: + for (track, angle, name, cid) in self.blockInfoList: + distFromSpindle = self.tracks[track] + na = angle - self.angle + xc = self.spindleX + (distFromSpindle * math.cos(math.radians(na))) + yc = self.spindleY + (distFromSpindle * math.sin(math.radians(na))) + if self.graphics: + self.canvas.coords(cid, xc, yc) + if self.currentBlock == name: + sz = self.targetSize + self.canvas.coords(self.targetID, xc-sz, yc-sz, xc+sz, yc+sz) + + # move the arm OR wait for a rotational delay + if self.state == STATE_SEEK: + if self.DoneWithSeek(): + self.rotBegin = self.timer + self.SwitchState(STATE_ROTATE) + self.SwitchColors('lightblue') + if self.state == STATE_ROTATE: + # check for read (disk arm must be settled) + if self.DoneWithRotation(): + self.xferBegin = self.timer + self.SwitchState(STATE_XFER) + self.SwitchColors('green') + if self.state == STATE_XFER: + if self.DoneWithTransfer(): + self.DoRequestStats() + self.SwitchState(STATE_DONE) + self.SwitchColors('red') + self.UpdateWindow() + currentBlock = self.currentBlock + self.GetNextIO() + nextBlock = self.currentBlock + if self.blockToTrackMap[currentBlock] == self.blockToTrackMap[nextBlock]: + if (currentBlock == self.tracksBeginEnd[self.armTrack][1] and nextBlock == self.tracksBeginEnd[self.armTrack][0]) or (currentBlock + 1 == nextBlock): + # need a special case here: to handle when we stay in transfer mode + (self.rotBegin, self.seekBegin, self.xferBegin) = (self.timer, self.timer, self.timer) + self.SwitchState(STATE_XFER) + self.SwitchColors('green') + + + + # make sure to keep the animation going! + if self.graphics: + self.root.after(20, self.Animate) + + def DoRequestStats(self): + seekTime = self.rotBegin - self.seekBegin + rotTime = self.xferBegin - self.rotBegin + xferTime = self.timer - self.xferBegin + totalTime = self.timer - self.seekBegin + + if self.compute == True: + print 'Block: %3d Seek:%3d Rotate:%3d Transfer:%3d Total:%4d' % (self.currentBlock, seekTime, rotTime, xferTime, totalTime) + + # if int(totalTime) != int(self.totalEst): + # print 'INTERNAL ERROR: estimate was', self.totalEst, 'whereas actual time to access block was', totalTime + # print 'Please report this bug and as much information as possible so as to make it easy to recreate. Thanks!' + + # update stats + self.seekTotal += seekTime + self.rotTotal += rotTime + self.xferTotal += xferTime + + + + def PrintStats(self): + if self.compute == True: + print '\nTOTALS Seek:%3d Rotate:%3d Transfer:%3d Total:%4d\n' % (self.seekTotal, self.rotTotal, self.xferTotal, self.timer) + +# END: class Disk + + + +# +# MAIN SIMULATOR +# +parser = OptionParser() +parser.add_option('-s', '--seed', default='0', help='Random seed', action='store', type='int', dest='seed') +parser.add_option('-a', '--addr', default='-1', help='Request list (comma-separated) [-1 -> use addrDesc]', action='store', type='string', dest='addr') +parser.add_option('-A', '--addrDesc', default='5,-1,0', help='Num requests, max request (-1->all), min request', action='store', type='string', dest='addrDesc') +parser.add_option('-S', '--seekSpeed', default='1', help='Speed of seek', action='store', type='string', dest='seekSpeed') +parser.add_option('-R', '--rotSpeed', default='1', help='Speed of rotation', action='store', type='string', dest='rotateSpeed') +parser.add_option('-p', '--policy', default='FIFO', help='Scheduling policy (FIFO, SSTF, SATF, BSATF)', action='store', type='string', dest='policy') +parser.add_option('-w', '--schedWindow', default=-1, help='Size of scheduling window (-1 -> all)', action='store', type='int', dest='window') +parser.add_option('-o', '--skewOffset', default=0, help='Amount of skew (in blocks)', action='store', type='int', dest='skew') +parser.add_option('-z', '--zoning', default='30,30,30', help='Angles between blocks on outer,middle,inner tracks', action='store', type='string', dest='zoning') +parser.add_option('-G', '--graphics', default=False, help='Turn on graphics', action='store_true', dest='graphics') +parser.add_option('-l', '--lateAddr', default='-1', help='Late: request list (comma-separated) [-1 -> random]', action='store', type='string', dest='lateAddr') +parser.add_option('-L', '--lateAddrDesc', default='0,-1,0', help='Num requests, max request (-1->all), min request', action='store', type='string', dest='lateAddrDesc') +parser.add_option('-c', '--compute', default=False, help='Compute the answers', action='store_true', dest='compute') +(options, args) = parser.parse_args() + +print 'OPTIONS seed', options.seed +print 'OPTIONS addr', options.addr +print 'OPTIONS addrDesc', options.addrDesc +print 'OPTIONS seekSpeed', options.seekSpeed +print 'OPTIONS rotateSpeed', options.rotateSpeed +print 'OPTIONS skew', options.skew +print 'OPTIONS window', options.window +print 'OPTIONS policy', options.policy +print 'OPTIONS compute', options.compute +print 'OPTIONS graphics', options.graphics +print 'OPTIONS zoning', options.zoning +print 'OPTIONS lateAddr', options.lateAddr +print 'OPTIONS lateAddrDesc', options.lateAddrDesc +print '' + +if options.window == 0: + print 'Scheduling window (%d) must be positive or -1 (which means a full window)' % options.window + sys.exit(1) + +if options.graphics and options.compute == False: + print '\nWARNING: Setting compute flag to True, as graphics are on\n' + options.compute = True + +# set up simulator info +d = Disk(addr=options.addr, addrDesc=options.addrDesc, lateAddr=options.lateAddr, lateAddrDesc=options.lateAddrDesc, + policy=options.policy, seekSpeed=Decimal(options.seekSpeed), rotateSpeed=Decimal(options.rotateSpeed), + skew=options.skew, window=options.window, compute=options.compute, graphics=options.graphics, zoning=options.zoning) + +# run simulation +d.Go()