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

273 lines
9.5 KiB

  1. #! /usr/bin/env python
  2. import sys
  3. from optparse import OptionParser
  4. import random
  5. # process switch behavior
  6. SCHED_SWITCH_ON_IO = 'SWITCH_ON_IO'
  7. # io finished behavior
  8. IO_RUN_LATER = 'IO_RUN_LATER'
  9. # process states
  10. STATE_RUNNING = 'RUNNING'
  11. STATE_READY = 'READY'
  12. STATE_DONE = 'DONE'
  13. STATE_WAIT = 'WAITING'
  14. # members of process structure
  15. PROC_CODE = 'code_'
  16. PROC_PC = 'pc_'
  17. PROC_ID = 'pid_'
  18. PROC_STATE = 'proc_state_'
  19. # things a process can do
  20. DO_COMPUTE = 'cpu'
  21. DO_YIELD = 'yld'
  22. DO_IO = 'io'
  23. class scheduler:
  24. def __init__(self, process_switch_behavior, io_done_behavior, io_length):
  25. # keep set of instructions for each of the processes
  26. self.proc_info = {}
  27. self.process_switch_behavior = process_switch_behavior
  28. self.io_done_behavior = io_done_behavior
  29. self.io_length = io_length
  30. return
  31. def new_process(self):
  32. proc_id = len(self.proc_info)
  33. self.proc_info[proc_id] = {}
  34. self.proc_info[proc_id][PROC_PC] = 0
  35. self.proc_info[proc_id][PROC_ID] = proc_id
  36. self.proc_info[proc_id][PROC_CODE] = []
  37. self.proc_info[proc_id][PROC_STATE] = STATE_READY
  38. return proc_id
  39. def load(self, program_description):
  40. proc_id = self.new_process()
  41. tmp = program_description.split(':')
  42. if len(tmp) != 3:
  43. print 'Bad description (%s): Must be number <x:y:z>'
  44. print ' where X is the number of instructions'
  45. print ' and Y is the percent change that an instruction is YIELD'
  46. print ' and Z is the percent change that an instruction is IO'
  47. exit(1)
  48. num_instructions, chance_yield, chance_io = int(tmp[0]), float(tmp[1])/100.0, float(tmp[2])/100.0
  49. assert(chance_yield+chance_io<1)
  50. #print "proc %d, num_instr %d, change_cpu %f" % (proc_id,num_instructions, chance_cpu)
  51. for i in range(num_instructions):
  52. randnum=random.random();
  53. if randnum < (1.0-chance_yield-chance_io):
  54. self.proc_info[proc_id][PROC_CODE].append(DO_COMPUTE)
  55. elif randnum >= (1.0-chance_yield-chance_io) and randnum < (1.0-chance_io):
  56. self.proc_info[proc_id][PROC_CODE].append(DO_YIELD)
  57. else:
  58. self.proc_info[proc_id][PROC_CODE].append(DO_IO)
  59. #print "proc %d, instr idx %d, instr cxt %s" % (proc_id, i, self.proc_info[proc_id][PROC_CODE][i])
  60. return
  61. #change to READY STATE, the current proc's state should be expected
  62. #if pid==-1, then pid=self.curr_proc
  63. def move_to_ready(self, expected, pid=-1):
  64. #YOUR CODE
  65. return
  66. #change to RUNNING STATE, the current proc's state should be expected
  67. def move_to_running(self, expected):
  68. #YOUR CODE
  69. return
  70. #change to DONE STATE, the current proc's state should be expected
  71. def move_to_done(self, expected):
  72. #YOUR CODE
  73. return
  74. #choose next proc using FIFO/FCFS scheduling, If pid==-1, then pid=self.curr_proc
  75. def next_proc(self, pid=-1):
  76. #YOUR CODE
  77. return
  78. def get_num_processes(self):
  79. return len(self.proc_info)
  80. def get_num_instructions(self, pid):
  81. return len(self.proc_info[pid][PROC_CODE])
  82. def get_instruction(self, pid, index):
  83. return self.proc_info[pid][PROC_CODE][index]
  84. def get_num_active(self):
  85. num_active = 0
  86. for pid in range(len(self.proc_info)):
  87. if self.proc_info[pid][PROC_STATE] != STATE_DONE:
  88. num_active += 1
  89. return num_active
  90. def get_num_runnable(self):
  91. num_active = 0
  92. for pid in range(len(self.proc_info)):
  93. if self.proc_info[pid][PROC_STATE] == STATE_READY or \
  94. self.proc_info[pid][PROC_STATE] == STATE_RUNNING:
  95. num_active += 1
  96. return num_active
  97. def get_ios_in_flight(self, current_time):
  98. num_in_flight = 0
  99. for pid in range(len(self.proc_info)):
  100. for t in self.io_finish_times[pid]:
  101. if t > current_time:
  102. num_in_flight += 1
  103. return num_in_flight
  104. def space(self, num_columns):
  105. for i in range(num_columns):
  106. print '%10s' % ' ',
  107. def check_if_done(self):
  108. if len(self.proc_info[self.curr_proc][PROC_CODE]) == 0:
  109. if self.proc_info[self.curr_proc][PROC_STATE] == STATE_RUNNING:
  110. self.move_to_done(STATE_RUNNING)
  111. self.next_proc()
  112. return
  113. def run(self):
  114. clock_tick = 0
  115. if len(self.proc_info) == 0:
  116. return
  117. # track outstanding IOs, per process
  118. self.io_finish_times = {}
  119. for pid in range(len(self.proc_info)):
  120. self.io_finish_times[pid] = []
  121. # make first one active
  122. self.curr_proc = 0
  123. self.move_to_running(STATE_READY)
  124. # OUTPUT: heade`[rs for each column
  125. print '%s' % 'Time',
  126. for pid in range(len(self.proc_info)):
  127. print '%10s' % ('PID:%2d' % (pid)),
  128. print '%10s' % 'CPU',
  129. print '%10s' % 'IOs',
  130. print ''
  131. # init statistics
  132. io_busy = 0
  133. cpu_busy = 0
  134. while self.get_num_active() > 0:
  135. clock_tick += 1
  136. # check for io finish
  137. io_done = False
  138. for pid in range(len(self.proc_info)):
  139. if clock_tick in self.io_finish_times[pid]:
  140. # if IO finished, the should do something for related process
  141. #YOUR CODE
  142. pass #YOU should delete this
  143. # if current proc is RUNNING and has an instruction, execute it
  144. instruction_to_execute = ''
  145. if self.proc_info[self.curr_proc][PROC_STATE] == STATE_RUNNING and \
  146. len(self.proc_info[self.curr_proc][PROC_CODE]) > 0:
  147. #pop a instruction from proc_info[self.curr_proc][PROC_CODE]to instruction_to_execute
  148. #YOUR CODE
  149. pass #YOU should delete this
  150. # OUTPUT: print what everyone is up to
  151. if io_done:
  152. print '%3d*' % clock_tick,
  153. else:
  154. print '%3d ' % clock_tick,
  155. for pid in range(len(self.proc_info)):
  156. if pid == self.curr_proc and instruction_to_execute != '':
  157. print '%10s' % ('RUN:'+instruction_to_execute),
  158. else:
  159. print '%10s' % (self.proc_info[pid][PROC_STATE]),
  160. if instruction_to_execute == '':
  161. print '%10s' % ' ',
  162. else:
  163. print '%10s' % 1,
  164. num_outstanding = self.get_ios_in_flight(clock_tick)
  165. if num_outstanding > 0:
  166. print '%10s' % str(num_outstanding),
  167. io_busy += 1
  168. else:
  169. print '%10s' % ' ',
  170. print ''
  171. # if this is an YIELD instruction, switch to ready state
  172. # and add an io completion in the future
  173. if instruction_to_execute == DO_YIELD:
  174. #YOUR CODE
  175. pass #YOU should delete this
  176. # if this is an IO instruction, switch to waiting state
  177. # and add an io completion in the future
  178. elif instruction_to_execute == DO_IO:
  179. #YOUR CODE
  180. pass #YOU should delete this
  181. # ENDCASE: check if currently running thing is out of instructions
  182. self.check_if_done()
  183. return (cpu_busy, io_busy, clock_tick)
  184. #
  185. # PARSE ARGUMENTS
  186. #
  187. parser = OptionParser()
  188. parser.add_option('-s', '--seed', default=0, help='the random seed', action='store', type='int', dest='seed')
  189. parser.add_option('-l', '--processlist', default='',
  190. help='a comma-separated list of processes to run, in the form X1:Y1:Z1,X2:Y2:Z2,... where X is the number of instructions that process should run, and Y/Z the chances (from 0 to 100) issue an YIELD/IO',
  191. action='store', type='string', dest='process_list')
  192. parser.add_option('-L', '--iolength', default=3, help='how long an IO takes', action='store', type='int', dest='io_length')
  193. parser.add_option('-p', '--printstats', help='print statistics at end; only useful with -c flag (otherwise stats are not printed)', action='store_true', default=False, dest='print_stats')
  194. (options, args) = parser.parse_args()
  195. random.seed(options.seed)
  196. process_switch_behavior = SCHED_SWITCH_ON_IO
  197. io_done_behavior = IO_RUN_LATER
  198. io_length=options.io_length
  199. s = scheduler(process_switch_behavior, io_done_behavior, io_length)
  200. # example process description (10:100,10:100)
  201. for p in options.process_list.split(','):
  202. s.load(p)
  203. print 'Produce a trace of what would happen when you run these processes:'
  204. for pid in range(s.get_num_processes()):
  205. print 'Process %d' % pid
  206. for inst in range(s.get_num_instructions(pid)):
  207. print ' %s' % s.get_instruction(pid, inst)
  208. print ''
  209. print 'Important behaviors:'
  210. print ' System will switch when',
  211. if process_switch_behavior == SCHED_SWITCH_ON_IO:
  212. print 'the current process is FINISHED or ISSUES AN YIELD or IO'
  213. else:
  214. print 'error in sched switch on iobehavior'
  215. exit (-1)
  216. print ' After IOs, the process issuing the IO will',
  217. if io_done_behavior == IO_RUN_LATER:
  218. print 'run LATER (when it is its turn)'
  219. else:
  220. print 'error in IO done behavior'
  221. exit (-1)
  222. print ''
  223. (cpu_busy, io_busy, clock_tick) = s.run()
  224. print ''
  225. print 'Stats: Total Time %d' % clock_tick
  226. print 'Stats: CPU Busy %d (%.2f%%)' % (cpu_busy, 100.0 * float(cpu_busy)/clock_tick)
  227. print 'Stats: IO Busy %d (%.2f%%)' % (io_busy, 100.0 * float(io_busy)/clock_tick)
  228. print ''