#! /usr/bin/env python import sys from optparse import OptionParser import random # process switch behavior SCHED_SWITCH_ON_IO = 'SWITCH_ON_IO' # io finished behavior IO_RUN_LATER = 'IO_RUN_LATER' # process states STATE_RUNNING = 'RUNNING' STATE_READY = 'READY' STATE_DONE = 'DONE' STATE_WAIT = 'WAITING' # members of process structure PROC_CODE = 'code_' PROC_PC = 'pc_' PROC_ID = 'pid_' PROC_STATE = 'proc_state_' # things a process can do DO_COMPUTE = 'cpu' DO_YIELD = 'yld' DO_IO = 'io' class scheduler: def __init__(self, process_switch_behavior, io_done_behavior, io_length): # keep set of instructions for each of the processes self.proc_info = {} self.process_switch_behavior = process_switch_behavior self.io_done_behavior = io_done_behavior self.io_length = io_length return def new_process(self): proc_id = len(self.proc_info) self.proc_info[proc_id] = {} self.proc_info[proc_id][PROC_PC] = 0 self.proc_info[proc_id][PROC_ID] = proc_id self.proc_info[proc_id][PROC_CODE] = [] self.proc_info[proc_id][PROC_STATE] = STATE_READY return proc_id def load(self, program_description): proc_id = self.new_process() tmp = program_description.split(':') if len(tmp) != 3: print 'Bad description (%s): Must be number ' print ' where X is the number of instructions' print ' and Y is the percent change that an instruction is YIELD' print ' and Z is the percent change that an instruction is IO' exit(1) num_instructions, chance_yield, chance_io = int(tmp[0]), float(tmp[1])/100.0, float(tmp[2])/100.0 assert(chance_yield+chance_io<1) #print "proc %d, num_instr %d, change_cpu %f" % (proc_id,num_instructions, chance_cpu) for i in range(num_instructions): randnum=random.random(); if randnum < (1.0-chance_yield-chance_io): self.proc_info[proc_id][PROC_CODE].append(DO_COMPUTE) elif randnum >= (1.0-chance_yield-chance_io) and randnum < (1.0-chance_io): self.proc_info[proc_id][PROC_CODE].append(DO_YIELD) else: self.proc_info[proc_id][PROC_CODE].append(DO_IO) #print "proc %d, instr idx %d, instr cxt %s" % (proc_id, i, self.proc_info[proc_id][PROC_CODE][i]) return #change to READY STATE, the current proc's state should be expected #if pid==-1, then pid=self.curr_proc def move_to_ready(self, expected, pid=-1): #YOUR CODE return #change to RUNNING STATE, the current proc's state should be expected def move_to_running(self, expected): #YOUR CODE return #change to DONE STATE, the current proc's state should be expected def move_to_done(self, expected): #YOUR CODE return #choose next proc using FIFO/FCFS scheduling, If pid==-1, then pid=self.curr_proc def next_proc(self, pid=-1): #YOUR CODE return def get_num_processes(self): return len(self.proc_info) def get_num_instructions(self, pid): return len(self.proc_info[pid][PROC_CODE]) def get_instruction(self, pid, index): return self.proc_info[pid][PROC_CODE][index] def get_num_active(self): num_active = 0 for pid in range(len(self.proc_info)): if self.proc_info[pid][PROC_STATE] != STATE_DONE: num_active += 1 return num_active def get_num_runnable(self): num_active = 0 for pid in range(len(self.proc_info)): if self.proc_info[pid][PROC_STATE] == STATE_READY or \ self.proc_info[pid][PROC_STATE] == STATE_RUNNING: num_active += 1 return num_active def get_ios_in_flight(self, current_time): num_in_flight = 0 for pid in range(len(self.proc_info)): for t in self.io_finish_times[pid]: if t > current_time: num_in_flight += 1 return num_in_flight def space(self, num_columns): for i in range(num_columns): print '%10s' % ' ', def check_if_done(self): if len(self.proc_info[self.curr_proc][PROC_CODE]) == 0: if self.proc_info[self.curr_proc][PROC_STATE] == STATE_RUNNING: self.move_to_done(STATE_RUNNING) self.next_proc() return def run(self): clock_tick = 0 if len(self.proc_info) == 0: return # track outstanding IOs, per process self.io_finish_times = {} for pid in range(len(self.proc_info)): self.io_finish_times[pid] = [] # make first one active self.curr_proc = 0 self.move_to_running(STATE_READY) # OUTPUT: heade`[rs for each column print '%s' % 'Time', for pid in range(len(self.proc_info)): print '%10s' % ('PID:%2d' % (pid)), print '%10s' % 'CPU', print '%10s' % 'IOs', print '' # init statistics io_busy = 0 cpu_busy = 0 while self.get_num_active() > 0: clock_tick += 1 # check for io finish io_done = False for pid in range(len(self.proc_info)): if clock_tick in self.io_finish_times[pid]: # if IO finished, the should do something for related process #YOUR CODE pass #YOU should delete this # if current proc is RUNNING and has an instruction, execute it instruction_to_execute = '' if self.proc_info[self.curr_proc][PROC_STATE] == STATE_RUNNING and \ len(self.proc_info[self.curr_proc][PROC_CODE]) > 0: #pop a instruction from proc_info[self.curr_proc][PROC_CODE]to instruction_to_execute #YOUR CODE pass #YOU should delete this # OUTPUT: print what everyone is up to if io_done: print '%3d*' % clock_tick, else: print '%3d ' % clock_tick, for pid in range(len(self.proc_info)): if pid == self.curr_proc and instruction_to_execute != '': print '%10s' % ('RUN:'+instruction_to_execute), else: print '%10s' % (self.proc_info[pid][PROC_STATE]), if instruction_to_execute == '': print '%10s' % ' ', else: print '%10s' % 1, num_outstanding = self.get_ios_in_flight(clock_tick) if num_outstanding > 0: print '%10s' % str(num_outstanding), io_busy += 1 else: print '%10s' % ' ', print '' # if this is an YIELD instruction, switch to ready state # and add an io completion in the future if instruction_to_execute == DO_YIELD: #YOUR CODE pass #YOU should delete this # if this is an IO instruction, switch to waiting state # and add an io completion in the future elif instruction_to_execute == DO_IO: #YOUR CODE pass #YOU should delete this # ENDCASE: check if currently running thing is out of instructions self.check_if_done() return (cpu_busy, io_busy, clock_tick) # # PARSE ARGUMENTS # parser = OptionParser() parser.add_option('-s', '--seed', default=0, help='the random seed', action='store', type='int', dest='seed') parser.add_option('-l', '--processlist', default='', 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', action='store', type='string', dest='process_list') parser.add_option('-L', '--iolength', default=3, help='how long an IO takes', action='store', type='int', dest='io_length') 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') (options, args) = parser.parse_args() random.seed(options.seed) process_switch_behavior = SCHED_SWITCH_ON_IO io_done_behavior = IO_RUN_LATER io_length=options.io_length s = scheduler(process_switch_behavior, io_done_behavior, io_length) # example process description (10:100,10:100) for p in options.process_list.split(','): s.load(p) print 'Produce a trace of what would happen when you run these processes:' for pid in range(s.get_num_processes()): print 'Process %d' % pid for inst in range(s.get_num_instructions(pid)): print ' %s' % s.get_instruction(pid, inst) print '' print 'Important behaviors:' print ' System will switch when', if process_switch_behavior == SCHED_SWITCH_ON_IO: print 'the current process is FINISHED or ISSUES AN YIELD or IO' else: print 'error in sched switch on iobehavior' exit (-1) print ' After IOs, the process issuing the IO will', if io_done_behavior == IO_RUN_LATER: print 'run LATER (when it is its turn)' else: print 'error in IO done behavior' exit (-1) print '' (cpu_busy, io_busy, clock_tick) = s.run() print '' print 'Stats: Total Time %d' % clock_tick print 'Stats: CPU Busy %d (%.2f%%)' % (cpu_busy, 100.0 * float(cpu_busy)/clock_tick) print 'Stats: IO Busy %d (%.2f%%)' % (io_busy, 100.0 * float(io_busy)/clock_tick) print ''