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

470 lines
13 KiB

  1. #! /usr/bin/env python
  2. import random
  3. from optparse import OptionParser
  4. DEBUG = False
  5. def dprint(str):
  6. if DEBUG:
  7. print str
  8. printOps = True
  9. printState = True
  10. printFinal = True
  11. class bitmap:
  12. def __init__(self, size):
  13. self.size = size
  14. self.bmap = []
  15. for num in range(size):
  16. self.bmap.append(0)
  17. def alloc(self):
  18. for num in range(len(self.bmap)):
  19. if self.bmap[num] == 0:
  20. self.bmap[num] = 1
  21. return num
  22. return -1
  23. def free(self, num):
  24. assert(self.bmap[num] == 1)
  25. self.bmap[num] = 0
  26. def markAllocated(self, num):
  27. assert(self.bmap[num] == 0)
  28. self.bmap[num] = 1
  29. def dump(self):
  30. s = ''
  31. for i in range(len(self.bmap)):
  32. s += str(self.bmap[i])
  33. return s
  34. class block:
  35. def __init__(self, ftype):
  36. assert(ftype == 'd' or ftype == 'f' or ftype == 'free')
  37. self.ftype = ftype
  38. # only for directories, properly a subclass but who cares
  39. self.dirUsed = 0
  40. self.maxUsed = 32
  41. self.dirList = []
  42. self.data = ''
  43. def dump(self):
  44. if self.ftype == 'free':
  45. return '[]'
  46. elif self.ftype == 'd':
  47. rc = ''
  48. for d in self.dirList:
  49. # d is of the form ('name', inum)
  50. short = '(%s,%s)' % (d[0], d[1])
  51. if rc == '':
  52. rc = short
  53. else:
  54. rc += ' ' + short
  55. return '['+rc+']'
  56. # return '%s' % self.dirList
  57. else:
  58. return '[%s]' % self.data
  59. def setType(self, ftype):
  60. assert(self.ftype == 'free')
  61. self.ftype = ftype
  62. def addData(self, data):
  63. assert(self.ftype == 'f')
  64. self.data = data
  65. def getNumEntries(self):
  66. assert(self.ftype == 'd')
  67. return self.dirUsed
  68. def getFreeEntries(self):
  69. assert(self.ftype == 'd')
  70. return self.maxUsed - self.dirUsed
  71. def getEntry(self, num):
  72. assert(self.ftype == 'd')
  73. assert(num < self.dirUsed)
  74. return self.dirList[num]
  75. def addDirEntry(self, name, inum):
  76. assert(self.ftype == 'd')
  77. self.dirList.append((name, inum))
  78. self.dirUsed += 1
  79. assert(self.dirUsed <= self.maxUsed)
  80. def delDirEntry(self, name):
  81. assert(self.ftype == 'd')
  82. tname = name.split('/')
  83. dname = tname[len(tname) - 1]
  84. for i in range(len(self.dirList)):
  85. if self.dirList[i][0] == dname:
  86. self.dirList.pop(i)
  87. self.dirUsed -= 1
  88. return
  89. assert(1 == 0)
  90. def dirEntryExists(self, name):
  91. assert(self.ftype == 'd')
  92. for d in self.dirList:
  93. if name == d[0]:
  94. return True
  95. return False
  96. def free(self):
  97. assert(self.ftype != 'free')
  98. if self.ftype == 'd':
  99. # check for only dot, dotdot here
  100. assert(self.dirUsed == 2)
  101. self.dirUsed = 0
  102. self.data = ''
  103. self.ftype = 'free'
  104. class inode:
  105. def __init__(self, ftype='free', addr=-1, refCnt=1):
  106. self.setAll(ftype, addr, refCnt)
  107. def setAll(self, ftype, addr, refCnt):
  108. assert(ftype == 'd' or ftype == 'f' or ftype == 'free')
  109. self.ftype = ftype
  110. self.addr = addr
  111. self.refCnt = refCnt
  112. def incRefCnt(self):
  113. self.refCnt += 1
  114. def decRefCnt(self):
  115. self.refCnt -= 1
  116. def getRefCnt(self):
  117. return self.refCnt
  118. def setType(self, ftype):
  119. assert(ftype == 'd' or ftype == 'f' or ftype == 'free')
  120. self.ftype = ftype
  121. def setAddr(self, block):
  122. self.addr = block
  123. def getSize(self):
  124. if self.addr == -1:
  125. return 0
  126. else:
  127. return 1
  128. def getAddr(self):
  129. return self.addr
  130. def getType(self):
  131. return self.ftype
  132. def free(self):
  133. self.ftype = 'free'
  134. self.addr = -1
  135. class fs:
  136. def __init__(self, numInodes, numData):
  137. self.numInodes = numInodes
  138. self.numData = numData
  139. self.ibitmap = bitmap(self.numInodes)
  140. self.inodes = []
  141. for i in range(self.numInodes):
  142. self.inodes.append(inode())
  143. self.dbitmap = bitmap(self.numData)
  144. self.data = []
  145. for i in range(self.numData):
  146. self.data.append(block('free'))
  147. # root inode
  148. self.ROOT = 0
  149. # create root directory
  150. self.ibitmap.markAllocated(self.ROOT)
  151. self.inodes[self.ROOT].setAll('d', 0, 2)
  152. self.dbitmap.markAllocated(self.ROOT)
  153. self.data[0].setType('d')
  154. self.data[0].addDirEntry('.', self.ROOT)
  155. self.data[0].addDirEntry('..', self.ROOT)
  156. # these is just for the fake workload generator
  157. self.files = []
  158. self.dirs = ['/']
  159. self.nameToInum = {'/':self.ROOT}
  160. def dump(self):
  161. print 'inode bitmap ', self.ibitmap.dump()
  162. print 'inodes ',
  163. for i in range(0,self.numInodes):
  164. ftype = self.inodes[i].getType()
  165. if ftype == 'free':
  166. print '[]',
  167. else:
  168. print '[%s a:%s r:%d]' % (ftype, self.inodes[i].getAddr(), self.inodes[i].getRefCnt()),
  169. print ''
  170. print 'data bitmap ', self.dbitmap.dump()
  171. print 'data ',
  172. for i in range(self.numData):
  173. print self.data[i].dump(),
  174. print ''
  175. def makeName(self):
  176. p = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
  177. return p[int(random.random() * len(p))]
  178. def inodeAlloc(self):
  179. return self.ibitmap.alloc()
  180. def inodeFree(self, inum):
  181. self.ibitmap.free(inum)
  182. self.inodes[inum].free()
  183. def dataAlloc(self):
  184. return self.dbitmap.alloc()
  185. def dataFree(self, bnum):
  186. self.dbitmap.free(bnum)
  187. self.data[bnum].free()
  188. def getParent(self, name):
  189. tmp = name.split('/')
  190. if len(tmp) == 2:
  191. return '/'
  192. pname = ''
  193. for i in range(1, len(tmp)-1):
  194. pname = pname + '/' + tmp[i]
  195. return pname
  196. def deleteFile(self, tfile):
  197. if printOps:
  198. print 'unlink("%s");' % tfile
  199. inum = self.nameToInum[tfile]
  200. # YOUR CODE, YOUR ID
  201. # IF inode.refcnt ==1, THEN free data blocks first, then free inode, ELSE dec indoe.refcnt
  202. # remove from parent directory: delete from parent inum, delete from parent addr
  203. # DONE
  204. # finally, remove from files list
  205. self.files.remove(tfile)
  206. return 0
  207. def createLink(self, target, newfile, parent):
  208. # YOUR CODE, YOUR ID
  209. # find info about parent
  210. # is there room in the parent directory?
  211. # if the newfile was already in parent dir?
  212. # now, find inumber of target
  213. # inc parent ref count
  214. # now add to directory
  215. # DONE
  216. return tinum
  217. def createFile(self, parent, newfile, ftype):
  218. # YOUR CODE, YOUR ID
  219. # find info about parent
  220. # is there room in the parent directory?
  221. # have to make sure file name is unique
  222. # find free inode
  223. # if a directory, have to allocate directory block for basic (., ..) info
  224. # now ok to init inode properly
  225. # inc parent ref count
  226. # and add to directory of parent
  227. # DONE
  228. return inum
  229. def writeFile(self, tfile, data):
  230. inum = self.nameToInum[tfile]
  231. curSize = self.inodes[inum].getSize()
  232. dprint('writeFile: inum:%d cursize:%d refcnt:%d' % (inum, curSize, self.inodes[inum].getRefCnt()))
  233. # YOUR CODE, YOUR ID
  234. # file is full?
  235. # no data blocks left
  236. # write file data
  237. # DONE
  238. if printOps:
  239. print 'fd=open("%s", O_WRONLY|O_APPEND); write(fd, buf, BLOCKSIZE); close(fd);' % tfile
  240. return 0
  241. def doDelete(self):
  242. dprint('doDelete')
  243. if len(self.files) == 0:
  244. return -1
  245. dfile = self.files[int(random.random() * len(self.files))]
  246. dprint('try delete(%s)' % dfile)
  247. return self.deleteFile(dfile)
  248. def doLink(self):
  249. dprint('doLink')
  250. if len(self.files) == 0:
  251. return -1
  252. parent = self.dirs[int(random.random() * len(self.dirs))]
  253. nfile = self.makeName()
  254. # pick random target
  255. target = self.files[int(random.random() * len(self.files))]
  256. # get full name of newfile
  257. if parent == '/':
  258. fullName = parent + nfile
  259. else:
  260. fullName = parent + '/' + nfile
  261. dprint('try createLink(%s %s %s)' % (target, nfile, parent))
  262. inum = self.createLink(target, nfile, parent)
  263. if inum >= 0:
  264. self.files.append(fullName)
  265. self.nameToInum[fullName] = inum
  266. if printOps:
  267. print 'link("%s", "%s");' % (target, fullName)
  268. return 0
  269. return -1
  270. def doCreate(self, ftype):
  271. dprint('doCreate')
  272. parent = self.dirs[int(random.random() * len(self.dirs))]
  273. nfile = self.makeName()
  274. if ftype == 'd':
  275. tlist = self.dirs
  276. else:
  277. tlist = self.files
  278. if parent == '/':
  279. fullName = parent + nfile
  280. else:
  281. fullName = parent + '/' + nfile
  282. dprint('try createFile(%s %s %s)' % (parent, nfile, ftype))
  283. inum = self.createFile(parent, nfile, ftype)
  284. if inum >= 0:
  285. tlist.append(fullName)
  286. self.nameToInum[fullName] = inum
  287. if parent == '/':
  288. parent = ''
  289. if ftype == 'd':
  290. if printOps:
  291. print 'mkdir("%s/%s");' % (parent, nfile)
  292. else:
  293. if printOps:
  294. print 'creat("%s/%s");' % (parent, nfile)
  295. return 0
  296. return -1
  297. def doAppend(self):
  298. dprint('doAppend')
  299. if len(self.files) == 0:
  300. return -1
  301. afile = self.files[int(random.random() * len(self.files))]
  302. dprint('try writeFile(%s)' % afile)
  303. data = chr(ord('a') + int(random.random() * 26))
  304. rc = self.writeFile(afile, data)
  305. return rc
  306. def run(self, numRequests):
  307. self.percentMkdir = 0.40
  308. self.percentWrite = 0.40
  309. self.percentDelete = 0.20
  310. self.numRequests = numRequests
  311. print 'Initial state'
  312. print ''
  313. self.dump()
  314. print ''
  315. for i in range(numRequests):
  316. if printOps == False:
  317. print 'Which operation took place?'
  318. rc = -1
  319. while rc == -1:
  320. r = random.random()
  321. if r < 0.3:
  322. rc = self.doAppend()
  323. dprint('doAppend rc:%d' % rc)
  324. elif r < 0.5:
  325. rc = self.doDelete()
  326. dprint('doDelete rc:%d' % rc)
  327. elif r < 0.7:
  328. rc = self.doLink()
  329. dprint('doLink rc:%d' % rc)
  330. else:
  331. if random.random() < 0.75:
  332. rc = self.doCreate('f')
  333. dprint('doCreate(f) rc:%d' % rc)
  334. else:
  335. rc = self.doCreate('d')
  336. dprint('doCreate(d) rc:%d' % rc)
  337. if printState == True:
  338. print ''
  339. self.dump()
  340. print ''
  341. else:
  342. print ''
  343. print ' State of file system (inode bitmap, inodes, data bitmap, data)?'
  344. print ''
  345. if printFinal:
  346. print ''
  347. print 'Summary of files, directories::'
  348. print ''
  349. print ' Files: ', self.files
  350. print ' Directories:', self.dirs
  351. print ''
  352. #
  353. # main program
  354. #
  355. parser = OptionParser()
  356. parser.add_option('-s', '--seed', default=0, help='the random seed', action='store', type='int', dest='seed')
  357. parser.add_option('-i', '--numInodes', default=8, help='number of inodes in file system', action='store', type='int', dest='numInodes')
  358. parser.add_option('-d', '--numData', default=8, help='number of data blocks in file system', action='store', type='int', dest='numData')
  359. parser.add_option('-n', '--numRequests', default=10, help='number of requests to simulate', action='store', type='int', dest='numRequests')
  360. parser.add_option('-r', '--reverse', default=False, help='instead of printing state, print ops', action='store_true', dest='reverse')
  361. parser.add_option('-p', '--printFinal', default=False, help='print the final set of files/dirs', action='store_true', dest='printFinal')
  362. (options, args) = parser.parse_args()
  363. print 'ARG seed', options.seed
  364. print 'ARG numInodes', options.numInodes
  365. print 'ARG numData', options.numData
  366. print 'ARG numRequests', options.numRequests
  367. print 'ARG reverse', options.reverse
  368. print 'ARG printFinal', options.printFinal
  369. print ''
  370. random.seed(options.seed)
  371. if options.reverse:
  372. printState = False
  373. printOps = True
  374. else:
  375. printState = True
  376. printOps = False
  377. printOps = True
  378. printState = True
  379. printFinal = options.printFinal
  380. #
  381. # have to generate RANDOM requests to the file system
  382. # that are VALID!
  383. #
  384. f = fs(options.numInodes, options.numData)
  385. #
  386. # ops: mkdir rmdir : create delete : append write
  387. #
  388. f.run(options.numRequests)