------------------------------------------------------------------------------- | Additional Change Needed for Objective 2 | ------------------------------------------------------------------------------- In obj2.c, you need to change the agent number that gets passed to Add_event() from 0 to (CPU.actvpcb->termnl + 1) in the function Cpu() for the SIO, WIO, and END instructions. 0 was used as the agent field for the boot program in objective 2; however, it is stated, although probably forgotten, that this should be (CPU.actvpcb->termnl + 1) for all other objectives so that, for instance, an SIO instruction will have the correct terminal matched up with it. This is important in functions such as Sio_Service() when you try to retrieve the pcb for the process that executed the SIO instruction, which is done by letting pcb = termtable[ AGENT - 1 ]. Otherwise, you will have pcb = termtable[ 0 - 1 ] = termtable[ -1 ], and your program will probably dump core. ------------------------------------------------------------------------------- | Scheduling Algorithm | ------------------------------------------------------------------------------- Only the First-Come-First-Serve (FCFS) scheduling algorithm must be implemented for objective 4. Although more than one scheduling algorithm can be used, we will only be doing FCFS. The scheduling algorithm to be used is a parameter in config.dat that the simulator reads in--the simulator will set the global variable SCHED to the constant FCFS after reading the parameter in. ------------------------------------------------------------------------------- | Computing the Device ID and Request ID in Sio_Service() and Wio_Service() | ------------------------------------------------------------------------------- First, I recommend looking at example 4 in intro.doc in order to understand how to interpret the I/O instructions SIO, WIO, REQ, and END. When you are in Sio_Service(), you need to start I/O on a device, the ID of which lies in the next instruction in MEM. For instance, you may have this: SIO 25 DISK 400 You are at SIO, but need to use the next instruction so you know that the I/O device is a DISK. The index into MEM that you need is this (which is given in the PPT slide): dev_addr = (int) MEMMAP[rb->pcb->cpu_save.pc.segment].membase + rb->pcb->cpu_save.pc.offset - 1 Now that you have this index, you can retrieve the opcode and operand fields for the device in MEM. You then need to convert the opcode into a device ID for the rb's dev field. Remember that in objective 2 you had to come up with your own non-conflicting value for device opcodes since only real instructions (SIO, WIO, etc.) had opcodes, not devices. Hence, you will need to do the inverse of this operation to convert from an opcode to a device ID. For example, suppose in objective 2 that you used an opcode of (devid + OPCDSIZE + 1) for devices. Then you would need to set the rb's dev ID to (opcode - OPCDSIZE - 1). Note that you may have used an alternate method in objective 2 for assigning opcodes to devices. This is fine as long as you can undo the method to retrieve a device ID from an opcode. Similarly, for Wio_Service(), you have this kind of setup when processing a WIO event: WIO 0 REQ [0,3] You need to get to the next instruction in MEM to figure out which device to wait for. You use the same method for calculating the request address as you did in Sio_Service() when you calculated the device address. The only difference is that you don't have to use a request block to get to the pcb; that is, you can just use pcb->cpu_save.pc.segment instead of rb->pcb->cpu_save... when getting the address. Of course, you need to retrieve the pcb from termtable first, though. ------------------------------------------------------------------------------- | Handling Time | ------------------------------------------------------------------------------- In objective 4, there are two main issues that must be dealt with: correctly handling I/O request blocks and recording time statistics. Approximately 80% of the work lies in the former, but you will need to implement the timing fields also to receive the remainder of the credit for the assignment. As far as producing output, the only thing the timing statistics influence is the Terminal and Device Summary sections at the bottom of the output file--you should be able to match your output above that section by implementing the I/O requests correctly. You are strongly urged to implement the Calc_stats() function in objective 5 so you can test your timing results. The function is not difficult, and you should be able to just follow the PowerPoint slides. Time Fields to Set ------------------ Before you begin setting the time fields in objective 4, you need to first familiarize yourself with the structures and types related to time. Here is a list of all the important fields pertaining to time in the pcb, rb, device, and CPU structures: struct pcb_type { struct simtime logont; /* time of logon */ struct simtime blockedt; /* last time BLOCKED */ struct simtime readyt; /* last time READY */ struct simtime runt; /* last time ACTIVE */ struct simtime tblocked; /* total time BLOCKED */ struct simtime tready; /* total time READY */ struct simtime trun; /* total time ACTIVE */ struct simtime tlogon; /* total time LOGGED ON */ }; struct rb_type { struct simtime queuet; /* time rb enters the device queue */ }; struct device_type { struct simtime busy; /* total busy time */ struct simtime qwait; /* total q-wait time */ struct simtime response; /* average response */ struct simtime idle; /* total idle time */ struct simtime start; /* time that I/O starts on a device */ }; struct cpu_type { struct simtime busy; /* total busy time */ struct simtime qwait; /* total q-wait time */ struct simtime response; /* average response */ struct simtime idle; /* total idle time */ }; Setting the Time in Objective 3 ------------------------------- Now, you will first need to make some changes to certain functions in objective 3. Here are the functions that need changing along with what should be implemented. Logon_Service(): Record the time that a process logs on and is blocked--the pcb has two fields logont and blockedt that need to be set to the current clock time. The current clock time is stored in the global variable CLOCK. Next_pgm(): If a user logs off (i.e., pcb->script[++pcb->pgmid] == LOGOFF is true ), then calculate the total logon time for the pcb by subtracting the logon time from the logoff time (i.e., the current clock time) and then adding the result to the pcb's total logon time field, tlogon. Also, calculate the total blocked time in the same way, except set the pcb's field called tblocked by using the field blockedt and the current clock time. If a user does not logoff, record the pcb's ready time, readyt, to the current clock time. End_Service(): Calculate the time a process has been active--subtract the current clock time from the pcb's running time, runt, and add the result to the pcb's total running time, trun. Calculate the time the CPU has been busy--subtract the current clock time from the pcb's running time, runt, and add the result to the CPU's busy time field, busy. Record the time that a process became blocked--set the pcb's blockedt field to the current clock time. Setting the Time in Objective 4 ------------------------------- Here is the list of things you must do in in objective 4 along with the functions to do them in. Scheduler(): For the next active process/pcb, record the time that a process becomes active--set the pcb's run time to the current time. Calculate the total length of time that the process has been ready by taking the difference between the pcb's ready time and the current time. This difference should then be added to the pcb's total ready time and CPU's queue wait time. Sio_Service(): Record the time the request block was enqueued--set the rb's queue time to the current clock time. Start_IO(): Update the total wait time for the device--take the difference between the device's rb's queue time (devtable[dev].currb->queuet) and the current clock time and add this time to the device's queue wait time. Update the device's busy time--calculate the time an operation will take based on the bytes to transfer and service speed using the information on slide 41. Create a simtime called eiotime using the number of seconds and nanoseconds just calculated. Add this time to the device's busy time field. Create a future time event for the IO operation--call Add_event() using a time of eiotime, event of type ENDIO, and agent ID of dev + TRMSIZE + 1. Add_devq(): Set the rb queue time to the current time. Wio_Service(): Calculate the time a process has been active--subtract the current clock time from the pcb's running time, runt, and add the result to the pcb's total running time, trun. Calculate the time the CPU has been busy--subtract the current clock time from the pcb's running time, runt, and add the result to the CPU's busy time field, busy. Record the time that a process became blocked--set the pcb's blockedt field to the current clock time. Eio_Service(): If the pcb is blocked (i.e., pcb->status == 'B' is true), record the time the process became ready. Then calculate the total time the process has been blocked by taking the difference between the blocked time and the current time and adding this difference to the pcb's total blocked time. If the pcb is done (i.e., pcb->status == 'D' is true), record the time the process became ready. Then calculate the total time the process has been blocked by taking the difference between the blocked time and the current time and adding this difference to the pcb's total blocked time. ------------------------------------------------------------------------------- | Calculating and Displaying the Number of CPU Burst Instructions | ------------------------------------------------------------------------------- In order for your program to match ossim.out exactly, you must properly set and output a field called sjnburst that is a field of a pcb_type structure. Explanation of the Burst Field ------------------------------ The sjnburst field for a pcb keeps track of the number of instructions/cycles performed up to the point that the process ends execution or is blocked for I/O. For example, user 1's editor program is the first process to wait for I/O since we have the following section in editor.dat: SIO 23 PRNT 100 SIO 32 DISK 300 WIO 0 The WIO instruction is reached, so the process must wait for I/O. The burst count is 55 instructions since the first SIO has 23 instructions before it and the second one has 32. Hence, we have the following output in ossim.out: PROCESS U001 IS BLOCKED FOR I/O. CPU BURST WAS 55 INSTRUCTIONS. Now, once I/O for user 1 has completed and user 1's editor program can continue running, we execute the following instructions: SIO 15 PRNT 150 END 17 The END instruction is reached, so the process has completed. The burst count is 32 instructions since SIO has 15 instructions before it and END has 17. Hence, we have the following output in ossim.out: END OF PROGRAM EDITOR FOR PROCESS U001 CPU BURST WAS 32 INSTRUCTIONS. Notice that the burst count resets everytime the process is rescheduled to run since it had lost control of the CPU. How to Set and Print the Burst Field ------------------------------------ To correctly keep track of this information, you will need to add code to obj4.c, obj3.c, and obj2.c. First, here's what you must do in obj4.c. In the function Scheduler(), you must reset the burst count for the next program to run back to 0. In Wio_Service(), the "CPU BURST ..." message will be printed to simout for the pcb after printing the "PROCESS U... IS BLOCKED FOR I/O." message. In obj3.c, you will print the same "CPU BURST ..." message after the "END OF PROGRAM ..." message in the function End_Service(). Finally, in obj2.c, you will need to make a change in the function Cpu(). If the instruction is SIO, WIO, or END, then you will need to increment the CPU's active pcb's burst field (i.e., CPU.actvpcb->sjnburst) by the current operand's burst field (i.e., IREG.operand.burst if you called your temporary instruction IREG).