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

551 lines
17 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. p = ['b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 's', 't', 'v', 'w', 'x', 'y', 'z']
  179. f = p[int(random.random() * len(p))]
  180. p = ['a', 'e', 'i', 'o', 'u']
  181. s = p[int(random.random() * len(p))]
  182. p = ['b', 'c', 'd', 'f', 'g', 'j', 'k', 'l', 'm', 'n', 'p', 's', 't', 'v', 'w', 'x', 'y', 'z']
  183. l = p[int(random.random() * len(p))]
  184. return '%c%c%c' % (f, s, l)
  185. def inodeAlloc(self):
  186. return self.ibitmap.alloc()
  187. def inodeFree(self, inum):
  188. self.ibitmap.free(inum)
  189. self.inodes[inum].free()
  190. def dataAlloc(self):
  191. return self.dbitmap.alloc()
  192. def dataFree(self, bnum):
  193. self.dbitmap.free(bnum)
  194. self.data[bnum].free()
  195. def getParent(self, name):
  196. tmp = name.split('/')
  197. if len(tmp) == 2:
  198. return '/'
  199. pname = ''
  200. for i in range(1, len(tmp)-1):
  201. pname = pname + '/' + tmp[i]
  202. return pname
  203. def deleteFile(self, tfile):
  204. if printOps:
  205. print 'unlink("%s");' % tfile
  206. inum = self.nameToInum[tfile]
  207. if self.inodes[inum].getRefCnt() == 1:
  208. # free data blocks first
  209. dblock = self.inodes[inum].getAddr()
  210. if dblock != -1:
  211. self.dataFree(dblock)
  212. # then free inode
  213. self.inodeFree(inum)
  214. else:
  215. self.inodes[inum].decRefCnt()
  216. # remove from parent directory
  217. parent = self.getParent(tfile)
  218. # print '--> delete from parent', parent
  219. pinum = self.nameToInum[parent]
  220. # print '--> delete from parent inum', pinum
  221. pblock = self.inodes[pinum].getAddr()
  222. # FIXED BUG: DECREASE PARENT INODE REF COUNT! (thanks to Srinivasan Thirunarayanan)
  223. self.inodes[pinum].decRefCnt()
  224. # print '--> delete from parent addr', pblock
  225. self.data[pblock].delDirEntry(tfile)
  226. # finally, remove from files list
  227. self.files.remove(tfile)
  228. return 0
  229. def createLink(self, target, newfile, parent):
  230. # find info about parent
  231. parentInum = self.nameToInum[parent]
  232. # is there room in the parent directory?
  233. pblock = self.inodes[parentInum].getAddr()
  234. if self.data[pblock].getFreeEntries() <= 0:
  235. dprint('*** createLink failed: no room in parent directory ***')
  236. return -1
  237. # print 'is %s in directory %d' % (newfile, pblock)
  238. if self.data[pblock].dirEntryExists(newfile):
  239. dprint('*** createLink failed: not a unique name ***')
  240. return -1
  241. # now, find inumber of target
  242. tinum = self.nameToInum[target]
  243. self.inodes[tinum].incRefCnt()
  244. # inc parent ref count
  245. self.inodes[parentInum].incRefCnt()
  246. # now add to directory
  247. tmp = newfile.split('/')
  248. ename = tmp[len(tmp)-1]
  249. self.data[pblock].addDirEntry(ename, tinum)
  250. return tinum
  251. def createFile(self, parent, newfile, ftype):
  252. # find info about parent
  253. parentInum = self.nameToInum[parent]
  254. # is there room in the parent directory?
  255. pblock = self.inodes[parentInum].getAddr()
  256. if self.data[pblock].getFreeEntries() <= 0:
  257. dprint('*** createFile failed: no room in parent directory ***')
  258. return -1
  259. # have to make sure file name is unique
  260. block = self.inodes[parentInum].getAddr()
  261. # print 'is %s in directory %d' % (newfile, block)
  262. if self.data[block].dirEntryExists(newfile):
  263. dprint('*** createFile failed: not a unique name ***')
  264. return -1
  265. # find free inode
  266. inum = self.inodeAlloc()
  267. if inum == -1:
  268. dprint('*** createFile failed: no inodes left ***')
  269. return -1
  270. # if a directory, have to allocate directory block for basic (., ..) info
  271. fblock = -1
  272. if ftype == 'd':
  273. refCnt = 2
  274. fblock = self.dataAlloc()
  275. if fblock == -1:
  276. dprint('*** createFile failed: no data blocks left ***')
  277. self.inodeFree(inum)
  278. return -1
  279. else:
  280. self.data[fblock].setType('d')
  281. self.data[fblock].addDirEntry('.', inum)
  282. self.data[fblock].addDirEntry('..', parentInum)
  283. else:
  284. refCnt = 1
  285. # now ok to init inode properly
  286. self.inodes[inum].setAll(ftype, fblock, refCnt)
  287. # inc parent ref count
  288. self.inodes[parentInum].incRefCnt()
  289. # and add to directory of parent
  290. self.data[pblock].addDirEntry(newfile, inum)
  291. return inum
  292. def writeFile(self, tfile, data):
  293. inum = self.nameToInum[tfile]
  294. curSize = self.inodes[inum].getSize()
  295. dprint('writeFile: inum:%d cursize:%d refcnt:%d' % (inum, curSize, self.inodes[inum].getRefCnt()))
  296. if curSize == 1:
  297. dprint('*** writeFile failed: file is full ***')
  298. return -1
  299. fblock = self.dataAlloc()
  300. if fblock == -1:
  301. dprint('*** writeFile failed: no data blocks left ***')
  302. return -1
  303. else:
  304. self.data[fblock].setType('f')
  305. self.data[fblock].addData(data)
  306. self.inodes[inum].setAddr(fblock)
  307. if printOps:
  308. print 'fd=open("%s", O_WRONLY|O_APPEND); write(fd, buf, BLOCKSIZE); close(fd);' % tfile
  309. return 0
  310. def doDelete(self):
  311. dprint('doDelete')
  312. if len(self.files) == 0:
  313. return -1
  314. dfile = self.files[int(random.random() * len(self.files))]
  315. dprint('try delete(%s)' % dfile)
  316. return self.deleteFile(dfile)
  317. def doLink(self):
  318. dprint('doLink')
  319. if len(self.files) == 0:
  320. return -1
  321. parent = self.dirs[int(random.random() * len(self.dirs))]
  322. nfile = self.makeName()
  323. # pick random target
  324. target = self.files[int(random.random() * len(self.files))]
  325. # get full name of newfile
  326. if parent == '/':
  327. fullName = parent + nfile
  328. else:
  329. fullName = parent + '/' + nfile
  330. dprint('try createLink(%s %s %s)' % (target, nfile, parent))
  331. inum = self.createLink(target, nfile, parent)
  332. if inum >= 0:
  333. self.files.append(fullName)
  334. self.nameToInum[fullName] = inum
  335. if printOps:
  336. print 'link("%s", "%s");' % (target, fullName)
  337. return 0
  338. return -1
  339. def doCreate(self, ftype):
  340. dprint('doCreate')
  341. parent = self.dirs[int(random.random() * len(self.dirs))]
  342. nfile = self.makeName()
  343. if ftype == 'd':
  344. tlist = self.dirs
  345. else:
  346. tlist = self.files
  347. if parent == '/':
  348. fullName = parent + nfile
  349. else:
  350. fullName = parent + '/' + nfile
  351. dprint('try createFile(%s %s %s)' % (parent, nfile, ftype))
  352. inum = self.createFile(parent, nfile, ftype)
  353. if inum >= 0:
  354. tlist.append(fullName)
  355. self.nameToInum[fullName] = inum
  356. if parent == '/':
  357. parent = ''
  358. if ftype == 'd':
  359. if printOps:
  360. print 'mkdir("%s/%s");' % (parent, nfile)
  361. else:
  362. if printOps:
  363. print 'creat("%s/%s");' % (parent, nfile)
  364. return 0
  365. return -1
  366. def doAppend(self):
  367. dprint('doAppend')
  368. if len(self.files) == 0:
  369. return -1
  370. afile = self.files[int(random.random() * len(self.files))]
  371. dprint('try writeFile(%s)' % afile)
  372. data = chr(ord('a') + int(random.random() * 26))
  373. rc = self.writeFile(afile, data)
  374. return rc
  375. def run(self, numRequests):
  376. self.percentMkdir = 0.40
  377. self.percentWrite = 0.40
  378. self.percentDelete = 0.20
  379. self.numRequests = 20
  380. print 'Initial state'
  381. print ''
  382. self.dump()
  383. print ''
  384. for i in range(numRequests):
  385. if printOps == False:
  386. print 'Which operation took place?'
  387. rc = -1
  388. while rc == -1:
  389. r = random.random()
  390. if r < 0.3:
  391. rc = self.doAppend()
  392. dprint('doAppend rc:%d' % rc)
  393. elif r < 0.5:
  394. rc = self.doDelete()
  395. dprint('doDelete rc:%d' % rc)
  396. elif r < 0.7:
  397. rc = self.doLink()
  398. dprint('doLink rc:%d' % rc)
  399. else:
  400. if random.random() < 0.75:
  401. rc = self.doCreate('f')
  402. dprint('doCreate(f) rc:%d' % rc)
  403. else:
  404. rc = self.doCreate('d')
  405. dprint('doCreate(d) rc:%d' % rc)
  406. if printState == True:
  407. print ''
  408. self.dump()
  409. print ''
  410. else:
  411. print ''
  412. print ' State of file system (inode bitmap, inodes, data bitmap, data)?'
  413. print ''
  414. if printFinal:
  415. print ''
  416. print 'Summary of files, directories::'
  417. print ''
  418. print ' Files: ', self.files
  419. print ' Directories:', self.dirs
  420. print ''
  421. #
  422. # main program
  423. #
  424. parser = OptionParser()
  425. parser.add_option('-s', '--seed', default=0, help='the random seed', action='store', type='int', dest='seed')
  426. parser.add_option('-i', '--numInodes', default=8, help='number of inodes in file system', action='store', type='int', dest='numInodes')
  427. parser.add_option('-d', '--numData', default=8, help='number of data blocks in file system', action='store', type='int', dest='numData')
  428. parser.add_option('-n', '--numRequests', default=10, help='number of requests to simulate', action='store', type='int', dest='numRequests')
  429. parser.add_option('-r', '--reverse', default=False, help='instead of printing state, print ops', action='store_true', dest='reverse')
  430. parser.add_option('-p', '--printFinal', default=False, help='print the final set of files/dirs', action='store_true', dest='printFinal')
  431. parser.add_option('-c', '--compute', default=False, help='compute answers for me', action='store_true', dest='solve')
  432. (options, args) = parser.parse_args()
  433. print 'ARG seed', options.seed
  434. print 'ARG numInodes', options.numInodes
  435. print 'ARG numData', options.numData
  436. print 'ARG numRequests', options.numRequests
  437. print 'ARG reverse', options.reverse
  438. print 'ARG printFinal', options.printFinal
  439. print ''
  440. random.seed(options.seed)
  441. if options.reverse:
  442. printState = False
  443. printOps = True
  444. else:
  445. printState = True
  446. printOps = False
  447. if options.solve:
  448. printOps = True
  449. printState = True
  450. printFinal = options.printFinal
  451. #
  452. # have to generate RANDOM requests to the file system
  453. # that are VALID!
  454. #
  455. f = fs(options.numInodes, options.numData)
  456. #
  457. # ops: mkdir rmdir : create delete : append write
  458. #
  459. f.run(options.numRequests)