《操作系统》的实验代码。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

719 lines
30 KiB

  1. #! /usr/bin/env python
  2. from Tkinter import *
  3. from types import *
  4. import math, random, time, sys, os
  5. from optparse import OptionParser
  6. from decimal import *
  7. MAXTRACKS = 1000
  8. # states that a request/disk go through
  9. STATE_NULL = 0
  10. STATE_SEEK = 1
  11. STATE_ROTATE = 2
  12. STATE_XFER = 3
  13. STATE_DONE = 4
  14. #
  15. # TODO
  16. # XXX transfer time
  17. # XXX satf
  18. # XXX skew
  19. # XXX scheduling window
  20. # XXX sstf
  21. # XXX specify requests vs. random requests in range
  22. # XXX add new requests as old ones complete (starvation)
  23. # XXX run in non-graphical mode
  24. # XXX better graphical display (show key, long lists of requests, more timings on screen)
  25. # XXX be able to do "pure" sequential
  26. # XXX add more blocks around outer tracks (zoning)
  27. # XXX simple flag to make scheduling window a fairness window (-F)
  28. # new algs to scan and c-scan the disk?
  29. #
  30. class Disk:
  31. def __init__(self, addr, addrDesc, lateAddr, lateAddrDesc,
  32. policy, seekSpeed, rotateSpeed, skew, window, compute,
  33. graphics, zoning):
  34. self.addr = addr
  35. self.addrDesc = addrDesc
  36. self.lateAddr = lateAddr
  37. self.lateAddrDesc = lateAddrDesc
  38. self.policy = policy
  39. self.seekSpeed = Decimal(seekSpeed)
  40. self.rotateSpeed = Decimal(rotateSpeed)
  41. self.skew = skew
  42. self.window = window
  43. self.compute = compute
  44. self.graphics = graphics
  45. self.zoning = zoning
  46. # figure out zones first, to figure out the max possible request
  47. self.InitBlockLayout()
  48. # figure out requests
  49. random.seed(options.seed)
  50. self.requests = self.MakeRequests(self.addr, self.addrDesc)
  51. self.lateRequests = self.MakeRequests(self.lateAddr, self.lateAddrDesc)
  52. # graphical startup
  53. self.width = 500
  54. if self.graphics:
  55. self.root = Tk()
  56. tmpLen = len(self.requests)
  57. if len(self.lateRequests) > 0:
  58. tmpLen += len(self.lateRequests)
  59. self.canvas = Canvas(self.root, width=410, height=460 + ((tmpLen / 20) * 20))
  60. self.canvas.pack()
  61. # fairness stuff
  62. if self.policy == 'BSATF' and self.window != -1:
  63. self.fairWindow = self.window
  64. else:
  65. self.fairWindow = -1
  66. print 'REQUESTS', self.requests
  67. print ''
  68. # for late requests
  69. self.lateCount = 0
  70. if len(self.lateRequests) > 0:
  71. print 'LATE REQUESTS', self.lateRequests
  72. print ''
  73. if self.compute == False:
  74. print ''
  75. print 'For the requests above, compute the seek, rotate, and transfer times.'
  76. print 'Use -c or the graphical mode (-G) to see the answers.'
  77. print ''
  78. # BINDINGS
  79. if self.graphics:
  80. self.root.bind('s', self.Start)
  81. self.root.bind('p', self.Pause)
  82. self.root.bind('q', self.Exit)
  83. # TRACK INFO
  84. self.tracks = {}
  85. self.trackWidth = 40
  86. self.tracks[0] = 140
  87. self.tracks[1] = self.tracks[0] - self.trackWidth
  88. self.tracks[2] = self.tracks[1] - self.trackWidth
  89. if (self.seekSpeed > 1 and self.trackWidth % self.seekSpeed != 0):
  90. print 'Seek speed (%d) must divide evenly into track width (%d)' % (self.seekSpeed, self.trackWidth)
  91. sys.exit(1)
  92. if self.seekSpeed < 1:
  93. x = (self.trackWidth / self.seekSpeed)
  94. y = int(float(self.trackWidth) / float(self.seekSpeed))
  95. if float(x) != float(y):
  96. print 'Seek speed (%d) must divide evenly into track width (%d)' % (self.seekSpeed, self.trackWidth)
  97. sys.exit(1)
  98. # DISK SURFACE
  99. self.cx = self.width/2.0
  100. self.cy = self.width/2.0
  101. if self.graphics:
  102. self.canvas.create_rectangle(self.cx-175, 30, self.cx - 20, 80, fill='gray', outline='black')
  103. self.platterSize = 320
  104. ps2 = self.platterSize / 2.0
  105. if self.graphics:
  106. self.canvas.create_oval(self.cx-ps2, self.cy-ps2, self.cx+ps2, self.cy + ps2, fill='darkgray', outline='black')
  107. for i in range(len(self.tracks)):
  108. t = self.tracks[i] - (self.trackWidth / 2.0)
  109. if self.graphics:
  110. self.canvas.create_oval(self.cx - t, self.cy - t, self.cx + t, self.cy + t, fill='', outline='black', width=1.0)
  111. # SPINDLE
  112. self.spindleX = self.cx
  113. self.spindleY = self.cy
  114. if self.graphics:
  115. self.spindleID = self.canvas.create_oval(self.spindleX-3, self.spindleY-3, self.spindleX+3, self.spindleY+3, fill='orange', outline='black')
  116. # DISK ARM
  117. self.armTrack = 0
  118. self.armSpeedBase = float(seekSpeed)
  119. self.armSpeed = float(seekSpeed)
  120. distFromSpindle = self.tracks[self.armTrack]
  121. self.armWidth = 20
  122. self.headWidth = 10
  123. self.armX = self.spindleX - (distFromSpindle * math.cos(math.radians(0)))
  124. self.armX1 = self.armX - self.armWidth
  125. self.armX2 = self.armX + self.armWidth
  126. self.armY1 = 50.0
  127. self.armY2 = self.width / 2.0
  128. self.headX1 = self.armX - self.headWidth
  129. self.headX2 = self.armX + self.headWidth
  130. self.headY1 = (self.width/2.0) - self.headWidth
  131. self.headY2 = (self.width/2.0) + self.headWidth
  132. if self.graphics:
  133. self.armID = self.canvas.create_rectangle(self.armX1, self.armY1, self.armX2, self.armY2, fill='gray', outline='black')
  134. self.headID = self.canvas.create_rectangle(self.headX1, self.headY1, self.headX2, self.headY2, fill='gray', outline='black')
  135. self.targetSize = 10.0
  136. if self.graphics:
  137. sz = self.targetSize
  138. self.targetID = self.canvas.create_oval(self.armX1-sz, self.armY1-sz, self.armX1+sz, self.armY1+sz, fill='orange', outline='')
  139. # IO QUEUE
  140. self.queueX = 20
  141. self.queueY = 450
  142. self.requestCount = 0
  143. self.requestQueue = []
  144. self.requestState = []
  145. self.queueBoxSize = 20
  146. self.queueBoxID = {}
  147. self.queueTxtID = {}
  148. # draw each box
  149. for index in range(len(self.requests)):
  150. self.AddQueueEntry(int(self.requests[index]), index)
  151. if self.graphics:
  152. self.canvas.create_text(self.queueX - 5, self.queueY - 20, anchor='w', text='Queue:')
  153. # scheduling window
  154. self.currWindow = self.window
  155. # draw current limits of queue
  156. if self.graphics:
  157. self.windowID = -1
  158. self.DrawWindow()
  159. # initial scheduling info
  160. self.currentIndex = -1
  161. self.currentBlock = -1
  162. # initial state of disk (vs seeking, rotating, transferring)
  163. self.state = STATE_NULL
  164. # DRAW BLOCKS on the TRACKS
  165. for bid in range(len(self.blockInfoList)):
  166. (track, angle, name) = self.blockInfoList[bid]
  167. if self.graphics:
  168. distFromSpindle = self.tracks[track]
  169. xc = self.spindleX + (distFromSpindle * math.cos(math.radians(angle)))
  170. yc = self.spindleY + (distFromSpindle * math.sin(math.radians(angle)))
  171. cid = self.canvas.create_text(xc, yc, text=name, anchor='center')
  172. else:
  173. cid = -1
  174. self.blockInfoList[bid] = (track, angle, name, cid)
  175. # angle of rotation
  176. self.angle = Decimal(0.0)
  177. # TIME INFO
  178. if self.graphics:
  179. self.timeID = self.canvas.create_text(10, 10, text='Time: 0.00', anchor='w')
  180. self.canvas.create_rectangle(95,0,200,18, fill='orange', outline='orange')
  181. self.seekID = self.canvas.create_text(100, 10, text='Seek: 0.00', anchor='w')
  182. self.canvas.create_rectangle(195,0,300,18, fill='lightblue', outline='lightblue')
  183. self.rotID = self.canvas.create_text(200, 10, text='Rotate: 0.00', anchor='w')
  184. self.canvas.create_rectangle(295,0,400,18, fill='green', outline='green')
  185. self.xferID = self.canvas.create_text(300, 10, text='Transfer: 0.00', anchor='w')
  186. self.canvas.create_text(320, 40, text='"s" to start', anchor='w')
  187. self.canvas.create_text(320, 60, text='"p" to pause', anchor='w')
  188. self.canvas.create_text(320, 80, text='"q" to quit', anchor='w')
  189. self.timer = 0
  190. # STATS
  191. self.seekTotal = 0.0
  192. self.rotTotal = 0.0
  193. self.xferTotal = 0.0
  194. # set up animation loop
  195. if self.graphics:
  196. self.doAnimate = True
  197. else:
  198. self.doAnimate = False
  199. self.isDone = False
  200. # call this to start simulation
  201. def Go(self):
  202. if options.graphics:
  203. self.root.mainloop()
  204. else:
  205. self.GetNextIO()
  206. while self.isDone == False:
  207. self.Animate()
  208. # crappy error message
  209. def PrintAddrDescMessage(self, value):
  210. print 'Bad address description (%s)' % value
  211. print 'The address description must be a comma-separated list of length three, without spaces.'
  212. print 'For example, "10,100,0" would indicate that 10 addresses should be generated, with'
  213. print '100 as the maximum value, and 0 as the minumum. A max of -1 means just use the highest'
  214. print 'possible value as the max address to generate.'
  215. sys.exit(1)
  216. #
  217. # ZONES AND BLOCK LAYOUT
  218. #
  219. def InitBlockLayout(self):
  220. self.blockInfoList = []
  221. self.blockToTrackMap = {}
  222. self.blockToAngleMap = {}
  223. self.tracksBeginEnd = {}
  224. self.blockAngleOffset = []
  225. zones = self.zoning.split(',')
  226. assert(len(zones) == 3)
  227. for i in range(len(zones)):
  228. self.blockAngleOffset.append(int(zones[i]) / 2)
  229. track = 0 # outer track
  230. angleOffset = 2 * self.blockAngleOffset[track]
  231. for angle in range(0, 360, angleOffset):
  232. block = angle / angleOffset
  233. self.blockToTrackMap[block] = track
  234. self.blockToAngleMap[block] = angle
  235. self.blockInfoList.append((track, angle, block))
  236. self.tracksBeginEnd[track] = (0, block)
  237. pblock = block + 1
  238. track = 1 # middle track
  239. skew = self.skew
  240. angleOffset = 2 * self.blockAngleOffset[track]
  241. for angle in range(0, 360, angleOffset):
  242. block = (angle / angleOffset) + pblock
  243. self.blockToTrackMap[block] = track
  244. self.blockToAngleMap[block] = angle + (angleOffset * skew)
  245. self.blockInfoList.append((track, angle + (angleOffset * skew), block))
  246. self.tracksBeginEnd[track] = (pblock, block)
  247. pblock = block + 1
  248. track = 2 # inner track
  249. skew = 2 * self.skew
  250. angleOffset = 2 * self.blockAngleOffset[track]
  251. for angle in range(0, 360, angleOffset):
  252. block = (angle / angleOffset) + pblock
  253. self.blockToTrackMap[block] = track
  254. self.blockToAngleMap[block] = angle + (angleOffset * skew)
  255. self.blockInfoList.append((track, angle + (angleOffset * skew), block))
  256. self.tracksBeginEnd[track] = (pblock, block)
  257. self.maxBlock = pblock
  258. # print 'MAX BLOCK:', self.maxBlock
  259. # adjust angle to starting position relative
  260. for i in self.blockToAngleMap:
  261. self.blockToAngleMap[i] = (self.blockToAngleMap[i] + 180) % 360
  262. # print 'btoa map', self.blockToAngleMap
  263. # print 'btot map', self.blockToTrackMap
  264. # print 'bao', self.blockAngleOffset
  265. def MakeRequests(self, addr, addrDesc):
  266. (numRequests, maxRequest, minRequest) = (0, 0, 0)
  267. if addr == '-1':
  268. # first extract values from descriptor
  269. desc = addrDesc.split(',')
  270. if len(desc) != 3:
  271. self.PrintAddrDescMessage(addrDesc)
  272. (numRequests, maxRequest, minRequest) = (int(desc[0]), int(desc[1]), int(desc[2]))
  273. if maxRequest == -1:
  274. maxRequest = self.maxBlock
  275. # now make list
  276. tmpList = []
  277. for i in range(numRequests):
  278. tmpList.append(int(random.random() * maxRequest) + minRequest)
  279. return tmpList
  280. else:
  281. return addr.split(',')
  282. #
  283. # BUTTONS
  284. #
  285. def Start(self, event):
  286. self.GetNextIO()
  287. self.doAnimate = True
  288. self.Animate()
  289. def Pause(self, event):
  290. if self.doAnimate == False:
  291. self.doAnimate = True
  292. else:
  293. self.doAnimate = False
  294. def Exit(self, event):
  295. sys.exit(0)
  296. #
  297. # CORE SIMULATION and ANIMATION
  298. #
  299. def UpdateTime(self):
  300. if self.graphics:
  301. self.canvas.itemconfig(self.timeID, text='Time: ' + str(self.timer))
  302. self.canvas.itemconfig(self.seekID, text='Seek: ' + str(self.seekTotal))
  303. self.canvas.itemconfig(self.rotID, text='Rotate: ' + str(self.rotTotal))
  304. self.canvas.itemconfig(self.xferID, text='Transfer: ' + str(self.xferTotal))
  305. def AddRequest(self, block):
  306. self.AddQueueEntry(block, len(self.requestQueue))
  307. def QueueMap(self, index):
  308. numPerRow = 400 / self.queueBoxSize
  309. return (index % numPerRow, index / numPerRow)
  310. def DrawWindow(self):
  311. if self.window == -1:
  312. return
  313. (col, row) = self.QueueMap(self.currWindow)
  314. if col == 0:
  315. (col, row) = (20, row - 1)
  316. if self.windowID != -1:
  317. self.canvas.delete(self.windowID)
  318. self.windowID = self.canvas.create_line(self.queueX + (col * 20) - 10, self.queueY - 13 + (row * 20),
  319. self.queueX + (col * 20) - 10, self.queueY + 13 + (row * 20), width=2)
  320. def AddQueueEntry(self, block, index):
  321. self.requestQueue.append((block, index))
  322. self.requestState.append(STATE_NULL)
  323. if self.graphics:
  324. (col, row) = self.QueueMap(index)
  325. sizeHalf = self.queueBoxSize / 2.0
  326. (cx, cy) = (self.queueX + (col * self.queueBoxSize), self.queueY + (row * self.queueBoxSize))
  327. self.queueBoxID[index] = self.canvas.create_rectangle(cx - sizeHalf, cy - sizeHalf, cx + sizeHalf, cy + sizeHalf, fill='white')
  328. self.queueTxtID[index] = self.canvas.create_text(cx, cy, anchor='center', text=str(block))
  329. def SwitchColors(self, c):
  330. if self.graphics:
  331. self.canvas.itemconfig(self.queueBoxID[self.currentIndex], fill=c)
  332. self.canvas.itemconfig(self.targetID, fill=c)
  333. def SwitchState(self, newState):
  334. self.state = newState
  335. self.requestState[self.currentIndex] = newState
  336. def RadiallyCloseTo(self, a1, a2):
  337. if a1 > a2:
  338. v = a1 - a2
  339. else:
  340. v = a2 - a1
  341. if v < self.rotateSpeed:
  342. return True
  343. return False
  344. def DoneWithTransfer(self):
  345. angleOffset = self.blockAngleOffset[self.armTrack]
  346. # if int(self.angle) == (self.blockToAngleMap[self.currentBlock] + angleOffset) % 360:
  347. if self.RadiallyCloseTo(self.angle, Decimal((self.blockToAngleMap[self.currentBlock] + angleOffset) % 360)):
  348. # print 'END TRANSFER', self.angle, self.timer
  349. self.SwitchState(STATE_DONE)
  350. self.requestCount += 1
  351. return True
  352. return False
  353. def DoneWithRotation(self):
  354. angleOffset = self.blockAngleOffset[self.armTrack]
  355. # XXX there is a weird bug in here
  356. # print self.timer, 'ROTATE:: ', self.currentBlock, 'currangle: ', self.angle, ' - mapangle: ', self.blockToAngleMap[self.currentBlock]
  357. # print ' angleOffset ', angleOffset
  358. # print ' blockMap ', (self.blockToAngleMap[self.currentBlock] - angleOffset) % 360
  359. # print ' self.angle ', self.angle, int(self.angle)
  360. # if int(self.angle) == (self.blockToAngleMap[self.currentBlock] - angleOffset) % 360:
  361. if self.RadiallyCloseTo(self.angle, Decimal((self.blockToAngleMap[self.currentBlock] - angleOffset) % 360)):
  362. self.SwitchState(STATE_XFER)
  363. # print ' --> DONE WITH ROTATION!', self.timer
  364. return True
  365. return False
  366. def PlanSeek(self, track):
  367. self.seekBegin = self.timer
  368. self.SwitchColors('orange')
  369. self.SwitchState(STATE_SEEK)
  370. if track == self.armTrack:
  371. self.rotBegin = self.timer
  372. self.SwitchColors('lightblue')
  373. self.SwitchState(STATE_ROTATE)
  374. return
  375. self.armTarget = track
  376. self.armTargetX1 = self.spindleX - self.tracks[track] - (self.trackWidth / 2.0)
  377. if track >= self.armTrack:
  378. self.armSpeed = self.armSpeedBase
  379. else:
  380. self.armSpeed = - self.armSpeedBase
  381. def DoneWithSeek(self):
  382. # move the disk arm
  383. self.armX1 += self.armSpeed
  384. self.armX2 += self.armSpeed
  385. self.headX1 += self.armSpeed
  386. self.headX2 += self.armSpeed
  387. # update it on screen
  388. if self.graphics:
  389. self.canvas.coords(self.armID, self.armX1, self.armY1, self.armX2, self.armY2)
  390. self.canvas.coords(self.headID, self.headX1, self.headY1, self.headX2, self.headY2)
  391. # check if done
  392. if (self.armSpeed > 0.0 and self.armX1 >= self.armTargetX1) or (self.armSpeed < 0.0 and self.armX1 <= self.armTargetX1):
  393. self.armTrack = self.armTarget
  394. return True
  395. return False
  396. def DoSATF(self, rList):
  397. minBlock = -1
  398. minIndex = -1
  399. minEst = -1
  400. # print '**** DoSATF ****', rList
  401. for (block, index) in rList:
  402. if self.requestState[index] == STATE_DONE:
  403. continue
  404. track = self.blockToTrackMap[block]
  405. angle = self.blockToAngleMap[block]
  406. # print 'track', track, 'angle', angle
  407. # estimate seek time
  408. dist = int(math.fabs(self.armTrack - track))
  409. seekEst = Decimal(self.trackWidth / self.armSpeedBase) * dist
  410. # estimate rotate time
  411. angleOffset = self.blockAngleOffset[track]
  412. angleAtArrival = (Decimal(self.angle) + (seekEst * self.rotateSpeed))
  413. while angleAtArrival > 360.0:
  414. angleAtArrival -= 360.0
  415. rotDist = Decimal((angle - angleOffset) - angleAtArrival)
  416. while rotDist > 360.0:
  417. rotDist -= Decimal(360.0)
  418. while rotDist < 0.0:
  419. rotDist += Decimal(360.0)
  420. rotEst = rotDist / self.rotateSpeed
  421. # finally, transfer
  422. xferEst = (Decimal(angleOffset) * Decimal(2.0)) / self.rotateSpeed
  423. totalEst = seekEst + rotEst + xferEst
  424. # should probably pick one on same track in case of a TIE
  425. if minEst == -1 or totalEst < minEst:
  426. minEst = totalEst
  427. minBlock = block
  428. minIndex = index
  429. # END loop
  430. # when done
  431. self.totalEst = minEst
  432. assert(minBlock != -1)
  433. assert(minIndex != -1)
  434. return (minBlock, minIndex)
  435. #
  436. # actually doesn't quite do SSTF
  437. # just finds all the blocks on the nearest track
  438. # (whatever that may be) and returns it as a list
  439. #
  440. def DoSSTF(self, rList):
  441. minDist = MAXTRACKS
  442. minBlock = -1
  443. trackList = [] # all the blocks on a track
  444. for (block, index) in rList:
  445. if self.requestState[index] == STATE_DONE:
  446. continue
  447. track = self.blockToTrackMap[block]
  448. dist = int(math.fabs(self.armTrack - track))
  449. if dist < minDist:
  450. trackList = []
  451. trackList.append((block, index))
  452. minDist = dist
  453. elif dist == minDist:
  454. trackList.append((block, index))
  455. assert(trackList != [])
  456. return trackList
  457. def UpdateWindow(self):
  458. if self.fairWindow == -1 and self.currWindow > 0 and self.currWindow < len(self.requestQueue):
  459. self.currWindow += 1
  460. if self.graphics:
  461. self.DrawWindow()
  462. def GetWindow(self):
  463. if self.currWindow <= -1:
  464. return len(self.requestQueue)
  465. else:
  466. if self.fairWindow != -1:
  467. if self.requestCount > 0 and (self.requestCount % self.fairWindow == 0):
  468. self.currWindow = self.currWindow + self.fairWindow
  469. if self.currWindow > len(self.requestQueue):
  470. self.currWindow = len(self.requestQueue)
  471. if self.graphics:
  472. self.DrawWindow()
  473. return self.currWindow
  474. else:
  475. return self.currWindow
  476. def GetNextIO(self):
  477. # check if done: if so, print stats and end animation
  478. if self.requestCount == len(self.requestQueue):
  479. self.UpdateTime()
  480. self.PrintStats()
  481. self.doAnimate = False
  482. self.isDone = True
  483. return
  484. # do policy: should set currentBlock,
  485. if self.policy == 'FIFO':
  486. (self.currentBlock, self.currentIndex) = self.requestQueue[self.requestCount]
  487. self.DoSATF(self.requestQueue[self.requestCount:self.requestCount+1])
  488. elif self.policy == 'SATF' or self.policy == 'BSATF':
  489. (self.currentBlock, self.currentIndex) = self.DoSATF(self.requestQueue[0:self.GetWindow()])
  490. elif self.policy == 'SSTF':
  491. # first, find all the blocks on a given track (given window constraints)
  492. trackList = self.DoSSTF(self.requestQueue[0:self.GetWindow()])
  493. # then, do SATF on those blocks (otherwise, will not do them in obvious order)
  494. (self.currentBlock, self.currentIndex) = self.DoSATF(trackList)
  495. else:
  496. print 'policy (%s) not implemented' % self.policy
  497. sys.exit(1)
  498. # once best block is decided, go ahead and do the seek
  499. self.PlanSeek(self.blockToTrackMap[self.currentBlock])
  500. # add another block?
  501. if len(self.lateRequests) > 0 and self.lateCount < len(self.lateRequests):
  502. self.AddRequest(self.lateRequests[self.lateCount])
  503. self.lateCount += 1
  504. def Animate(self):
  505. if self.graphics == True and self.doAnimate == False:
  506. self.root.after(20, self.Animate)
  507. return
  508. # timer
  509. self.timer += 1
  510. self.UpdateTime()
  511. # see which blocks are rotating on the disk
  512. # print 'SELF ANGLE', self.angle
  513. self.angle = Decimal(self.angle + self.rotateSpeed)
  514. if self.angle >= 360.0:
  515. self.angle = Decimal(0.0)
  516. # move the blocks
  517. if self.graphics:
  518. for (track, angle, name, cid) in self.blockInfoList:
  519. distFromSpindle = self.tracks[track]
  520. na = angle - self.angle
  521. xc = self.spindleX + (distFromSpindle * math.cos(math.radians(na)))
  522. yc = self.spindleY + (distFromSpindle * math.sin(math.radians(na)))
  523. if self.graphics:
  524. self.canvas.coords(cid, xc, yc)
  525. if self.currentBlock == name:
  526. sz = self.targetSize
  527. self.canvas.coords(self.targetID, xc-sz, yc-sz, xc+sz, yc+sz)
  528. # move the arm OR wait for a rotational delay
  529. if self.state == STATE_SEEK:
  530. if self.DoneWithSeek():
  531. self.rotBegin = self.timer
  532. self.SwitchState(STATE_ROTATE)
  533. self.SwitchColors('lightblue')
  534. if self.state == STATE_ROTATE:
  535. # check for read (disk arm must be settled)
  536. if self.DoneWithRotation():
  537. self.xferBegin = self.timer
  538. self.SwitchState(STATE_XFER)
  539. self.SwitchColors('green')
  540. if self.state == STATE_XFER:
  541. if self.DoneWithTransfer():
  542. self.DoRequestStats()
  543. self.SwitchState(STATE_DONE)
  544. self.SwitchColors('red')
  545. self.UpdateWindow()
  546. currentBlock = self.currentBlock
  547. self.GetNextIO()
  548. nextBlock = self.currentBlock
  549. if self.blockToTrackMap[currentBlock] == self.blockToTrackMap[nextBlock]:
  550. if (currentBlock == self.tracksBeginEnd[self.armTrack][1] and nextBlock == self.tracksBeginEnd[self.armTrack][0]) or (currentBlock + 1 == nextBlock):
  551. # need a special case here: to handle when we stay in transfer mode
  552. (self.rotBegin, self.seekBegin, self.xferBegin) = (self.timer, self.timer, self.timer)
  553. self.SwitchState(STATE_XFER)
  554. self.SwitchColors('green')
  555. # make sure to keep the animation going!
  556. if self.graphics:
  557. self.root.after(20, self.Animate)
  558. def DoRequestStats(self):
  559. seekTime = self.rotBegin - self.seekBegin
  560. rotTime = self.xferBegin - self.rotBegin
  561. xferTime = self.timer - self.xferBegin
  562. totalTime = self.timer - self.seekBegin
  563. if self.compute == True:
  564. print 'Block: %3d Seek:%3d Rotate:%3d Transfer:%3d Total:%4d' % (self.currentBlock, seekTime, rotTime, xferTime, totalTime)
  565. # if int(totalTime) != int(self.totalEst):
  566. # print 'INTERNAL ERROR: estimate was', self.totalEst, 'whereas actual time to access block was', totalTime
  567. # print 'Please report this bug and as much information as possible so as to make it easy to recreate. Thanks!'
  568. # update stats
  569. self.seekTotal += seekTime
  570. self.rotTotal += rotTime
  571. self.xferTotal += xferTime
  572. def PrintStats(self):
  573. if self.compute == True:
  574. print '\nTOTALS Seek:%3d Rotate:%3d Transfer:%3d Total:%4d\n' % (self.seekTotal, self.rotTotal, self.xferTotal, self.timer)
  575. # END: class Disk
  576. #
  577. # MAIN SIMULATOR
  578. #
  579. parser = OptionParser()
  580. parser.add_option('-s', '--seed', default='0', help='Random seed', action='store', type='int', dest='seed')
  581. parser.add_option('-a', '--addr', default='-1', help='Request list (comma-separated) [-1 -> use addrDesc]', action='store', type='string', dest='addr')
  582. parser.add_option('-A', '--addrDesc', default='5,-1,0', help='Num requests, max request (-1->all), min request', action='store', type='string', dest='addrDesc')
  583. parser.add_option('-S', '--seekSpeed', default='1', help='Speed of seek', action='store', type='string', dest='seekSpeed')
  584. parser.add_option('-R', '--rotSpeed', default='1', help='Speed of rotation', action='store', type='string', dest='rotateSpeed')
  585. parser.add_option('-p', '--policy', default='FIFO', help='Scheduling policy (FIFO, SSTF, SATF, BSATF)', action='store', type='string', dest='policy')
  586. parser.add_option('-w', '--schedWindow', default=-1, help='Size of scheduling window (-1 -> all)', action='store', type='int', dest='window')
  587. parser.add_option('-o', '--skewOffset', default=0, help='Amount of skew (in blocks)', action='store', type='int', dest='skew')
  588. parser.add_option('-z', '--zoning', default='30,30,30', help='Angles between blocks on outer,middle,inner tracks', action='store', type='string', dest='zoning')
  589. parser.add_option('-G', '--graphics', default=False, help='Turn on graphics', action='store_true', dest='graphics')
  590. parser.add_option('-l', '--lateAddr', default='-1', help='Late: request list (comma-separated) [-1 -> random]', action='store', type='string', dest='lateAddr')
  591. parser.add_option('-L', '--lateAddrDesc', default='0,-1,0', help='Num requests, max request (-1->all), min request', action='store', type='string', dest='lateAddrDesc')
  592. parser.add_option('-c', '--compute', default=False, help='Compute the answers', action='store_true', dest='compute')
  593. (options, args) = parser.parse_args()
  594. print 'OPTIONS seed', options.seed
  595. print 'OPTIONS addr', options.addr
  596. print 'OPTIONS addrDesc', options.addrDesc
  597. print 'OPTIONS seekSpeed', options.seekSpeed
  598. print 'OPTIONS rotateSpeed', options.rotateSpeed
  599. print 'OPTIONS skew', options.skew
  600. print 'OPTIONS window', options.window
  601. print 'OPTIONS policy', options.policy
  602. print 'OPTIONS compute', options.compute
  603. print 'OPTIONS graphics', options.graphics
  604. print 'OPTIONS zoning', options.zoning
  605. print 'OPTIONS lateAddr', options.lateAddr
  606. print 'OPTIONS lateAddrDesc', options.lateAddrDesc
  607. print ''
  608. if options.window == 0:
  609. print 'Scheduling window (%d) must be positive or -1 (which means a full window)' % options.window
  610. sys.exit(1)
  611. if options.graphics and options.compute == False:
  612. print '\nWARNING: Setting compute flag to True, as graphics are on\n'
  613. options.compute = True
  614. # set up simulator info
  615. d = Disk(addr=options.addr, addrDesc=options.addrDesc, lateAddr=options.lateAddr, lateAddrDesc=options.lateAddrDesc,
  616. policy=options.policy, seekSpeed=Decimal(options.seekSpeed), rotateSpeed=Decimal(options.rotateSpeed),
  617. skew=options.skew, window=options.window, compute=options.compute, graphics=options.graphics, zoning=options.zoning)
  618. # run simulation
  619. d.Go()