Homework for Chapter 4 of 《Operating Systems: Three Easy Pieces》: Process Intro

  •  111 views
  • Note:
    I converted the original process-run.py using ChatGPT because I am too lasy to have another python on my server. I shared the file below as someone may also need one.
    Hopefully it won’t involve me into copyright issues, but if it did, please kindly tell me.
    process-run.py for Python3

    And sure, if you’re not interested in setting your own parameters to do further testing, or you happen to be inconvenient to run the script yourself, you can just refer to the outputs I provided with below.

    1. python3 ./process-run.py -l 5:100,5:100
      Expected: The CPU using rate will be 100%.
      Actual: It did.
    ~# python3 ./process-run.py -l 5:100,5:100 -c -p
    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
    
    Stats: Total Time 10
    Stats: CPU Busy 10 (100.00%)
    Stats: IO Busy  0 (0.00%)

    As there is no chance that a IO operation will be executed, the CPU is always occupied.

    1. python3 ./process-run.py -l 4:100,1:0 -c -p
      Expected: I forget whether another process will be switched to simutaneously when the current process switches into IO state or not. My instinct tells me it’s not. IOs cost one unit and CPU cost 4 units of time, so I thought it would be 5 units of time.
      Actual: It takes 10 units.
    ~# python3 ./process-run.py -l 4:100,1:0 -c -p
    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            DONE           RUN:io           1
      6            DONE           WAITING                      1
      7            DONE           WAITING                      1
      8            DONE           WAITING                      1
      9            DONE           WAITING                      1
     10*           DONE           DONE
    
    Stats: Total Time 10
    Stats: CPU Busy 5 (50.00%)
    Stats: IO Busy  4 (40.00%)

    I neglected the fact that a IO process takes 5 units of time by default. However, there is one thing worth noticing: the final time segment. It simply doesn’t nothing but was still counted into total times, and has a special asterisk mark on itself. Personally I think it should not be counted as it has no active task being executed actually.

    1. python3 ./process-run.py -l 1:0,4:100 -c -p
      Expected: As the sequence changes, in the first unit of time, a IO process will be activated and the current process will be switched to the 2nd process. And as the policy is switching to another when the whole process is done, it won’t switch back to the first even if the io process is done. So I thought it would be:
      1 2
      1 IO, wait
      2 wait, CPU
      3 wait, CPU
      4 wait, CPU
      5 IO fin,but still waiting CPU
      6* done done

    Actual: Partially correct.

    ~# python3 ./process-run.py -l 1:0,4:100 -c -p
    Time           PID: 0           PID: 1           CPU           IOs
    
      1            RUN:io           READY           1
      2            WAITING           RUN:cpu           1           1
      3            WAITING           RUN:cpu           1           1
      4            WAITING           RUN:cpu           1           1
      5            WAITING           RUN:cpu           1           1
      6*           DONE           DONE
    
    Stats: Total Time 6
    Stats: CPU Busy 5 (83.33%)
    Stats: IO Busy  4 (66.67%)

    At first It seems my guessing is wrong as it immediately switchs its current process and make good use of the vacant CPU when a IO happens. But I noticed that the first CPU period is actually used by the first process and it’s also counted into IO length! That is so confusing as there is not only IO usage counted in the IO length.
    Furthermore, by “Partially correct” I mean while I was writing the correct sequence of processingm I overlooked the actual state of both processes. If a process is not blocked it should not be “waiting” but “ready”.

    1. python3 ./process-run.py -l 1:0,4:100 -c -S SWITCH_ON_END
      Expected: SWITCH_ON_END means switch to another process when the current whole process is end, which means you can’t make good use of the idle CPU. So it would be:

    RUN:IO READY
    WAITING READY
    WAITING READY
    WAITING READY
    WAITING READY
    DONE RUN:CPU
    DONE RUN:CPU
    DONE RUN:CPU
    DONE RUN:CPU
    DONE DONE

    Actual: It does.

    ~# python3 ./process-run.py -l 1:0,4:100 -c -S SWITCH_ON_END
    Time           PID: 0           PID: 1           CPU           IOs
    
      1            RUN:io           READY           1
      2            WAITING           READY                      1
      3            WAITING           READY                      1
      4            WAITING           READY                      1
      5            WAITING           READY                      1
      6*           DONE           RUN:cpu           1
      7            DONE           RUN:cpu           1
      8            DONE           RUN:cpu           1
      9            DONE           RUN:cpu           1

    Notice that there is no double DONE for the last segment. I don’t know why but I don’t care 😀

    1. python3 ./process-run.py -l 1:0,4:100 -c -S SWITCH_ON_IO
      Expected:
      RUN:IO READY
      WAITING RUN:CPU
      WAITING RUN:CPU
      WAITING RUN:CPU
      WAITING RUN:CPU
      DONE DONE

    Actually:

    ~# python3 ./process-run.py -l 1:0,4:100 -c -S SWITCH_ON_IO
    Time           PID: 0           PID: 1           CPU           IOs
    
      1            RUN:io           READY           1
      2            WAITING           RUN:cpu           1           1
      3            WAITING           RUN:cpu           1           1
      4            WAITING           RUN:cpu           1           1
      5            WAITING           RUN:cpu           1           1
      6*           DONE           DONE

    Now I have another guessing that the asterisk is to especially point out when the IO process actually done.

    1. python3 ./process-run.py -l 3:0,5:100,5:100,5:100 -S SWITCH_ON_IO -IIO_RUN_LATER -c -p

    This one is kinda complicated at the first glance. But Noticed that there is only one IO related process and it won’t switch back until another CPU related process is done. Roughly I thought it would be:
    RUN:IO READY READY READY
    WAITING RUN:CPU READY …
    WAITING RUN:CPU READY …
    WAITING RUN:CPU READY …
    WAITING RUN:CPU READY …
    RUN:IO DONE READY …
    WAITING DONE RUN:CPU …

    Actually:

    ~# python3 ./process-run.py -l 3:0,5:100,5:100,5:100 -S SWITCH_ON_IO -IIO_RUN_LATER -c -p
    Time           PID: 0           PID: 1           PID: 2           PID: 3           CPU           IOs
    
      1            RUN:io           READY           READY           READY           1
      2            WAITING           RUN:cpu           READY           READY           1           1
      3            WAITING           RUN:cpu           READY           READY           1           1
      4            WAITING           RUN:cpu           READY           READY           1           1
      5            WAITING           RUN:cpu           READY           READY           1           1
      6*           READY           RUN:cpu           READY           READY           1
      7            READY           DONE           RUN:cpu           READY           1
      8            READY           DONE           RUN:cpu           READY           1
      9            READY           DONE           RUN:cpu           READY           1
     10            READY           DONE           RUN:cpu           READY           1
     11            READY           DONE           RUN:cpu           READY           1
     12            READY           DONE           DONE           RUN:cpu           1
     13            READY           DONE           DONE           RUN:cpu           1
     14            READY           DONE           DONE           RUN:cpu           1
     15            READY           DONE           DONE           RUN:cpu           1
     16            READY           DONE           DONE           RUN:cpu           1
     17            RUN:io           DONE           DONE           DONE           1
     18            WAITING           DONE           DONE           DONE                      1
     19            WAITING           DONE           DONE           DONE                      1
     20            WAITING           DONE           DONE           DONE                      1
     21            WAITING           DONE           DONE           DONE                      1
     22*           RUN:io           DONE           DONE           DONE           1
     23            WAITING           DONE           DONE           DONE                      1
     24            WAITING           DONE           DONE           DONE                      1
     25            WAITING           DONE           DONE           DONE                      1
     26            WAITING           DONE           DONE           DONE                      1
     27*           DONE           DONE           DONE           DONE
    
    Stats: Total Time 27
    Stats: CPU Busy 18 (66.67%)
    Stats: IO Busy  12 (44.44%)

    I think I have a misunderstanding of IO_RUN_LATER. That LATER is later later, as it won’t switch back until all the other process finished.

    1. python3 ./process-run.py -l 3:0,5:100,5:100,5:100 -S SWITCH_ON_IO -IIO_RUN_IMMEDIATE -c -p
      Thought it would be:
      RUN:IO READY READY READY
      WAITING RUN:CPU READY …
      WAITING RUN:CPU READY …
      WAITING RUN:CPU READY …
      WAITING RUN:CPU READY …
      RUN:IO READY READY …
      WAITING RUN:CPU READY …
      WAITING DONE RUN:CPU …
      WAITING DONE RUN:CPU …
      WAITING DONE RUN:CPU …

    Actually:

    ~# python3 ./process-run.py -l 3:0,5:100,5:100,5:100 -S SWITCH_ON_IO -IIO_RUN_IMMEDIATE -c -p
    Time           PID: 0           PID: 1           PID: 2           PID: 3           CPU           IOs
    
      1            RUN:io           READY           READY           READY           1
      2            WAITING           RUN:cpu           READY           READY           1           1
      3            WAITING           RUN:cpu           READY           READY           1           1
      4            WAITING           RUN:cpu           READY           READY           1           1
      5            WAITING           RUN:cpu           READY           READY           1           1
      6*           RUN:io           READY           READY           READY           1
      7            WAITING           RUN:cpu           READY           READY           1           1
      8            WAITING           DONE           RUN:cpu           READY           1           1
      9            WAITING           DONE           RUN:cpu           READY           1           1
     10            WAITING           DONE           RUN:cpu           READY           1           1
     11*           RUN:io           DONE           READY           READY           1
     12            WAITING           DONE           RUN:cpu           READY           1           1
     13            WAITING           DONE           RUN:cpu           READY           1           1
     14            WAITING           DONE           DONE           RUN:cpu           1           1
     15            WAITING           DONE           DONE           RUN:cpu           1           1
     16*           DONE           DONE           DONE           RUN:cpu           1
     17            DONE           DONE           DONE           RUN:cpu           1
     18            DONE           DONE           DONE           RUN:cpu           1
    
    Stats: Total Time 18
    Stats: CPU Busy 18 (100.00%)
    Stats: IO Busy  12 (66.67%)
    

    Correct.
    IO-related process produces vacant CPU times. Starting IO process earlier means more chance for other CPU-related process to have better use of these vacant times.

    8.Randomly Generated Param:

    ~# python3 ./process-run.py -s 1 -l 3:50,3:50
    Produce a trace of what would happen when you run these processes:
    Process 0
      cpu
      io
      io
    
    Process 1
      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)

    Starts with process 1 but process 2 finishes earlier due to io waiting.

    # python3 ./process-run.py -s 1 -l 3:50,3:50 -c
    Time           PID: 0           PID: 1           CPU           IOs
    
      1            RUN:cpu           READY           1
      2            RUN:io           READY           1
      3            WAITING           RUN:cpu           1           1
      4            WAITING           RUN:cpu           1           1
      5            WAITING           RUN:cpu           1           1
      6            WAITING           DONE                      1
      7*           RUN:io           DONE           1
      8            WAITING           DONE                      1
      9            WAITING           DONE                      1
     10            WAITING           DONE                      1
     11            WAITING           DONE                      1
     12*           DONE           DONE
     ~# python3 ./process-run.py -s 2 -l 3:50,3:50
    Produce a trace of what would happen when you run these processes:
    Process 0
      io
      io
      cpu
    
    Process 1
      cpu
      io
      io
    
    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)

    Two processes finishes almost at the same time as they have the same proportion of IO tasks even though the sequence is different, which produces more cpu vacant time.

    ~# python3 ./process-run.py -s 2 -l 3:50,3:50 -c
    Time           PID: 0           PID: 1           CPU           IOs
    
      1            RUN:io           READY           1
      2            WAITING           RUN:cpu           1           1
      3            WAITING           RUN:io           1           1
      4            WAITING           WAITING                      2
      5            WAITING           WAITING                      2
      6*           RUN:io           WAITING           1           1
      7            WAITING           WAITING                      2
      8*           WAITING           RUN:io           1           1
      9            WAITING           WAITING                      2
     10            WAITING           WAITING                      2
     11*           RUN:cpu           WAITING           1           1
     12            DONE           WAITING                      1
     13*           DONE           DONE
    python3 ./process-run.py -s 3 -l 3:50,3:50
    Produce a trace of what would happen when you run these processes:
    Process 0
      cpu
      io
      cpu
    
    Process 1
      io
      io
      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)

    Process 0 finishes earlier and there is too much cpu vacant time.

    ~# python3 ./process-run.py -s 3 -l 3:50,3:50 -c
    Time           PID: 0           PID: 1           CPU           IOs
    
      1            RUN:cpu           READY           1
      2            RUN:io           READY           1
      3            WAITING           RUN:io           1           1
      4            WAITING           WAITING                      2
      5            WAITING           WAITING                      2
      6            WAITING           WAITING                      2
      7*           RUN:cpu           WAITING           1           1
      8*           DONE           RUN:io           1
      9            DONE           WAITING                      1
     10            DONE           WAITING                      1
     11            DONE           WAITING                      1
     12            DONE           WAITING                      1
     13*           DONE           RUN:cpu           1

    I have no idea if I should toy with the -I and -S options. But let’s have a look at -S SWITCH_ON_END first. I think it would be a disaster if there are two much IO-related tasks. Take
    python3 ./process-run.py -s 2 -l 3:50,3:50 as a Example:

    root@C202511211157657:~# python3 ./process-run.py -s 2 -l 3:50,3:50 -S SWITCH_ON_END -c
    Time           PID: 0           PID: 1           CPU           IOs
    
      1            RUN:io           READY           1
      2            WAITING           READY                      1
      3            WAITING           READY                      1
      4            WAITING           READY                      1
      5            WAITING           READY                      1
      6*           RUN:io           READY           1
      7            WAITING           READY                      1
      8            WAITING           READY                      1
      9            WAITING           READY                      1
     10            WAITING           READY                      1
     11*           RUN:cpu           READY           1
     12            DONE           RUN:cpu           1
     13            DONE           RUN:io           1
     14            DONE           WAITING                      1
     15            DONE           WAITING                      1
     16            DONE           WAITING                      1
     17            DONE           WAITING                      1
     18*           DONE           RUN:io           1
     19            DONE           WAITING                      1
     20            DONE           WAITING                      1
     21            DONE           WAITING                      1
     22            DONE           WAITING                      1
     23*           DONE           DONE
    

    Yes, the sequence grows almost 1 time bigger than the other one.

    As for the -IIO_RUN_LATER, I think it would have the exact same effect on our -s 2 scenario:

    ~# python3 ./process-run.py -s 2 -l 3:50,3:50 -IIO_RUN_LATER -c
    Time           PID: 0           PID: 1           CPU           IOs
    
      1            RUN:io           READY           1
      2            WAITING           RUN:cpu           1           1
      3            WAITING           RUN:io           1           1
      4            WAITING           WAITING                      2
      5            WAITING           WAITING                      2
      6*           RUN:io           WAITING           1           1
      7            WAITING           WAITING                      2
      8*           WAITING           RUN:io           1           1
      9            WAITING           WAITING                      2
     10            WAITING           WAITING                      2
     11*           RUN:cpu           WAITING           1           1
     12            DONE           WAITING                      1
     13*           DONE           DONE

    But actually it wasn’t. I think the reason maybe they both have almost the same proportion of IO, and if I change the ratio and instruction number for the 2nd process, there would be a disaster, let’s see:

     ~#  python3 ./process-run.py -s 2 -l 3:50,20:100 -IIO_RUN_LATER -c
    Time           PID: 0           PID: 1           CPU           IOs
    
      1            RUN:io           READY           1
      2            WAITING           RUN:cpu           1           1
      3            WAITING           RUN:cpu           1           1
      4            WAITING           RUN:cpu           1           1
      5            WAITING           RUN:cpu           1           1
      6*           READY           RUN:cpu           1
      7            READY           RUN:cpu           1
      8            READY           RUN:cpu           1
      9            READY           RUN:cpu           1
     10            READY           RUN:cpu           1
     11            READY           RUN:cpu           1
     12            READY           RUN:cpu           1
     13            READY           RUN:cpu           1
     14            READY           RUN:cpu           1
     15            READY           RUN:cpu           1
     16            READY           RUN:cpu           1
     17            READY           RUN:cpu           1
     18            READY           RUN:cpu           1
     19            READY           RUN:cpu           1
     20            READY           RUN:cpu           1
     21            READY           RUN:cpu           1
     22            RUN:io           DONE           1
     23            WAITING           DONE                      1
     24            WAITING           DONE                      1
     25            WAITING           DONE                      1
     26            WAITING           DONE                      1
     27*           RUN:cpu           DONE           1

    For comparision let’s change the -I option:

     ~# python3 ./process-run.py -s 2 -l 3:50,20:100 -c -IIO_RUN_IMMEDIATE
    Time           PID: 0           PID: 1           CPU           IOs
    
      1            RUN:io           READY           1
      2            WAITING           RUN:cpu           1           1
      3            WAITING           RUN:cpu           1           1
      4            WAITING           RUN:cpu           1           1
      5            WAITING           RUN:cpu           1           1
      6*           RUN:io           READY           1
      7            WAITING           RUN:cpu           1           1
      8            WAITING           RUN:cpu           1           1
      9            WAITING           RUN:cpu           1           1
     10            WAITING           RUN:cpu           1           1
     11*           RUN:cpu           READY           1
     12            DONE           RUN:cpu           1
     13            DONE           RUN:cpu           1
     14            DONE           RUN:cpu           1
     15            DONE           RUN:cpu           1
     16            DONE           RUN:cpu           1
     17            DONE           RUN:cpu           1
     18            DONE           RUN:cpu           1
     19            DONE           RUN:cpu           1
     20            DONE           RUN:cpu           1
     21            DONE           RUN:cpu           1
     22            DONE           RUN:cpu           1
     23            DONE           RUN:cpu           1

    A considerable improvement. I think it would be more considerable if the number of IO tasks for the first process grows bigger.
    So we might draw the conclusion that -S SWITCH_ON_END would be a disaster if there is too much IO; and -IIO_RUN_LATER would be a disaster if there is a huge difference for the ratio of IO tasks and CPU tasks for each processes.

    Thanks for your reading. Leave a comment if you have any idea, question or mistakes found in the post.

    Leave a Reply

    Your email address will not be published. Required fields are marked *