《操作系统》的实验代码。
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.

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