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

325 lines
12 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. SCHED_SWITCH_ON_END = 'SWITCH_ON_END'
  8. # io finished behavior
  9. IO_RUN_LATER = 'IO_RUN_LATER'
  10. IO_RUN_IMMEDIATE = 'IO_RUN_IMMEDIATE'
  11. # process states
  12. STATE_RUNNING = 'RUNNING'
  13. STATE_READY = 'READY'
  14. STATE_DONE = 'DONE'
  15. STATE_WAIT = 'WAITING'
  16. # members of process structure
  17. PROC_CODE = 'code_'
  18. PROC_PC = 'pc_'
  19. PROC_ID = 'pid_'
  20. PROC_STATE = 'proc_state_'
  21. # things a process can do
  22. DO_COMPUTE = 'cpu'
  23. DO_IO = 'io'
  24. class scheduler:
  25. def __init__(self, process_switch_behavior, io_done_behavior, io_length):
  26. # keep set of instructions for each of the processes
  27. self.proc_info = {}
  28. self.process_switch_behavior = process_switch_behavior
  29. self.io_done_behavior = io_done_behavior
  30. self.io_length = io_length
  31. return
  32. def new_process(self):
  33. proc_id = len(self.proc_info)
  34. self.proc_info[proc_id] = {}
  35. self.proc_info[proc_id][PROC_PC] = 0
  36. self.proc_info[proc_id][PROC_ID] = proc_id
  37. self.proc_info[proc_id][PROC_CODE] = []
  38. self.proc_info[proc_id][PROC_STATE] = STATE_READY
  39. return proc_id
  40. def load_file(self, progfile):
  41. fd = open(progfile)
  42. proc_id = self.new_process()
  43. for line in fd:
  44. tmp = line.split()
  45. if len(tmp) == 0:
  46. continue
  47. opcode = tmp[0]
  48. if opcode == 'compute':
  49. assert(len(tmp) == 2)
  50. for i in range(int(tmp[1])):
  51. self.proc_info[proc_id][PROC_CODE].append(DO_COMPUTE)
  52. elif opcode == 'io':
  53. assert(len(tmp) == 1)
  54. self.proc_info[proc_id][PROC_CODE].append(DO_IO)
  55. fd.close()
  56. return
  57. def load(self, program_description):
  58. proc_id = self.new_process()
  59. tmp = program_description.split(':')
  60. if len(tmp) != 2:
  61. print 'Bad description (%s): Must be number <x:y>'
  62. print ' where X is the number of instructions'
  63. print ' and Y is the percent change that an instruction is CPU not IO'
  64. exit(1)
  65. num_instructions, chance_cpu = int(tmp[0]), float(tmp[1])/100.0
  66. for i in range(num_instructions):
  67. if random.random() < chance_cpu:
  68. self.proc_info[proc_id][PROC_CODE].append(DO_COMPUTE)
  69. else:
  70. self.proc_info[proc_id][PROC_CODE].append(DO_IO)
  71. return
  72. def move_to_ready(self, expected, pid=-1):
  73. if pid == -1:
  74. pid = self.curr_proc
  75. assert(self.proc_info[pid][PROC_STATE] == expected)
  76. self.proc_info[pid][PROC_STATE] = STATE_READY
  77. return
  78. def move_to_wait(self, expected):
  79. assert(self.proc_info[self.curr_proc][PROC_STATE] == expected)
  80. self.proc_info[self.curr_proc][PROC_STATE] = STATE_WAIT
  81. return
  82. def move_to_running(self, expected):
  83. assert(self.proc_info[self.curr_proc][PROC_STATE] == expected)
  84. self.proc_info[self.curr_proc][PROC_STATE] = STATE_RUNNING
  85. return
  86. def move_to_done(self, expected):
  87. assert(self.proc_info[self.curr_proc][PROC_STATE] == expected)
  88. self.proc_info[self.curr_proc][PROC_STATE] = STATE_DONE
  89. return
  90. def next_proc(self, pid=-1):
  91. if pid != -1:
  92. self.curr_proc = pid
  93. self.move_to_running(STATE_READY)
  94. return
  95. for pid in range(self.curr_proc + 1, len(self.proc_info)):
  96. if self.proc_info[pid][PROC_STATE] == STATE_READY:
  97. self.curr_proc = pid
  98. self.move_to_running(STATE_READY)
  99. return
  100. for pid in range(0, self.curr_proc + 1):
  101. if self.proc_info[pid][PROC_STATE] == STATE_READY:
  102. self.curr_proc = pid
  103. self.move_to_running(STATE_READY)
  104. return
  105. return
  106. def get_num_processes(self):
  107. return len(self.proc_info)
  108. def get_num_instructions(self, pid):
  109. return len(self.proc_info[pid][PROC_CODE])
  110. def get_instruction(self, pid, index):
  111. return self.proc_info[pid][PROC_CODE][index]
  112. def get_num_active(self):
  113. num_active = 0
  114. for pid in range(len(self.proc_info)):
  115. if self.proc_info[pid][PROC_STATE] != STATE_DONE:
  116. num_active += 1
  117. return num_active
  118. def get_num_runnable(self):
  119. num_active = 0
  120. for pid in range(len(self.proc_info)):
  121. if self.proc_info[pid][PROC_STATE] == STATE_READY or \
  122. self.proc_info[pid][PROC_STATE] == STATE_RUNNING:
  123. num_active += 1
  124. return num_active
  125. def get_ios_in_flight(self, current_time):
  126. num_in_flight = 0
  127. for pid in range(len(self.proc_info)):
  128. for t in self.io_finish_times[pid]:
  129. if t > current_time:
  130. num_in_flight += 1
  131. return num_in_flight
  132. def check_for_switch(self):
  133. return
  134. def space(self, num_columns):
  135. for i in range(num_columns):
  136. print '%10s' % ' ',
  137. def check_if_done(self):
  138. if len(self.proc_info[self.curr_proc][PROC_CODE]) == 0:
  139. if self.proc_info[self.curr_proc][PROC_STATE] == STATE_RUNNING:
  140. self.move_to_done(STATE_RUNNING)
  141. self.next_proc()
  142. return
  143. def run(self):
  144. clock_tick = 0
  145. if len(self.proc_info) == 0:
  146. return
  147. # track outstanding IOs, per process
  148. self.io_finish_times = {}
  149. for pid in range(len(self.proc_info)):
  150. self.io_finish_times[pid] = []
  151. # make first one active
  152. self.curr_proc = 0
  153. self.move_to_running(STATE_READY)
  154. # OUTPUT: headers for each column
  155. print '%s' % 'Time',
  156. for pid in range(len(self.proc_info)):
  157. print '%10s' % ('PID:%2d' % (pid)),
  158. print '%10s' % 'CPU',
  159. print '%10s' % 'IOs',
  160. print ''
  161. # init statistics
  162. io_busy = 0
  163. cpu_busy = 0
  164. while self.get_num_active() > 0:
  165. clock_tick += 1
  166. # check for io finish
  167. io_done = False
  168. for pid in range(len(self.proc_info)):
  169. if clock_tick in self.io_finish_times[pid]:
  170. io_done = True
  171. self.move_to_ready(STATE_WAIT, pid)
  172. if self.io_done_behavior == IO_RUN_IMMEDIATE:
  173. # IO_RUN_IMMEDIATE
  174. if self.curr_proc != pid:
  175. if self.proc_info[self.curr_proc][PROC_STATE] == STATE_RUNNING:
  176. self.move_to_ready(STATE_RUNNING)
  177. self.next_proc(pid)
  178. else:
  179. # IO_RUN_LATER
  180. if self.process_switch_behavior == SCHED_SWITCH_ON_END:
  181. # this means the process that issued the io should be run
  182. self.next_proc(pid)
  183. if self.get_num_runnable() == 1:
  184. # this is the only thing to run: so run it
  185. self.next_proc(pid)
  186. self.check_if_done()
  187. # if current proc is RUNNING and has an instruction, execute it
  188. instruction_to_execute = ''
  189. if self.proc_info[self.curr_proc][PROC_STATE] == STATE_RUNNING and \
  190. len(self.proc_info[self.curr_proc][PROC_CODE]) > 0:
  191. instruction_to_execute = self.proc_info[self.curr_proc][PROC_CODE].pop(0)
  192. cpu_busy += 1
  193. # OUTPUT: print what everyone is up to
  194. if io_done:
  195. print '%3d*' % clock_tick,
  196. else:
  197. print '%3d ' % clock_tick,
  198. for pid in range(len(self.proc_info)):
  199. if pid == self.curr_proc and instruction_to_execute != '':
  200. print '%10s' % ('RUN:'+instruction_to_execute),
  201. else:
  202. print '%10s' % (self.proc_info[pid][PROC_STATE]),
  203. if instruction_to_execute == '':
  204. print '%10s' % ' ',
  205. else:
  206. print '%10s' % 1,
  207. num_outstanding = self.get_ios_in_flight(clock_tick)
  208. if num_outstanding > 0:
  209. print '%10s' % str(num_outstanding),
  210. io_busy += 1
  211. else:
  212. print '%10s' % ' ',
  213. print ''
  214. # if this is an IO instruction, switch to waiting state
  215. # and add an io completion in the future
  216. if instruction_to_execute == DO_IO:
  217. self.move_to_wait(STATE_RUNNING)
  218. self.io_finish_times[self.curr_proc].append(clock_tick + self.io_length)
  219. if self.process_switch_behavior == SCHED_SWITCH_ON_IO:
  220. self.next_proc()
  221. # ENDCASE: check if currently running thing is out of instructions
  222. self.check_if_done()
  223. return (cpu_busy, io_busy, clock_tick)
  224. #
  225. # PARSE ARGUMENTS
  226. #
  227. parser = OptionParser()
  228. parser.add_option('-s', '--seed', default=0, help='the random seed', action='store', type='int', dest='seed')
  229. parser.add_option('-l', '--processlist', default='',
  230. help='a comma-separated list of processes to run, in the form X1:Y1,X2:Y2,... where X is the number of instructions that process should run, and Y the chances (from 0 to 100) that an instruction will use the CPU or issue an IO',
  231. action='store', type='string', dest='process_list')
  232. parser.add_option('-L', '--iolength', default=5, help='how long an IO takes', action='store', type='int', dest='io_length')
  233. parser.add_option('-S', '--switch', default='SWITCH_ON_IO',
  234. help='when to switch between processes: SWITCH_ON_IO, SWITCH_ON_END',
  235. action='store', type='string', dest='process_switch_behavior')
  236. parser.add_option('-I', '--iodone', default='IO_RUN_LATER',
  237. help='type of behavior when IO ends: IO_RUN_LATER, IO_RUN_IMMEDIATE',
  238. action='store', type='string', dest='io_done_behavior')
  239. parser.add_option('-c', help='compute answers for me', action='store_true', default=False, dest='solve')
  240. 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')
  241. (options, args) = parser.parse_args()
  242. random.seed(options.seed)
  243. assert(options.process_switch_behavior == SCHED_SWITCH_ON_IO or \
  244. options.process_switch_behavior == SCHED_SWITCH_ON_END)
  245. assert(options.io_done_behavior == IO_RUN_IMMEDIATE or \
  246. options.io_done_behavior == IO_RUN_LATER)
  247. s = scheduler(options.process_switch_behavior, options.io_done_behavior, options.io_length)
  248. # example process description (10:100,10:100)
  249. for p in options.process_list.split(','):
  250. s.load(p)
  251. if options.solve == False:
  252. print 'Produce a trace of what would happen when you run these processes:'
  253. for pid in range(s.get_num_processes()):
  254. print 'Process %d' % pid
  255. for inst in range(s.get_num_instructions(pid)):
  256. print ' %s' % s.get_instruction(pid, inst)
  257. print ''
  258. print 'Important behaviors:'
  259. print ' System will switch when',
  260. if options.process_switch_behavior == SCHED_SWITCH_ON_IO:
  261. print 'the current process is FINISHED or ISSUES AN IO'
  262. else:
  263. print 'the current process is FINISHED'
  264. print ' After IOs, the process issuing the IO will',
  265. if options.io_done_behavior == IO_RUN_IMMEDIATE:
  266. print 'run IMMEDIATELY'
  267. else:
  268. print 'run LATER (when it is its turn)'
  269. print ''
  270. exit(0)
  271. (cpu_busy, io_busy, clock_tick) = s.run()
  272. if options.print_stats:
  273. print ''
  274. print 'Stats: Total Time %d' % clock_tick
  275. print 'Stats: CPU Busy %d (%.2f%%)' % (cpu_busy, 100.0 * float(cpu_busy)/clock_tick)
  276. print 'Stats: IO Busy %d (%.2f%%)' % (io_busy, 100.0 * float(io_busy)/clock_tick)
  277. print ''