# README Process Run This program, called `process-run.py`, allows you to see how the state of a process state changes as it runs on a CPU. As described in the chapter, processes can be in a few different states: ```text RUNNING - the process is using the CPU right now READY - the process could be using the CPU right now but (alas) some other process is WAITING - the process is waiting on I/O (e.g., it issued a request to a disk) DONE - the process is finished executing ``` In this homework, we'll see how these process states change as a program runs, and thus learn a little bit better how these things work. To run the program and get its options, do this: ```text prompt> ./process-run.py -h ```` If this doesn't work, type "python" before the command, like this: ```text prompt> python process-run.py -h ``` What you should see is this: ```text Usage: process-run.py [options] Options: -h, --help show this help message and exit -s SEED, --seed=SEED the random seed -l PROCESS_LIST, --processlist=PROCESS_LIST 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 -L IO_LENGTH, --iolength=IO_LENGTH how long an IO takes -S PROCESS_SWITCH_BEHAVIOR, --switch=PROCESS_SWITCH_BEHAVIOR when to switch between processes: SWITCH_ON_IO, SWITCH_ON_END -I IO_DONE_BEHAVIOR, --iodone=IO_DONE_BEHAVIOR type of behavior when IO ends: IO_RUN_LATER, IO_RUN_IMMEDIATE -c compute answers for me -p, --printstats print statistics at end; only useful with -c flag (otherwise stats are not printed) ``` The most important option to understand is the **PROCESS_LIST** (as specified by the -l or --processlist flags) which specifies exactly what each running program (or "process") will do. A process consists of instructions, and each instruction can just do one of two things: - use the CPU - issue an IO (and wait for it to complete) When a process uses the CPU (and does no IO at all), it should simply alternate between RUNNING on the CPU or being READY to run. For example, here is a simple run that just has one program being run, and that program only uses the CPU (it does no IO). ```text prompt> ./process-run.py -l 5:100 Produce a trace of what would happen when you run these processes: Process 0 cpu cpu cpu cpu cpu Important behaviors: System will switch when the current process is FINISHED or ISSUES AN IO After IOs, the process issuing the IO will run LATER (when it is its turn) prompt> ``` Here, the process we specified is "5:100" which means it should consist of 5 instructions, and the chances that each instruction is a CPU instruction are 100%. You can see what happens to the process by using the -c flag, which computes the answers for you: ```text prompt> ./process-run.py -l 5:100 -c Time PID: 0 CPU IOs 1 RUN:cpu 1 2 RUN:cpu 1 3 RUN:cpu 1 4 RUN:cpu 1 5 RUN:cpu 1 ```` This result is not too interesting: the process is simple in the **RUN** state and then finishes, using the CPU the whole time and thus keeping the CPU busy the entire run, and not doing any I/Os. Let's make it slightly more complex by running two processes: ```text prompt> ./process-run.py -l 5:100,5:100 Produce a trace of what would happen when you run these processes: Process 0 cpu cpu cpu cpu cpu Process 1 cpu cpu cpu cpu cpu Important behaviors: Scheduler will switch when the current process is FINISHED or ISSUES AN IO After IOs, the process issuing the IO will run LATER (when it is its turn) ``` In this case, two different processes run, each again just using the CPU. What happens when the operating system runs them? Let's find out: ```text prompt> ./process-run.py -l 5:100,5:100 -c Time PID: 0 PID: 1 CPU IOs 1 RUN:cpu READY 1 2 RUN:cpu READY 1 3 RUN:cpu READY 1 4 RUN:cpu READY 1 5 RUN:cpu READY 1 6 DONE RUN:cpu 1 7 DONE RUN:cpu 1 8 DONE RUN:cpu 1 9 DONE RUN:cpu 1 10 DONE RUN:cpu 1 ``` As you can see above, first the process with "process ID" (or "PID") 0 runs, while process 1 is READY to run but just waits until 0 is done. When 0 is finished, it moves to the DONE state, while 1 runs. When 1 finishes, the trace is done. Let's look at one more example before getting to some questions. In this example, the process just issues I/O requests. We specify here tht I/Os take 5 time units to complete with the flag -L. ```text prompt> ./process-run.py -l 3:0 -L 5 Produce a trace of what would happen when you run these processes: Process 0 io-start io-start io-start Important behaviors: System will switch when the current process is FINISHED or ISSUES AN IO After IOs, the process issuing the IO will run LATER (when it is its turn) ``` What do you think the execution trace will look like? Let's find out: ```text prompt> ./process-run.py -l 3:0 -L 5 -c Time PID: 0 CPU IOs 1 RUN:io-start 1 2 WAITING 1 3 WAITING 1 4 WAITING 1 5 WAITING 1 6* RUN:io-start 1 7 WAITING 1 8 WAITING 1 9 WAITING 1 10 WAITING 1 11* RUN:io-start 1 12 WAITING 1 13 WAITING 1 14 WAITING 1 15 WAITING 1 16* DONE ``` As you can see, the program just issues three I/Os. When each I/O is issued, the process moves to a WAITING state, and while the device is busy servicing the I/O, the CPU is idle. Let's print some stats (run the same command as above, but with the `-p flag) to see some overall behaviors: ```text ... Stats: Total Time 16 Stats: CPU Busy 3 (18.75%) Stats: IO Busy 12 (75.00%) ``` As you can see, the trace took 16 clock ticks to run, but the CPU was only busy less than 20% of the time. The IO device, on the other hand, was quite busy. In general, we'd like to keep all the devices busy, as that is a better use of resources. There are a few other important flags: * `-s SEED, --seed=SEED` the random seed this gives you way to create a bunch of different jobs randomly * `-L IO_LENGTH, --iolength=IO_LENGTH` this determines how long IOs take to complete (default is 5 ticks) * `-S PROCESS_SWITCH_BEHAVIOR, --switch=PROCESS_SWITCH_BEHAVIOR` when to switch between processes: `SWITCH_ON_IO, SWITCH_ON_END` this determines when we switch to another process: - `SWITCH_ON_IO`, the system will switch when a process issues an IO - `SWITCH_ON_END`, the system will only switch when the current process is done * `-I IO_DONE_BEHAVIOR, --iodone=IO_DONE_BEHAVIOR` type of behavior when IO ends: `IO_RUN_LATER`, `IO_RUN_IMMEDIATE` this determines when a process runs after it issues an IO: - `IO_RUN_IMMEDIATE`: switch to this process right now - `IO_RUN_LATER`: switch to this process when it is natural to (e.g., depending on process-switching behavior)