├── README.md ├── LICENSE ├── Lab Notes(old).txt ├── Lab Notes.txt └── Lab Notes - SmallEditAt - 9112022.txt /README.md: -------------------------------------------------------------------------------- 1 | # NachOS-SC2005-Operating_Systems-Lab_Guide 2 | A brief explanation about what is even going on. 3 | See 'Lab Notes - SmallEditAt - 9112022'. 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 zlw9991 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Lab Notes(old).txt: -------------------------------------------------------------------------------- 1 | NACHOS NOTES : SC2005 : NANYANG TECHNOLOGICAL UNIVERSITY : IDEALLY VIEW WITH MS PGOTHIC FONT SIZE 12 UTF-8 : BEST VIEWED IN NOTEPAD++ 2 |                        ,.、 3 |            _,,... -─-- 、.,_   /ヽ,ヽ, 4 |       ,.. '"´         `ヽ/::::/ヽ、> 5 |      /                /:::::::::/ヽ. 6 |     ,.'   ,            ヽ!`ヾイ:::::::::::! 7 |     ,'   /  / __ ! ./ | /|__」_ ',___!/Y´i ̄ 8 |    i.  ,'   7´i /|/   !/ !  ノ` ! | :  ! 9 |    i   !  /ァ'´`ヽ    ァ'"´ `ヽ! |.   | 10 |    !   ! /i   ()     ()   レ' |    | 11 |   .レヘレ',! `           - '/ !   | 12 |      /⊂⊃   rァ─- 、   ⊂⊃.!  i  | 13 |     ,'  ト、,   !    )   /.  /   !  ! 14 |     i ノイ i> .、.,,______   イi  /  ハ. | 15 |     .レ'´ レ'ヽ.  .,.イ〈 i____,.ノ ,|/レヘ/  V 16 |          `7 i::ヽレム /:::::::/`ヽ. 17 |          /  !::::::くン-'::::::::/     ':, 18 |        /  ,'::::::::::::::::::::::::i      i 19 | 20 | "I paid afew grand for this?" 21 | 22 | 23 | 24 | GENERAL NOTES: 25 | /* 26 | - Universal pointers used by many .cc files: see in system.cc 27 | 28 | IE: 29 | Thread *currentThread; // the thread we are running now 30 | Thread *threadToBeDestroyed; // the thread that just finished 31 | Scheduler *scheduler; // the ready list 32 | Interrupt *interrupt; // interrupt status 33 | Statistics *stats; // performance metrics 34 | Timer *timer // the hardware timer device, 35 | // for invoking context switches 36 | ^ All in system.cc 37 | 38 | Use these pointers in place of 'this' when calling outside of function's usual place. 39 | 40 | IE: 41 | original in timer.cc: interrupt->Schedule(TimerHandler, (_int) this, TimeOfNextInterrupt(), TimerInt); 42 | New: (void) interrupt->Schedule(TimerHandler, (_int)timer <-- universal pointer ,400,TimerInt); 43 | 44 | 45 | - If need be, copy functions into other .cc file portions between '#ifdef CHANGED' and '#endif'. 46 | 47 | IE: 48 | #ifdef CHANGED 49 | static void TimerHandler(_int arg) 50 | { Timer *p = (Timer *)arg; p->TimerExpired(); } <-- copied function 51 | .... 52 | #endif 53 | 54 | - WIP 55 | 56 | */ 57 | 58 | 59 | : // EXPERIMENT 1 60 | FUNCTIONS TO BE STUDIED FOR EXPERIMENT 1: 61 | /* 62 | thread.cc: Fork(),Thread(),Yield() 63 | scheduler.cc: contains functions used by Fork() and Yield() 64 | threadtest.cc: simplethread() 65 | 66 | */ 67 | 68 | // THEORY 69 | void 70 | SimpleThread(_int which) 71 | { 72 | int num; 73 | 74 | for (num = 0; num < 3; num++) { 75 | printf("*** thread %d looped %d times\n", (int) which, num); // runs a printf saying which current thread has looped how many times 76 | currentThread->Yield(); // yields the current thread, this means: 77 | // 1. The first thread in the ready Q is fetched. (nextThread = scheduler->FindNextToRun();) 78 | // 2. current thread is put back to end of ready queue (scheduler->ReadyToRun(this); , see scheduler.cc) 79 | // 3. The fetched thread is run. (scheduler->Run(nextThread);) 80 | 81 | // repeat untill each thread's simplethread function has run 3 times. 82 | /* 83 | like so: (main thread = 0 , '->' = yield()) 84 | 85 | 0 -> 1 -> 2 -> 0 -> 1 -> 2 -> 0 -> 1 -> 2 86 | 87 | see table.csv and output.txt 88 | 89 | */ 90 | } 91 | } 92 | 93 | 94 | Thread *t1 = new Thread("child1"); // constructs a thread named 'child1' 95 | t1->Fork(SimpleThread, 1, 0); 96 | // child1 thread, calling fork function, has the function simplethread queued into ready queue to run with arguement of '1' passed into function simplethread. 97 | // the '0' ^ above in Fork() represents if a thread should be joined or not ie: wait untill t1 finishes executing, '0' == do not join, '1' join. 98 | // see: Thread.cc Fork() 99 | 100 | Thread *t2 = new Thread("child2"); // repeat with child2 101 | t2->Fork(SimpleThread, 2, 0); 102 | 103 | SimpleThread(0); // main thread is now to run SimpleThread function with arguement 0 (ie: main thread is called thread 0) 104 | 105 | 106 | // NOTES ON HOW EXPERIMENT 1 ENDS: 107 | 1. With the last forked procedure yielding to the main thread 0 in the third loop, finish() is called. This runs: DEBUG('t', "Finishing thread %s #%i\n", getName(), pid); 108 | 2. It then sets threadToBeDestroyed = currentThread; which is itself. 109 | 3. It then runs sleep(). 110 | 4. In sleep(): DEBUG('t', "Sleeping thread %s #%i\n", getName(), pid); is run. 111 | 5. Since thread 1 n 2 are still in the Q, the while loop will not be triggered and it skips to: scheduler->Run(nextThread). 112 | 6. Under scheduler->Run(nextThread), DEBUG('t', "Switching from thread %s #%i to thread %s #%i\n",oldThread->getName(), oldThread->pid, nextThread->getName(),nextThread->pid); and 113 | DEBUG('t', "Now in thread %s #%i\n", currentThread->getName(), currentThread->pid); are run, this means the current thread is now 1. 114 | 7. Then: delete threadToBeDestroyed; is run, which calls the thread destructor on thread 0. 115 | 8. This deletes thread 0, as shown with: DEBUG('t', "Deleting thread %s #%i\n", name, pid); running. 116 | 9. repeat steps 1-8 with thread 1. 117 | 10. At thread 2, the aforementioned while loop will be triggered. This runs: interrupt->Idle(). 118 | 11. Under Idle{}, this will run: DEBUG('i', "Machine idling; checking for interrupts.\n"); 119 | 12. Since there are no interrupts, these will run: DEBUG('i', "Machine idle. No interrupts to do.\n"); 120 | printf("No threads ready or runnable, and no pending interrupts.\n"); 121 | printf("Assuming the program completed.\n"); 122 | 13. This signifies there is nothing left and the program ends. 123 | 124 | // HOW CODE RUNS: 125 | void 126 | SimpleThread(_int which) 127 | { 128 | int num; 129 | 130 | for (num = 0; num < 3; num++) { 131 | printf("*** thread %d looped %d times\n", (int) which, num); 132 | currentThread->Yield(); 133 | 134 | /* 135 | Tick 30: T0 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T0 (Sends it to the back of the Q) and fetches first item in Q which is thread T1. 136 | Tick 40: T1 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T1 and fetches first item in Q which is thread T2 137 | Tick 50: T2 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T2 and fetches first item in Q which is thread T0 138 | 139 | Tick 60: T0 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T0 and fetches first item in Q which is thread T1. 140 | Tick 70: T1 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T1 and fetches first item in Q which is thread T2 141 | Tick 80: T2 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T2 and fetches first item in Q which is thread T0 142 | 143 | Tick 90: T0 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T0 and fetches first item in Q which is thread T1. 144 | Tick 100: T1 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T1 and fetches first item in Q which is thread T2 145 | Tick 110: T2 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T2 and fetches first item in Q which is thread T0 146 | 147 | After 3 loops: 148 | Tick 120: T0 finish()->sleep()->(switch to T1)->delete T0 149 | Tick 130: T1 finish()->sleep()->(switch to T2)->delete T1 150 | Tick 140: T2 finish()->sleep()->idle()->nointerrupts = Kill Program. 151 | */ 152 | } 153 | } 154 | 155 | //---------------------------------------------------------------------- 156 | // ThreadTest 157 | // Set up a ping-pong between two threads, by forking a thread 158 | // to call SimpleThread, and then calling SimpleThread ourselves. 159 | //---------------------------------------------------------------------- 160 | 161 | void 162 | ThreadTest() 163 | { 164 | DEBUG('t', "Entering SimpleTest"); 165 | // each q will Q the function n id ie: simplethread , 1 166 | Thread *t1 = new Thread("child1"); 167 | t1->Fork(SimpleThread, 1, 0); // Tick 10 Q T1 168 | Thread *t2 = new Thread("child2"); 169 | t2->Fork(SimpleThread, 2, 0); // Tick 20 Q T2 170 | SimpleThread(0); // Tick 30 Run simplethread with main thread of id '0', main_thread will be referred to as T0 171 | } 172 | 173 | 174 | 175 | : // EXPERIMENT 2 176 | FUNCTIONS TO BE STUDIED FOR EXPERIMENT 2: 177 | // Thread.cc, thread.h 178 | Finish() 179 | Yield() 180 | 181 | //system.cc, timer.cc and timer.h 182 | TimerInterruptHandler() <--system.cc 183 | TimerExpired() 184 | TimeOfNextInterrupt() 185 | 186 | // interrupt.h and interrupt.cc 187 | Schedule() 188 | OneTick(); 189 | CheckIfDue() 190 | YieldOnReturn() 191 | 192 | // How functions interact with one another, based on part b): 193 | When a timer interrupt is to occur (ie: at 40): 194 | 1. OneTick() is run, which in turn runs CheckIfDue() as apart of a while loop. 195 | 2. CheckIfDue() then runs: DEBUG('i', "Invoking interrupt handler for the %s at time %d\n", intTypeNames[toOccur->type], toOccur->when). 196 | 3. As CheckIfDue() runs, it will eventually call the TimerHandler() from timer.cc with: (*(toOccur->handler))(toOccur->arg). 197 | 4. TimerHandler() then calls TimerExpired() 198 | 5. TimerExpired() then schedules the next Timer interrupt at present_time+TimeOfNextInterrupt(), this is shown when the interrupt->Schedule(..) section runs: DEBUG('i', "Scheduling interrupt handler the %s at time = %d\n", intTypeNames[type], when); 199 | 6. TimerExpired() then also calls TimerInterruptHandler() from system.cc with: (*handler)(arg); 200 | 7. TimerInterruptHandler() then calls YieldOnReturn(), which sets yieldOnReturn to true. 201 | 8. Since nothing further is called, we jump all the way back to CheckIfDue(), which then returns true. 202 | 9. As CheckIfDue() returns true, the while loop in part 1. will loop once, then in the next loop CheckIfDue() returns false since the pending interrupt has already been delivered. This breaks the while loop. 203 | 10. Since yieldOnReturn has been set to true, we enter the if else section in OneTick(), which then runs yield(). 204 | 11. When yield() is run, this sends the main thread to the ready queue and fetches the first thread in the ready Q to be run. 205 | 12. This allows for the forked threads to be run essentially. 206 | 13. When each forked thread ends, it runs finish(), which resets the next timer interrupt, and then calls sleep() 207 | 14. sleep() then calls for the next thread in the queue to be run. 208 | (I have no idea why, but only the last thread to be called to sleep before main is specifically deleted by a destructor. probably due to how scheduler.cc->Run()->SWITCH(oldThread, nextThread); snippet works.) 209 | (It is a mystery) 210 | 15. This process is repeated untill the main thread is run again. 211 | 16. Once at the main thread, the next bunch of threads will be forked. (Since we are now back at the main section of the code, see bottom for better explaination) 212 | 17. At some point the next timer interrupt will be due to occur, then we go back to step 1. repeat. 213 | 214 | 215 | PART A): 216 | 217 | // THEORY 218 | 219 | // Because part A wants us to trigger an interrupt every 40 seconds, this means at every 40 second interval: 220 | // The interrupt will be triggered 221 | // when first interrupt is triggered, all queued thread's functions (Simplethread) will be run one by one. 222 | // The second interrupt will be scheduled 40 seconds later. 223 | // each run thanks to for loop 'for (num = 0; num < 1000000; num++) {}' will take 10 ticks. 224 | // thus for each 10 tick one thread will be completed 225 | // At the second interrupt, we will jump back into the main code, where will fork the next 3 threads to Q (add thread's functions to Q). 226 | // The third interrupt will be scheduled 40 seconds later. 227 | // Each addition to the Q takes 10 ticks. 228 | // At the third interrupt, same thing happens as the first. 229 | // At the fourth interrupt, same thing happens as the second. 230 | // Repeat untill we reach SimpleThread(0). 231 | /* 232 | Tick 10 - 30: Fork (queue) threads 1-3 and their functions (SimpleThread) 233 | Tick 40: invokve interrupt, schedule next interrupt, switch to running queued threads (1-3) by calling yield(). 234 | Tick 50-70: run queued threads (1-3), at last thread, finish() calls sleep(), which switches us back to the main program for forking threads 4-6. 235 | Tick 80: invokve interrupt, schedule next interrupt, call yield() <- waste 236 | Tick 90-110: fork threads 4-6 237 | Tick 120: invokve interrupt, schedule next interrupt, switch to running queued threads (4-6) by calling yield(). 238 | Tick 130-150: run queued threads (4-6), at last thread, finish() calls sleep(), which switches us back to the main program for forking threads 7-9. 239 | Tick 160: invokve interrupt, schedule next interrupt, call yield() <- waste 240 | Tick 170-190: fork threads 7-9 241 | Tick 200: invokve interrupt, schedule next interrupt, switch to running queued threads(7-9) by calling yield(). 242 | Tick 210-230: run queued threads(7-9), at last thread, finish() calls sleep(), which switches us back to the main program. 243 | Tick 240: invokve interrupt, schedule next interrupt, call yield() <- waste 244 | Tick 250: run main thread with function SimpleThread ( SimpleThread(0);). Since nothing left, program completed 245 | */ 246 | 247 | // CODE TO BE EDITED 248 | 249 | //Under system.cc, set randomYield to TRUE: 250 | bool randomYield = TRUE; // timer is activated by this? i changed this. 251 | // This will permanently activate the function we need in Timer.cc 252 | 253 | // Next, in Timer.cc copy this section in Timer() into TimerExpired(). 254 | interrupt->Schedule(TimerHandler, (_int) this, TimeOfNextInterrupt(), 255 | TimerInt); 256 | // This will ensure TimerExpired will always schedule the next interrupt after a current interrupt is invoked.. 257 | 258 | // FInally, under TimeOfNextInterrupt(), set return to permanently 40, or comment out the section under if(randomize) and set it to permanently return 40, like so: 259 | if (randomize) 260 | return 40; // 1 + (Random() % (TimerTicks * 2)); <- original, changed to return 40 261 | // since randomize = true for timer to be activated 262 | else 263 | return TimerTicks; 264 | // This ensures, since random is set to True pemanently, that 40 ticks will always be returned when timer expires in an interrupt invocation. 265 | 266 | 267 | PART B): 268 | 269 | // THEORY 270 | 271 | // In part b), whenever a thread is finished running (ie: printf("Thread %d Completed.\n",(int)which);), they want the scheduled timer interrupt to be reset to the current tick + 40 ticks, ie: 272 | // if in tick 50, thread 1 finishes. the original scheduled timer interrupt at 80 seconds will now be at 90 seconds.. 273 | // This saves on ticks, as when the last thread that is not 'main thread' in Q runs, it also resets the scheduled next interrupt time, that means in the next 10 Ticks: 274 | // it can go straight to forking, without the need to spend a whole 10 Ticks invoke interrupt, schedule the next interrupt and running yield() . like in part a) 275 | // thus overall shorter time needed 276 | /* 277 | Tick 10-30: Fork 1-3 278 | Tick 40: Invoke Interrupt, next at 80, switch to running queued threads by calling yield(). 279 | Tick 50: Finish thread1, reset interrupt to 40 ticks later (Interrupt at 90) 280 | Tick 60: Finish thread2, Interrupt reset to 100 281 | Tick 70: Finish thread3, Interrupt reset to 110, at last thread, finish() calls sleep(), which switches us back to the main program for forking threads 282 | Tick 80 - 100: Fork 4-6 <-- Tick of 10 saved here 283 | Tick 110: Invoke Interrupt, next at 150, switch to running queued threads by calling yield(). 284 | Tick 120: Finish thread4, Interrupt reset to 160 285 | Tick 130: Finish thread5, Interrupt reset to 170 286 | Tick 140: Finish thread6, Interrupt reset to 180, at last thread, finish() calls sleep(), which switches us back to the main program for forking threads 287 | Tick 150-170: Fork 7-9 288 | Tick 180: Invoke Interrupt, next at 220, switch to running queued threads by calling yield(). 289 | Tick 190: Finish a thread7, Interrupt reset to 230 290 | Tick 200: Finish a thread8, Interrupt reset to 240 291 | Tick 210: Finish a thread9, Interrupt reset to 250, at last thread, finish() calls sleep(), which switches us back to the main program. 292 | Tick 220: Run main thread with function SimpleThread ( SimpleThread(0);). Since nothing left, program completed 293 | <30 ticks saved!> 294 | 295 | */ 296 | 297 | // CODE TO BE EDITED 298 | 299 | // Under threads.cc, find the finish() function portion, then: 300 | // Step 1. add in this code chunk after #ifdef CHANGED: 301 | #ifdef CHANGED 302 | static void TimerHandler(_int arg) 303 | { Timer *p = (Timer *)arg; p->TimerExpired(); } 304 | // This is a copy of a function that we will need to use. 305 | 306 | // Step 2. Under the finish() function, under both if else results, insert this one line above sleep(): 307 | (void) interrupt->Schedule(TimerHandler, (_int)timer ,400,TimerInt); 308 | // This snippet will tell the universal interrupt pointer (interrupt->) to : 309 | // 1. Feed the universal timer pointer (timer) to TimerHandler(). 310 | // 2. TimerHandler() (using universal timer pointer) signals that the current interrupt is expired and due to be replaced. 311 | // 3. That the next interrupt is to be 400 seconds from now, and that it is Type TimerInt (you may simply set this to 40 seconds instead) 312 | 313 | 314 | // Under interrupt.cc, check the schedule() function: 315 | // Add this snippet if you used 400 seconds from now: 316 | if(fromNow == 400){ 317 | delete pending->Remove(); 318 | fromNow = fromNow / 10;} // '400' signifies that this schedule call was from a thread finishing, and thus must be corrected. 319 | 320 | // Add this if you used 40 seconds: 321 | delete pending->Remove(); // This deletes the present scheduled interrupt. 322 | 323 | // The rest of the schedule() function will schedule a new interrupt at present time + 40, thus fulfilling our requirements. 324 | 325 | 326 | // HOW CODE RUNS: 327 | Part A) 328 | 329 | void 330 | SimpleThread(_int which) 331 | { 332 | int num; 333 | 334 | for (num = 0; num < 1000000; num++) {} 335 | printf("Thread %d Completed.\n",(int)which); 336 | } 337 | 338 | void 339 | ThreadTest() 340 | { 341 | DEBUG('t', "Entering SimpleTest"); // Interrupt Scheduled at 40 342 | 343 | Thread *t1 = new Thread("child1"); 344 | t1->Fork(SimpleThread, 1, 0); // Tick 10 Q T1 345 | Thread *t2 = new Thread("child2"); 346 | t2->Fork(SimpleThread, 2, 0); // Tick 20 Q T2 347 | Thread *t3 = new Thread("child3"); 348 | t3->Fork(SimpleThread, 3, 0); // Tick 30 Q T3 349 | 350 | // TICK 40 - INTERRUPT - NEXT SCHEDULED AT 80 351 | // YIELD() RUNS: T1 <- FIRST ITEM IN Q RETRIEVED, MAIN_THREAD ADDED TO BACK OF Q, RUNS T1 352 | // T1 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T2 353 | // T2 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T3 354 | // T3 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), T3 DELETED, PASS ON TO MAIN_THREAD 355 | 356 | // PROGRAM RETURNS BACK TO HERE 357 | // TICK 80 - INTERRUPT - NEXT SCHEDULED AT 120 358 | 359 | Thread *t4 = new Thread("child4"); 360 | t4->Fork(SimpleThread, 4, 0); // Tick 90 Q T4 361 | Thread *t5 = new Thread("child5"); 362 | t5->Fork(SimpleThread, 5, 0); // Tick 100 Q T5 363 | Thread *t6 = new Thread("child6"); 364 | t6->Fork(SimpleThread, 6, 0); // Tick 110 Q T6 365 | 366 | // TICK 120 - INTERRUPT - NEXT SCHEDULED AT 160 367 | // YIELD() RUNS: T4 <- FIRST ITEM IN Q RETRIEVED, MAIN_THREAD ADDED TO BACK OF Q, RUNS T4 368 | // T4 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T5 369 | // T5 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T6 370 | // T6 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), T6 DELETED, PASS ON TO MAIN_THREAD 371 | 372 | // PROGRAM RETURNS BACK TO HERE 373 | // TICK 160 - INTERRUPT - NEXT SCHEDULED AT 200 374 | 375 | Thread *t7 = new Thread("child7"); 376 | t7->Fork(SimpleThread, 7, 0); // Tick 170 Q T7 377 | Thread *t8 = new Thread("child8"); 378 | t8->Fork(SimpleThread, 8, 0); // Tick 180 Q T8 379 | Thread *t9 = new Thread("child9"); 380 | t9->Fork(SimpleThread, 9, 0); // Tick 190 Q T9 381 | 382 | 383 | // TICK 200 - INTERRUPT - NEXT SCHEDULED AT 240 384 | // YIELD() RUNS: T7 <- FIRST ITEM IN Q RETRIEVED, MAIN_THREAD ADDED TO BACK OF Q, RUNS T7 385 | // T7 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T8 386 | // T8 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T9 387 | // T9 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), T9 DELETED, PASS ON TO MAIN_THREAD 388 | 389 | // PROGRAM RETURNS BACK TO HERE 390 | // TICK 240 - INTERRUPT - NEXT SCHEDULED AT 280 391 | 392 | 393 | SimpleThread(0); // Tick 250: run this, program ends. as idle determines no threads, interrupts or anything in Q left. 394 | } 395 | 396 | Part B) 397 | 398 | void 399 | SimpleThread(_int which) 400 | { 401 | int num; 402 | 403 | for (num = 0; num < 1000000; num++) {} 404 | printf("Thread %d Completed.\n",(int)which); 405 | } 406 | 407 | void 408 | ThreadTest() 409 | { 410 | DEBUG('t', "Entering SimpleTest"); // Interrupt Scheduled at 40 411 | 412 | Thread *t1 = new Thread("child1"); 413 | t1->Fork(SimpleThread, 1, 0); // Tick 10 Q T1 414 | Thread *t2 = new Thread("child2"); 415 | t2->Fork(SimpleThread, 2, 0); // Tick 20 Q T2 416 | Thread *t3 = new Thread("child3"); 417 | t3->Fork(SimpleThread, 3, 0); // Tick 30 Q T3 418 | 419 | // TICK 40 - INTERRUPT - NEXT SCHEDULED AT 80 420 | // YIELD() RUNS: T1 <- FIRST ITEM IN Q RETRIEVED, MAIN_THREAD ADDED TO BACK OF Q, RUNS T1 421 | // TICK 50 - T1 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T2, NEXT INTERRUPT SCHEDULED AT 90 422 | // TICK 60 - T2 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T3, NEXT INTERRUPT SCHEDULED AT 100 423 | // TICK 70 - T3 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), T3 DELETED, PASS ON TO MAIN_THREAD, NEXT INTERRUPT SCHEDULED AT 110 424 | 425 | // PROGRAM RETURNS BACK TO HERE 426 | // nothing runs here: ticks saved 427 | 428 | Thread *t4 = new Thread("child4"); 429 | t4->Fork(SimpleThread, 4, 0); // Tick 80 Q T4 430 | Thread *t5 = new Thread("child5"); 431 | t5->Fork(SimpleThread, 5, 0); // Tick 90 Q T5 432 | Thread *t6 = new Thread("child6"); 433 | t6->Fork(SimpleThread, 6, 0); // Tick 100 Q T6 434 | 435 | // TICK 110 - INTERRUPT - NEXT SCHEDULED AT 150 436 | // YIELD() RUNS: T4 <- FIRST ITEM IN Q RETRIEVED, MAIN_THREAD ADDED TO BACK OF Q, RUNS T4 437 | // TICK 120 - T4 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T5, NEXT INTERRUPT SCHEDULED AT 160 438 | // TICK 130 - T5 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T6, NEXT INTERRUPT SCHEDULED AT 170 439 | // TICK 140 - T6 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), T6 DELETED, PASS ON TO MAIN_THREAD, NEXT INTERRUPT SCHEDULED AT 180 440 | 441 | // PROGRAM RETURNS BACK TO HERE 442 | // nothing runs here: ticks saved 443 | 444 | Thread *t7 = new Thread("child7"); 445 | t7->Fork(SimpleThread, 7, 0); // Tick 150 Q T7 446 | Thread *t8 = new Thread("child8"); 447 | t8->Fork(SimpleThread, 8, 0); // Tick 160 Q T8 448 | Thread *t9 = new Thread("child9"); 449 | t9->Fork(SimpleThread, 9, 0); // Tick 170 Q T9 450 | 451 | 452 | // TICK 180 - INTERRUPT - NEXT SCHEDULED AT 220 453 | // YIELD() RUNS: T7 <- FIRST ITEM IN Q RETRIEVED, MAIN_THREAD ADDED TO BACK OF Q, RUNS T7 454 | // TICK 190 - T7 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T8, NEXT INTERRUPT SCHEDULED AT 230 455 | // TICK 200 - T8 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T9, NEXT INTERRUPT SCHEDULED AT 240 456 | // TICK 210 - T9 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), T9 DELETED, PASS ON TO MAIN_THREAD, NEXT INTERRUPT SCHEDULED AT 250 457 | 458 | // PROGRAM RETURNS BACK TO HERE 459 | // nothing runs here: ticks saved 460 | 461 | 462 | SimpleThread(0); // Tick 220: run this, program ends. as idle determines no threads, interrupts or anything in Q left. 463 | } 464 | 465 | : // EXPERIMENT 3 466 |        _,,..-=ニ二7 467 |      r'ア´ 468 |     ノノ   r'ア'"`ヽ.,_ _,,..-=-、    _,. -rァ 469 |    r'ァ⌒ヽ、i7::::::::;>''"´:: ̄ ̄`"''<´:::::::::!( 470 |    .||    r!:::/::::::::::::::::::::::::::::::::::::::::ヽ:::::/i' 471 |    ||      ^Y:::::;:::::i:::::::::/i::::::i:::::::;:::::::;::::Y (_ 472 |    ||    ./i::::::i:::/!--/ |:::;ハ_!_::i::::::i:::::::i r'ヽ. 473 |    !!    く:::L__ハ/-‐‐'  レ' _!__;ハ::::ハ:::::|_,ゝ:::', 474 |    ',',ヽ.   ヽヘ「7""         `レ7´)/:::iヽ;:::i 475 |    i´`とン' ´`ヽ!,人  「' ̄ `i ""7_/'´':::::::! i:::!    "What the fuck is going on" 476 |    ヽ.,_//」 、_,ノ:::::ノ>.、、,___,ノ_,,.イ:::!、__!7ノ__. レ' 477 |      i   ゝ-ァ'/ /)_iヽ/ /(/ゝ、.,_ノ   ̄「iー-、 478 |     ノ〈)    `  /::::ソ^ヽ、/」::_r' _/ /」      |つ-' 479 |    <.,  _____,,,... イ::::くr-、_」:::::::::Y^ヽ、    [] ', 480 |       ̄  レ'   l>-、::;;_______;;::」〉'ノヽ.   __ 〉 481 |    ((    r'ア'"/:::/ i ヽ;::::ヽ`''::ーァ、`''ー-┴'" 482 |         r'i:::/::::::/ .l   ';:::::::::::::::::!,」 483 |         `ゝ、:::::/  l   ヽ;::::::::::/」 484 |           └へ>、,_!_______,,..>ァニン! 485 |             'r-- 'i    ̄ヽ,_ノ 486 |             ヽ二ノ 487 | -------------------------------------------------------------------------------- /Lab Notes.txt: -------------------------------------------------------------------------------- 1 | NACHOS NOTES : SC2005 : NANYANG TECHNOLOGICAL UNIVERSITY : IDEALLY VIEW WITH MS PGOTHIC FONT SIZE 12 UTF-8 : BEST VIEWED IN NOTEPAD++ 2 |                        ,.、 3 |            _,,... -─-- 、.,_   /ヽ,ヽ, 4 |       ,.. '"´         `ヽ/::::/ヽ、> 5 |      /                /:::::::::/ヽ. 6 |     ,.'   ,            ヽ!`ヾイ:::::::::::! 7 |     ,'   /  / __ ! ./ | /|__」_ ',___!/Y´i ̄ 8 |    i.  ,'   7´i /|/   !/ !  ノ` ! | :  ! 9 |    i   !  /ァ'´`ヽ    ァ'"´ `ヽ! |.   | 10 |    !   ! /i   ()     ()   レ' |    | 11 |   .レヘレ',! `           - '/ !   | 12 |      /⊂⊃   rァ─- 、   ⊂⊃.!  i  | 13 |     ,'  ト、,   !    )   /.  /   !  ! 14 |     i ノイ i> .、.,,______   イi  /  ハ. | 15 |     .レ'´ レ'ヽ.  .,.イ〈 i____,.ノ ,|/レヘ/  V 16 |          `7 i::ヽレム /:::::::/`ヽ. 17 |          /  !::::::くン-'::::::::/     ':, 18 |        /  ,'::::::::::::::::::::::::i      i 19 | 20 | "I paid afew grand for this?" 21 | 22 | 23 | 24 | GENERAL NOTES: 25 | /* 26 | - Universal pointers used by many .cc files: see in system.cc 27 | 28 | IE: 29 | Thread *currentThread; // the thread we are running now 30 | Thread *threadToBeDestroyed; // the thread that just finished 31 | Scheduler *scheduler; // the ready list 32 | Interrupt *interrupt; // interrupt status 33 | Statistics *stats; // performance metrics 34 | Timer *timer // the hardware timer device, 35 | // for invoking context switches 36 | ^ All in system.cc 37 | 38 | Use these pointers in place of 'this' when calling outside of function's usual place. 39 | 40 | IE: 41 | original in timer.cc: interrupt->Schedule(TimerHandler, (_int) this, TimeOfNextInterrupt(), TimerInt); 42 | New: (void) interrupt->Schedule(TimerHandler, (_int)timer <-- universal pointer ,400,TimerInt); 43 | 44 | 45 | - If need be, copy functions into other .cc file portions between '#ifdef CHANGED' and '#endif'. 46 | 47 | IE: 48 | #ifdef CHANGED 49 | static void TimerHandler(_int arg) 50 | { Timer *p = (Timer *)arg; p->TimerExpired(); } <-- copied function 51 | .... 52 | #endif 53 | 54 | - WIP 55 | 56 | */ 57 | 58 | 59 | : // EXPERIMENT 1 60 | FUNCTIONS TO BE STUDIED FOR EXPERIMENT 1: 61 | /* 62 | thread.cc: Fork(),Thread(),Yield() 63 | scheduler.cc: contains functions used by Fork() and Yield() 64 | threadtest.cc: simplethread() 65 | 66 | */ 67 | 68 | // THEORY 69 | void 70 | SimpleThread(_int which) 71 | { 72 | int num; 73 | 74 | for (num = 0; num < 3; num++) { 75 | printf("*** thread %d looped %d times\n", (int) which, num); // runs a printf saying which current thread has looped how many times 76 | currentThread->Yield(); // yields the current thread, this means: 77 | // 1. The first thread in the ready Q is fetched. (nextThread = scheduler->FindNextToRun();) 78 | // 2. current thread is put back to end of ready queue (scheduler->ReadyToRun(this); , see scheduler.cc) 79 | // 3. The fetched thread is run. (scheduler->Run(nextThread);) 80 | 81 | // repeat untill each thread's simplethread function has run 3 times. 82 | /* 83 | like so: (main thread = 0 , '->' = yield()) 84 | 85 | 0 -> 1 -> 2 -> 0 -> 1 -> 2 -> 0 -> 1 -> 2 86 | 87 | see table.csv and output.txt 88 | 89 | */ 90 | } 91 | } 92 | 93 | 94 | Thread *t1 = new Thread("child1"); // constructs a thread named 'child1' 95 | t1->Fork(SimpleThread, 1, 0); 96 | // child1 thread, calling fork function, has the function simplethread queued into ready queue to run with arguement of '1' passed into function simplethread. 97 | // the '0' ^ above in Fork() represents if a thread should be joined or not ie: wait untill t1 finishes executing, '0' == do not join, '1' join. 98 | // see: Thread.cc Fork() 99 | 100 | Thread *t2 = new Thread("child2"); // repeat with child2 101 | t2->Fork(SimpleThread, 2, 0); 102 | 103 | SimpleThread(0); // main thread is now to run SimpleThread function with arguement 0 (ie: main thread is called thread 0) 104 | 105 | 106 | // NOTES ON HOW EXPERIMENT 1 ENDS: 107 | 1. With the last forked procedure yielding to the main thread 0 in the third loop, finish() is called. This runs: DEBUG('t', "Finishing thread %s #%i\n", getName(), pid); 108 | 2. It then sets threadToBeDestroyed = currentThread; which is itself. 109 | 3. It then runs sleep(). 110 | 4. In sleep(): DEBUG('t', "Sleeping thread %s #%i\n", getName(), pid); is run. 111 | 5. Since thread 1 n 2 are still in the Q, the while loop will not be triggered and it skips to: scheduler->Run(nextThread). 112 | 6. Under scheduler->Run(nextThread), DEBUG('t', "Switching from thread %s #%i to thread %s #%i\n",oldThread->getName(), oldThread->pid, nextThread->getName(),nextThread->pid); and 113 | DEBUG('t', "Now in thread %s #%i\n", currentThread->getName(), currentThread->pid); are run, this means the current thread is now 1. 114 | 7. Then: delete threadToBeDestroyed; is run, which calls the thread destructor on thread 0. 115 | 8. This deletes thread 0, as shown with: DEBUG('t', "Deleting thread %s #%i\n", name, pid); running. 116 | 9. repeat steps 1-8 with thread 1. 117 | 10. At thread 2, the aforementioned while loop will be triggered. This runs: interrupt->Idle(). 118 | 11. Under Idle{}, this will run: DEBUG('i', "Machine idling; checking for interrupts.\n"); 119 | 12. Since there are no interrupts, these will run: DEBUG('i', "Machine idle. No interrupts to do.\n"); 120 | printf("No threads ready or runnable, and no pending interrupts.\n"); 121 | printf("Assuming the program completed.\n"); 122 | 13. This signifies there is nothing left and the program ends. 123 | 124 | // HOW CODE RUNS: 125 | void 126 | SimpleThread(_int which) 127 | { 128 | int num; 129 | 130 | for (num = 0; num < 3; num++) { 131 | printf("*** thread %d looped %d times\n", (int) which, num); 132 | currentThread->Yield(); 133 | 134 | /* 135 | Tick 30: T0 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T0 (Sends it to the back of the Q) and fetches first item in Q which is thread T1. 136 | Tick 40: T1 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T1 and fetches first item in Q which is thread T2 137 | Tick 50: T2 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T2 and fetches first item in Q which is thread T0 138 | 139 | Tick 60: T0 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T0 and fetches first item in Q which is thread T1. 140 | Tick 70: T1 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T1 and fetches first item in Q which is thread T2 141 | Tick 80: T2 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T2 and fetches first item in Q which is thread T0 142 | 143 | Tick 90: T0 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T0 and fetches first item in Q which is thread T1. 144 | Tick 100: T1 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T1 and fetches first item in Q which is thread T2 145 | Tick 110: T2 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T2 and fetches first item in Q which is thread T0 146 | 147 | After 3 loops: 148 | Tick 120: T0 finish()->sleep()->(switch to T1)->delete T0 149 | Tick 130: T1 finish()->sleep()->(switch to T2)->delete T1 150 | Tick 140: T2 finish()->sleep()->idle()->nointerrupts = Kill Program. 151 | */ 152 | } 153 | } 154 | 155 | //---------------------------------------------------------------------- 156 | // ThreadTest 157 | // Set up a ping-pong between two threads, by forking a thread 158 | // to call SimpleThread, and then calling SimpleThread ourselves. 159 | //---------------------------------------------------------------------- 160 | 161 | void 162 | ThreadTest() 163 | { 164 | DEBUG('t', "Entering SimpleTest"); 165 | // each q will Q the function n id ie: simplethread , 1 166 | Thread *t1 = new Thread("child1"); 167 | t1->Fork(SimpleThread, 1, 0); // Tick 10 Q T1 168 | Thread *t2 = new Thread("child2"); 169 | t2->Fork(SimpleThread, 2, 0); // Tick 20 Q T2 170 | SimpleThread(0); // Tick 30 Run simplethread with main thread of id '0', main_thread will be referred to as T0 171 | } 172 | 173 | 174 | 175 | : 176 |           __,.、__,.、_ 177 |         _r'"7_,.!-‐'-.、7-、_ 178 |       r´ >'"       ̄`ヾ!、 179 |      rゝ'´/   /i.   i 、  Y 180 | . /|   /Y ./ /メ、ハ  /| ハ   | 181 | || / ノ/! イ ○ レ' レ_,. レ'ヽ,ゝ 182 | r'-'┐/ _,ノ ノ|  "     O |ソ|   /| 183 | `l^ヽ、ヘ⌒ヽr,ヘ.  /´`ヽ  "从 ',  / .| 184 |  ヽ、_ノ7  (Y,/」>!、_,/_,. イノヘ〉r// 185 |    `ー-∠ト、7:::|/ムヽノ:::i´`7"´__ン、> 186 |    ノ\_rヘく:::::::::::ハ::::::::/-‐' ̄ 187 |    r'ア'"::::`''ーァ二ニ=-、イ、7 188 |  ノ7:::::/::::::::/|}  ノ/⌒ヽ>、 189 |  }」/::::::::::::/:::}|___,ンi二二!-' 190 |   iヽ、_:::__ンニ='-‐=-'!、__.ノ 191 |   `7二`/´ 192 |   〈__ン 193 | 194 | // EXPERIMENT 2 195 | FUNCTIONS TO BE STUDIED FOR EXPERIMENT 2: 196 | // Thread.cc, thread.h 197 | Finish() 198 | Yield() 199 | 200 | //system.cc, timer.cc and timer.h 201 | TimerInterruptHandler() <--system.cc 202 | TimerExpired() 203 | TimeOfNextInterrupt() 204 | 205 | // interrupt.h and interrupt.cc 206 | Schedule() 207 | OneTick(); 208 | CheckIfDue() 209 | YieldOnReturn() 210 | 211 | // How functions interact with one another, based on part b): 212 | When a timer interrupt is to occur (ie: at 40): 213 | 1. OneTick() is run, which in turn runs CheckIfDue() as apart of a while loop. 214 | 2. CheckIfDue() then runs: DEBUG('i', "Invoking interrupt handler for the %s at time %d\n", intTypeNames[toOccur->type], toOccur->when). 215 | 3. As CheckIfDue() runs, it will eventually call the TimerHandler() from timer.cc with: (*(toOccur->handler))(toOccur->arg). 216 | 4. TimerHandler() then calls TimerExpired() 217 | 5. TimerExpired() then schedules the next Timer interrupt at present_time+TimeOfNextInterrupt(), this is shown when the interrupt->Schedule(..) section runs: DEBUG('i', "Scheduling interrupt handler the %s at time = %d\n", intTypeNames[type], when); 218 | 6. TimerExpired() then also calls TimerInterruptHandler() from system.cc with: (*handler)(arg); 219 | 7. TimerInterruptHandler() then calls YieldOnReturn(), which sets yieldOnReturn to true. 220 | 8. Since nothing further is called, we jump all the way back to CheckIfDue(), which then returns true. 221 | 9. As CheckIfDue() returns true, the while loop in part 1. will loop once, then in the next loop CheckIfDue() returns false since the pending interrupt has already been delivered. This breaks the while loop. 222 | 10. Since yieldOnReturn has been set to true, we enter the if else section in OneTick(), which then runs yield(). 223 | 11. When yield() is run, this sends the main thread to the ready queue and fetches the first thread in the ready Q to be run. 224 | 12. This allows for the forked threads to be run essentially. 225 | 13. When each forked thread ends, it runs finish(), which resets the next timer interrupt, and then calls sleep() 226 | 14. sleep() then calls for the next thread in the queue to be run. 227 | (I have no idea why, but only the last thread to be called to sleep before main is specifically deleted by a destructor. probably due to how scheduler.cc->Run()->SWITCH(oldThread, nextThread); snippet works.) 228 | (It is a mystery) 229 | 15. This process is repeated untill the main thread is run again. 230 | 16. Once at the main thread, the next bunch of threads will be forked. (Since we are now back at the main section of the code, see bottom for better explaination) 231 | 17. At some point the next timer interrupt will be due to occur, then we go back to step 1. repeat. 232 | 233 | 234 | PART A): 235 | 236 | // THEORY 237 | 238 | // Because part A wants us to trigger an interrupt every 40 seconds, this means at every 40 second interval: 239 | // The interrupt will be triggered 240 | // when first interrupt is triggered, all queued thread's functions (Simplethread) will be run one by one. 241 | // The second interrupt will be scheduled 40 seconds later. 242 | // each run thanks to for loop 'for (num = 0; num < 1000000; num++) {}' will take 10 ticks. 243 | // thus for each 10 tick one thread will be completed 244 | // At the second interrupt, we will jump back into the main code, where will fork the next 3 threads to Q (add thread's functions to Q). 245 | // The third interrupt will be scheduled 40 seconds later. 246 | // Each addition to the Q takes 10 ticks. 247 | // At the third interrupt, same thing happens as the first. 248 | // At the fourth interrupt, same thing happens as the second. 249 | // Repeat untill we reach SimpleThread(0). 250 | /* 251 | Tick 10 - 30: Fork (queue) threads 1-3 and their functions (SimpleThread) 252 | Tick 40: invokve interrupt, schedule next interrupt, switch to running queued threads (1-3) by calling yield(). 253 | Tick 50-70: run queued threads (1-3), at last thread, finish() calls sleep(), which switches us back to the main program for forking threads 4-6. 254 | Tick 80: invokve interrupt, schedule next interrupt, call yield() <- waste 255 | Tick 90-110: fork threads 4-6 256 | Tick 120: invokve interrupt, schedule next interrupt, switch to running queued threads (4-6) by calling yield(). 257 | Tick 130-150: run queued threads (4-6), at last thread, finish() calls sleep(), which switches us back to the main program for forking threads 7-9. 258 | Tick 160: invokve interrupt, schedule next interrupt, call yield() <- waste 259 | Tick 170-190: fork threads 7-9 260 | Tick 200: invokve interrupt, schedule next interrupt, switch to running queued threads(7-9) by calling yield(). 261 | Tick 210-230: run queued threads(7-9), at last thread, finish() calls sleep(), which switches us back to the main program. 262 | Tick 240: invokve interrupt, schedule next interrupt, call yield() <- waste 263 | Tick 250: run main thread with function SimpleThread ( SimpleThread(0);). Since nothing left, program completed 264 | */ 265 | 266 | // CODE TO BE EDITED 267 | 268 | //Under system.cc, set randomYield to TRUE: 269 | bool randomYield = TRUE; // timer is activated by this? i changed this. 270 | // This will permanently activate the function we need in Timer.cc 271 | 272 | // Next, in Timer.cc copy this section in Timer() into TimerExpired(). 273 | interrupt->Schedule(TimerHandler, (_int) this, TimeOfNextInterrupt(), 274 | TimerInt); 275 | // This will ensure TimerExpired will always schedule the next interrupt after a current interrupt is invoked.. 276 | 277 | // FInally, under TimeOfNextInterrupt(), set return to permanently 40, or comment out the section under if(randomize) and set it to permanently return 40, like so: 278 | if (randomize) 279 | return 40; // 1 + (Random() % (TimerTicks * 2)); <- original, changed to return 40 280 | // since randomize = true for timer to be activated 281 | else 282 | return TimerTicks; 283 | // This ensures, since random is set to True pemanently, that 40 ticks will always be returned when timer expires in an interrupt invocation. 284 | 285 | 286 | PART B): 287 | 288 | // THEORY 289 | 290 | // In part b), whenever a thread is finished running (ie: printf("Thread %d Completed.\n",(int)which);), they want the scheduled timer interrupt to be reset to the current tick + 40 ticks, ie: 291 | // if in tick 50, thread 1 finishes. the original scheduled timer interrupt at 80 seconds will now be at 90 seconds.. 292 | // This saves on ticks, as when the last thread that is not 'main thread' in Q runs, it also resets the scheduled next interrupt time, that means in the next 10 Ticks: 293 | // it can go straight to forking, without the need to spend a whole 10 Ticks invoke interrupt, schedule the next interrupt and running yield() . like in part a) 294 | // thus overall shorter time needed 295 | /* 296 | Tick 10-30: Fork 1-3 297 | Tick 40: Invoke Interrupt, next at 80, switch to running queued threads by calling yield(). 298 | Tick 50: Finish thread1, reset interrupt to 40 ticks later (Interrupt at 90) 299 | Tick 60: Finish thread2, Interrupt reset to 100 300 | Tick 70: Finish thread3, Interrupt reset to 110, at last thread, finish() calls sleep(), which switches us back to the main program for forking threads 301 | Tick 80 - 100: Fork 4-6 <-- Tick of 10 saved here 302 | Tick 110: Invoke Interrupt, next at 150, switch to running queued threads by calling yield(). 303 | Tick 120: Finish thread4, Interrupt reset to 160 304 | Tick 130: Finish thread5, Interrupt reset to 170 305 | Tick 140: Finish thread6, Interrupt reset to 180, at last thread, finish() calls sleep(), which switches us back to the main program for forking threads 306 | Tick 150-170: Fork 7-9 307 | Tick 180: Invoke Interrupt, next at 220, switch to running queued threads by calling yield(). 308 | Tick 190: Finish a thread7, Interrupt reset to 230 309 | Tick 200: Finish a thread8, Interrupt reset to 240 310 | Tick 210: Finish a thread9, Interrupt reset to 250, at last thread, finish() calls sleep(), which switches us back to the main program. 311 | Tick 220: Run main thread with function SimpleThread ( SimpleThread(0);). Since nothing left, program completed 312 | <30 ticks saved!> 313 | 314 | */ 315 | 316 | // CODE TO BE EDITED 317 | 318 | // Under threads.cc, find the finish() function portion, then: 319 | // Step 1. add in this code chunk after #ifdef CHANGED: 320 | #ifdef CHANGED 321 | static void TimerHandler(_int arg) 322 | { Timer *p = (Timer *)arg; p->TimerExpired(); } 323 | // This is a copy of a function that we will need to use. 324 | 325 | // Step 2. Under the finish() function, under both if else results, insert this one line above sleep(): 326 | (void) interrupt->Schedule(TimerHandler, (_int)timer ,400,TimerInt); 327 | // This snippet will tell the universal interrupt pointer (interrupt->) to : 328 | // 1. Feed the universal timer pointer (timer) to TimerHandler(). 329 | // 2. TimerHandler() (using universal timer pointer) signals that the current interrupt is expired and due to be replaced. 330 | // 3. That the next interrupt is to be 400 seconds from now, and that it is Type TimerInt (you may simply set this to 40 seconds instead) 331 | 332 | 333 | // Under interrupt.cc, check the schedule() function: 334 | // Add this snippet if you used 400 seconds from now: 335 | if(fromNow == 400){ 336 | delete pending->Remove(); 337 | fromNow = fromNow / 10;} // '400' signifies that this schedule call was from a thread finishing, and thus must be corrected. 338 | 339 | // Add this if you used 40 seconds: 340 | delete pending->Remove(); // This deletes the present scheduled interrupt. 341 | 342 | // The rest of the schedule() function will schedule a new interrupt at present time + 40, thus fulfilling our requirements. 343 | 344 | 345 | // HOW CODE RUNS: 346 | Part A) 347 | 348 | void 349 | SimpleThread(_int which) 350 | { 351 | int num; 352 | 353 | for (num = 0; num < 1000000; num++) {} 354 | printf("Thread %d Completed.\n",(int)which); 355 | } 356 | 357 | void 358 | ThreadTest() 359 | { 360 | DEBUG('t', "Entering SimpleTest"); // Interrupt Scheduled at 40 361 | 362 | Thread *t1 = new Thread("child1"); 363 | t1->Fork(SimpleThread, 1, 0); // Tick 10 Q T1 364 | Thread *t2 = new Thread("child2"); 365 | t2->Fork(SimpleThread, 2, 0); // Tick 20 Q T2 366 | Thread *t3 = new Thread("child3"); 367 | t3->Fork(SimpleThread, 3, 0); // Tick 30 Q T3 368 | 369 | // TICK 40 - INTERRUPT - NEXT SCHEDULED AT 80 370 | // YIELD() RUNS: T1 <- FIRST ITEM IN Q RETRIEVED, MAIN_THREAD ADDED TO BACK OF Q, RUNS T1 371 | // T1 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T2 372 | // T2 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T3 373 | // T3 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), T3 DELETED, PASS ON TO MAIN_THREAD 374 | 375 | // PROGRAM RETURNS BACK TO HERE 376 | // TICK 80 - INTERRUPT - NEXT SCHEDULED AT 120 377 | 378 | Thread *t4 = new Thread("child4"); 379 | t4->Fork(SimpleThread, 4, 0); // Tick 90 Q T4 380 | Thread *t5 = new Thread("child5"); 381 | t5->Fork(SimpleThread, 5, 0); // Tick 100 Q T5 382 | Thread *t6 = new Thread("child6"); 383 | t6->Fork(SimpleThread, 6, 0); // Tick 110 Q T6 384 | 385 | // TICK 120 - INTERRUPT - NEXT SCHEDULED AT 160 386 | // YIELD() RUNS: T4 <- FIRST ITEM IN Q RETRIEVED, MAIN_THREAD ADDED TO BACK OF Q, RUNS T4 387 | // T4 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T5 388 | // T5 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T6 389 | // T6 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), T6 DELETED, PASS ON TO MAIN_THREAD 390 | 391 | // PROGRAM RETURNS BACK TO HERE 392 | // TICK 160 - INTERRUPT - NEXT SCHEDULED AT 200 393 | 394 | Thread *t7 = new Thread("child7"); 395 | t7->Fork(SimpleThread, 7, 0); // Tick 170 Q T7 396 | Thread *t8 = new Thread("child8"); 397 | t8->Fork(SimpleThread, 8, 0); // Tick 180 Q T8 398 | Thread *t9 = new Thread("child9"); 399 | t9->Fork(SimpleThread, 9, 0); // Tick 190 Q T9 400 | 401 | 402 | // TICK 200 - INTERRUPT - NEXT SCHEDULED AT 240 403 | // YIELD() RUNS: T7 <- FIRST ITEM IN Q RETRIEVED, MAIN_THREAD ADDED TO BACK OF Q, RUNS T7 404 | // T7 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T8 405 | // T8 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T9 406 | // T9 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), T9 DELETED, PASS ON TO MAIN_THREAD 407 | 408 | // PROGRAM RETURNS BACK TO HERE 409 | // TICK 240 - INTERRUPT - NEXT SCHEDULED AT 280 410 | 411 | 412 | SimpleThread(0); // Tick 250: run this, program ends. as idle determines no threads, interrupts or anything in Q left. 413 | } 414 | 415 | Part B) 416 | 417 | void 418 | SimpleThread(_int which) 419 | { 420 | int num; 421 | 422 | for (num = 0; num < 1000000; num++) {} 423 | printf("Thread %d Completed.\n",(int)which); 424 | } 425 | 426 | void 427 | ThreadTest() 428 | { 429 | DEBUG('t', "Entering SimpleTest"); // Interrupt Scheduled at 40 430 | 431 | Thread *t1 = new Thread("child1"); 432 | t1->Fork(SimpleThread, 1, 0); // Tick 10 Q T1 433 | Thread *t2 = new Thread("child2"); 434 | t2->Fork(SimpleThread, 2, 0); // Tick 20 Q T2 435 | Thread *t3 = new Thread("child3"); 436 | t3->Fork(SimpleThread, 3, 0); // Tick 30 Q T3 437 | 438 | // TICK 40 - INTERRUPT - NEXT SCHEDULED AT 80 439 | // YIELD() RUNS: T1 <- FIRST ITEM IN Q RETRIEVED, MAIN_THREAD ADDED TO BACK OF Q, RUNS T1 440 | // TICK 50 - T1 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T2, NEXT INTERRUPT SCHEDULED AT 90 441 | // TICK 60 - T2 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T3, NEXT INTERRUPT SCHEDULED AT 100 442 | // TICK 70 - T3 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), T3 DELETED, PASS ON TO MAIN_THREAD, NEXT INTERRUPT SCHEDULED AT 110 443 | 444 | // PROGRAM RETURNS BACK TO HERE 445 | // nothing runs here: ticks saved 446 | 447 | Thread *t4 = new Thread("child4"); 448 | t4->Fork(SimpleThread, 4, 0); // Tick 80 Q T4 449 | Thread *t5 = new Thread("child5"); 450 | t5->Fork(SimpleThread, 5, 0); // Tick 90 Q T5 451 | Thread *t6 = new Thread("child6"); 452 | t6->Fork(SimpleThread, 6, 0); // Tick 100 Q T6 453 | 454 | // TICK 110 - INTERRUPT - NEXT SCHEDULED AT 150 455 | // YIELD() RUNS: T4 <- FIRST ITEM IN Q RETRIEVED, MAIN_THREAD ADDED TO BACK OF Q, RUNS T4 456 | // TICK 120 - T4 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T5, NEXT INTERRUPT SCHEDULED AT 160 457 | // TICK 130 - T5 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T6, NEXT INTERRUPT SCHEDULED AT 170 458 | // TICK 140 - T6 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), T6 DELETED, PASS ON TO MAIN_THREAD, NEXT INTERRUPT SCHEDULED AT 180 459 | 460 | // PROGRAM RETURNS BACK TO HERE 461 | // nothing runs here: ticks saved 462 | 463 | Thread *t7 = new Thread("child7"); 464 | t7->Fork(SimpleThread, 7, 0); // Tick 150 Q T7 465 | Thread *t8 = new Thread("child8"); 466 | t8->Fork(SimpleThread, 8, 0); // Tick 160 Q T8 467 | Thread *t9 = new Thread("child9"); 468 | t9->Fork(SimpleThread, 9, 0); // Tick 170 Q T9 469 | 470 | 471 | // TICK 180 - INTERRUPT - NEXT SCHEDULED AT 220 472 | // YIELD() RUNS: T7 <- FIRST ITEM IN Q RETRIEVED, MAIN_THREAD ADDED TO BACK OF Q, RUNS T7 473 | // TICK 190 - T7 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T8, NEXT INTERRUPT SCHEDULED AT 230 474 | // TICK 200 - T8 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T9, NEXT INTERRUPT SCHEDULED AT 240 475 | // TICK 210 - T9 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), T9 DELETED, PASS ON TO MAIN_THREAD, NEXT INTERRUPT SCHEDULED AT 250 476 | 477 | // PROGRAM RETURNS BACK TO HERE 478 | // nothing runs here: ticks saved 479 | 480 | 481 | SimpleThread(0); // Tick 220: run this, program ends. as idle determines no threads, interrupts or anything in Q left. 482 | } 483 | 484 | : // EXPERIMENT 3 485 |        _,,..-=ニ二7 486 |      r'ア´ 487 |     ノノ   r'ア'"`ヽ.,_ _,,..-=-、    _,. -rァ 488 |    r'ァ⌒ヽ、i7::::::::;>''"´:: ̄ ̄`"''<´:::::::::!( 489 |    .||    r!:::/::::::::::::::::::::::::::::::::::::::::ヽ:::::/i' 490 |    ||      ^Y:::::;:::::i:::::::::/i::::::i:::::::;:::::::;::::Y (_ 491 |    ||    ./i::::::i:::/!--/ |:::;ハ_!_::i::::::i:::::::i r'ヽ. 492 |    !!    く:::L__ハ/-‐‐'  レ' _!__;ハ::::ハ:::::|_,ゝ:::', 493 |    ',',ヽ.   ヽヘ「7""         `レ7´)/:::iヽ;:::i 494 |    i´`とン' ´`ヽ!,人  「' ̄ `i ""7_/'´':::::::! i:::!    "What the fuck is going on" 495 |    ヽ.,_//」 、_,ノ:::::ノ>.、、,___,ノ_,,.イ:::!、__!7ノ__. レ' 496 |      i   ゝ-ァ'/ /)_iヽ/ /(/ゝ、.,_ノ   ̄「iー-、 497 |     ノ〈)    `  /::::ソ^ヽ、/」::_r' _/ /」      |つ-' 498 |    <.,  _____,,,... イ::::くr-、_」:::::::::Y^ヽ、    [] ', 499 |       ̄  レ'   l>-、::;;_______;;::」〉'ノヽ.   __ 〉 500 |    ((    r'ア'"/:::/ i ヽ;::::ヽ`''::ーァ、`''ー-┴'" 501 |         r'i:::/::::::/ .l   ';:::::::::::::::::!,」 502 |         `ゝ、:::::/  l   ヽ;::::::::::/」 503 |           └へ>、,_!_______,,..>ァニン! 504 |             'r-- 'i    ̄ヽ,_ノ 505 |             ヽ二ノ 506 | 507 | 508 | PART 1) 509 | TASK: After running 2 INC and 2DEC, the final result should be 1 due to race conditions.. 510 | 511 | //CODE 512 | void Inc_v1(_int which) 513 | { 514 | //fill your code 515 | int a=value; 516 | a++; 517 | currentThread->Yield(); // context switches here to next thread in Q (CS) 518 | value=a; 519 | printf("**** Inc thread %d new value %d\n", (int) which, value); 520 | } 521 | 522 | void Dec_v1(_int which) 523 | { 524 | //fill your code 525 | int a=value; 526 | a--; 527 | currentThread->Yield(); // context switches here to next thread in Q (CS) 528 | value=a; 529 | printf("**** Dec thread %d new value %d\n", (int) which, value); 530 | } 531 | 532 | void TestValueOne() 533 | { 534 | value=0; 535 | printf("enter TestValueOne, value=%d...\n", value); 536 | //1. fill your code here. 537 | Thread *t1 = new Thread("child1"); // thread is created 538 | t1->Fork(Inc_v1, 1, 0); // fork(function to be run, thread id given, not to be joined) 539 | Thread *t2 = new Thread("child2");// thread is created 540 | t2->Fork(Dec_v1, 2, 0);// fork(function to be run, thread id given, not to be joined) 541 | Thread *t3 = new Thread("child3");// thread is created 542 | t3->Fork(Dec_v1, 3, 0);//fork(function to be run, thread id given, not to be joined) 543 | Thread *t4 = new Thread("child4");// thread is created 544 | t4->Fork(Inc_v1, 4, 1); // fork(function to be run, thread id given, to be joined) 545 | currentThread->Join(t4); // join current thread (CT) to t4 546 | // this means CT progress will be paused untill t4 is finished 547 | 548 | //2. checking the value. you should not modify the code or add any code lines behind 549 | //this section. 550 | if(value==1) 551 | printf("congratulations! passed.\n"); 552 | else 553 | printf("value=%d, failed.\n", value); 554 | }//CODE 555 | 556 | // THEORY: 557 | Based on the pattern, the Q looks like this: 558 | t1,t2,t3.t4, 559 | 560 | since CT is joined to t4:, this means untill t4 is finished (where it runs printf), the rest of the code will not be run, ie: if(value==1) will not be checked. 561 | 562 | finish() moves to next thread in Q and deletes current thread. 563 | 564 | runs like so: (CS: context switch) 565 | 1. t1: a=1, CS->t2, queue t1, queue now looks like: t2,t3,t4,t1 566 | 2. t2: a=-1, CS->t3 queue t2, queue now looks like: t3,t4,t1,t2 567 | 3. t3: a=-1, CS->t4, queue t3, queue now looks like: t4,t1,t2,t3 568 | 4. t4: a=1, CS->t1, queue t4, queue now looks like: t1,t2,t3,t4 569 | 5. t1: value=1, run printf(), finish(t1)->CS->t2, queue now looks like: t2,t3,t4 570 | 6. t2: value=-1, run printf(), finish(t2)->CS->t3, queue now looks like: t3,t4 571 | 7. t3: value=-1, run printf(), finish(t3)->CS->t4, queue now looks like: t4 572 | 8. t4: value=1, run printf(), finish(t4)->CS->CT, queue now looks like: 573 | 9. since t4 is finished, were now back in CT, now the rest of code will be run, ie: if(value==1). 574 | 575 | for TestValueMinusOne, swap the inc & dec running function patterns: 576 | 577 | //CODE 578 | Thread *t1 = new Thread("child1"); 579 | t1->Fork(Dec_v2, 1, 0); 580 | Thread *t2 = new Thread("child2"); 581 | t2->Fork(Inc_v2, 2, 0); 582 | Thread *t3 = new Thread("child3"); 583 | t3->Fork(Inc_v2, 3, 0); 584 | Thread *t4 = new Thread("child4"); 585 | t4->Fork(Dec_v2, 4, 1); // indicate item is to be joined changed 586 | currentThread->Join(t4); 587 | //CODE 588 | 589 | or: 590 | -1 591 | 1 592 | 1 593 | -1 594 | 595 | thus it runs like so: 596 | 1. t1: a=-1, CS->t2, queue t1, queue now looks like: t2,t3,t4,t1 597 | 2. t2: a=1, CS->t3 queue t2, queue now looks like: t3,t4,t1,t2 598 | 3. t3: a=1, CS->t4, queue t3, queue now looks like: t4,t1,t2,t3 599 | 4. t4: a=-1, CS->t1, queue t4, queue now looks like: t1,t2,t3,t4 600 | 5. t1: value=-1, run printf(), finish(t1)->CS->t2, queue now looks like: t2,t3,t4 601 | 6. t2: value=1, run printf(), finish(t2)->CS->t3, queue now looks like: t3,t4 602 | 7. t3: value=1, run printf(), finish(t3)->CS->t4, queue now looks like: t4 603 | 8. t4: value=-1, run printf(), finish(t4)->CS->CT, queue now looks like: 604 | 9. since t4 is finished, were now back in CT, now the rest of code will be run, ie: if(value==1). 605 | 606 | 607 | PART 2): 608 | To Ensure Consistency in increment & decrement, semaphores are used. 609 | 610 | //CODE 611 | Semaphore *S = new Semaphore("S1",1); // Create semaphore S with name "S1" and init val '1' 612 | //2. implement the new version of Inc: Inc_Consistent 613 | void Inc_Consistent(_int which) 614 | { 615 | //fill your code 616 | S->P(); // Wait() equivalent 617 | int a=value; 618 | a++; 619 | currentThread->Yield(); // changed 620 | value=a; 621 | printf("**** Inc thread %d new value %d\n", (int) which, value); 622 | S->V(); // Signal() Equivalent 623 | } 624 | 625 | //3. implement the new version of Dec: Dec_Consistent 626 | void Dec_Consistent(_int which) 627 | { 628 | //fill your code 629 | 630 | S->P(); // Wait() equivalent 631 | int a=value; 632 | a--; 633 | currentThread->Yield(); // changed 634 | value=a; 635 | printf("**** Dec thread %d new value %d\n", (int) which, value); 636 | S->V(); // Signal() Equivalent 637 | } 638 | 639 | //4. implement TestValueMinusOne by create two threads with Inc_Consistent and two threads with Dec_Consistent 640 | // you should pass the checking at the end, printing "congratulations! passed." 641 | void TestConsistency() 642 | { 643 | value=0; 644 | printf("enter TestConsistency, value=%d...\n", value); 645 | 646 | //fill your code 647 | Thread *t1 = new Thread("child1"); 648 | t1->Fork(Inc_Consistent, 1, 0); 649 | Thread *t2 = new Thread("child2"); 650 | t2->Fork(Dec_Consistent, 2, 0); 651 | Thread *t3 = new Thread("child3"); 652 | t3->Fork(Dec_Consistent, 3, 0); 653 | Thread *t4 = new Thread("child4"); 654 | t4->Fork(Inc_Consistent, 4, 1); // indicate item is to be joined changed 655 | currentThread->Join(t4); 656 | 657 | //2. checking the value. you should not modify the code or add any code lines behind 658 | //this section. 659 | if(value==0) 660 | printf("congratulations! passed.\n"); 661 | else 662 | printf("value=%d, failed.\n", value); 663 | }//CODE 664 | 665 | //THEORY 666 | Semaphore's ->P() is equvalent to wait(). 667 | 668 | Semaphore's ->V() is equvalent to signal(). 669 | 670 | Due to semaphore inclusion, it runs like so: 671 | 672 | 1. t1: passes S->P() and enters critical , a=1, CS->t2, queue t1, queue is now: t2,t3,t4,t1 673 | 2. t2: fails S->P() as t1 - critical , sleep t2 thus CS->t3, queue t2, queue is now: t3,t4,t1,t2 674 | 3. t3: fails S->P() as t1 - critical , sleep t3 thus CS->t4, queue t3, queue is now: t4,t1,t2,t3 675 | 4. t4: fails S->P() as t1 - critical , sleep t4 thus CS->t1, queue t4, queue is now: t1,t2,t3,t4 676 | 5. t1: value=1, printf, passes S->V() and exits critical, finish()->CS->t2, queue is now: t2,t3,t4 677 | 678 | 6. t2: passes S->P() and enters critical , a=0, CS->t3, queue t2, queue is now: t3,t4,t2 679 | 7. t3: fails S->P() as t2 - critical , sleep t3 thus CS->t4, queue t3, queue is now: t4,t2,t3 680 | 8. t4: fails S->P() as t2 - critical , sleep t4 thus CS->t2, queue t4, queue is now: t2,t3,t4 681 | 9. t2: value = 0, printf, passes S->V() and exits critical, finish()->CS->t3, queue is now: t3,t4 682 | 683 | 10. t3: passes S->P() and enters critical , a=-1, CS->t4, queue t3, queue is now: t4,t3 684 | 11. t4: fails S->P() as t3 - critical , sleep t4 thus CS->t3 queue t4, queue is now: t3,t4 685 | 12. t3: value=-1, printf, passes S->V() and exits critical, finish()->CS->t4, queue is now: t4 686 | 687 | 688 | 13. t4: passes S->P() and enters critical , a=0, no usual CS + Queue from -> yield as no other threads in Q left. 689 | 14. t4: value=0, printf, passes S->V() and exits critical, finish()->CT 690 | 15. since t4 is finished, were now back in CT, now the rest of code will be run, ie: if(value==1). 691 | 692 | // FUNCTIONS TO REVISE: 693 | Thread() 694 | Fork() 695 | Yield() 696 | Sleep() 697 | Finish() 698 | Join() 699 | 700 | Nachos Semaphores Functions 701 | Nachos Lock Functions 702 | ^ Both are in synch.cc 703 | 704 | : // EXPERIMENT 4 705 |            ,. -ー- 、                 ,. -ー- 、 706 |    '         /.,riljljljh, ヽ _ ,,, .. --ー.ー-- .. ,,, _ /.,riljljljh, ヽ      / 707 |     '        ! il|l|l|l|l|l|l! i . . . . . . . . . . . . . . . . . . ! il|l|l|l|l|l|l! i       / 708 |     '.      ヽヾ!ilililiツ ,'. . . . . . . . . . . . . . . . . . .ヽヾ!ilililiツ ,'    /      / 709 |     ',        `'‐-,-‐": : : . . . . . . . . . . . . . . . : : :`'‐-,-‐"    /     / 710 |      ',         ./ : : : : : : . . . . . . . . . . . . . . : : : : : : ヘ.           / 711 |      ',        / : : : : : : : : : : : : : : : : : : : : : : : : : : : : ヽ      / 712 |       ',     ./ __,,..  ..-..--..─―..―..ー..-..  、、..__: :ヽ      _,、∧/ 713 | \      _,,. r;:'' "; ;:;:- '"´ ̄`ヽ、::::-─- '"´ ̄ `ヽ、; ; ``;ヽ.、  「 714 |       ,.r'" ; ; ; _:;ア´                     ヽ、 ; ; ; ; <   I HATE NACHOS 715 |     , ' ; ; ; ; ; ア´/  , '´   /     i    ',    i   Y; ; ; ; ; ; >. I HATE NACHOS 716 | ..,,_  i ; ; ; ; ; ;:/ /  /   i. 、,'  ハ  ,ハ   ,i   ハ_    iヽ;: ; ; ; ;>  I HATE NACHOS 717 |     ヽ、; ; ; ノ ,'  .i   ハ  i\/ ', / i  / i ,.イ´./i   !  i; ;/   I HATE NACHOS 718 | _____    `イ   i   i  ./ ァ'" ̄`ヽー/  | /,ァ''" ̄`ヽハ  ハ ∠_.    I HATE NACHOS 719 |     ∧ '" /| ノ ,ハイ   i'´'`i     レ'  i'´'`i.   ト| /ニi,_  ヽ7  I HATE NACHOS 720 | \∧/  Vi/  |_,. -‐ァi/    !__,リ          !__,リ   ' レ'  ̄!ヾ'、 ./へ 721 |  NO  MORE   ./=i.  `'' ー-         -‐ ''´  i==!i !i  i  \/V\/ 722 | NO  MORE   .∠, i. ""       `       ""!  .i!l !i. ',      ─--- 723 | NO  MORE    ./  i.       ,.'´ ̄ ̄`ヽ       .ィ  .!!l  !i. ヽ. 724 | NO  MORE   ./_ , ト.     i.,.-ー─-、.i    ,.イ!   .!ヾ='<   ',    - ..,,__ 725 | NO  MORE    ./  !::::ヽ   ヽ,    ノ   ノ'':::::i.   ', ‐ 、 ヽ  i       726 |  MORE      く   i::::::::`i丶.、.,, ̄ ̄ ,.. イ::::::::::::i    ,j   ヽ、ヘ/     \ 727 | NMORE       .>  .i:::::::::::ヽ、.,__ ̄ ̄__,.ノ:::::::::::::i.   ,ィ'.     ヽ.  ',   \ 728 |     !!!      <.ヽ   !::::::::::::::::::::: ̄ ̄::::::::::::::::::::::!  /       ヽ,  ', 729 | 730 | 731 |    \       ',     |       /        /      , ' 732 |     \      ',     |     /        /     , ' 733 |       \                      /     , ' 734 |          ァ' ⌒ヽ._,. --──-- ァ' ⌒ヽ.       , ' 735 | ` 、       ! (:::) i        .! (:::) i 736 |          ':、.,__,.ノ         ':、.,__,.ノ 737 |          /               ',        --  ── 738 |         _/-‐ニ=--────-=ニ、..,,_';、.,_ 739 |      ,..-'´ァ''"´               `"ヽ`ヽ、 740 | ─--r'"´:::::::/ .  | 、  !∧  ./!__」イ´ .   |::::::::::::::`ヽ. 741 |    ヽ、::::::/ '、  ! ,>t、 \/'´l'ハ`ヽ./ ∧::::::::::::::::::ノ 742 |       `ヽ   \| 7  l,ハ     !_ソ ノレ'  \::::- ''´ 743 |        ヽ  /レヘ、 ゝ'  '     ⊂⊃  !, '⌒ヽ. 744 |        レイ ⊂⊃  rァ'´ ̄`ヽ. u / ! 7    | 745 |         /  ,ハ、   !      ノ / / ,'    / 746 |         |    > 、.,_   _,. イ'|/ ./   / 747 |         |  /ヽ_,.. イ「こ__/ /____/    ,'-、_ 748 |         レ´/\::::::::::::::::::::::::::::::::_/    /`ヽ._ \ 749 |         /´`'ー--|:::ト∧ノ::i´ ̄ ̄     ,'   / `ヽヽ、 750 |        ,r/     r)/o-o、'、__,.、     ー!   /  / )Y 751 |       /、____,.イ7:::/ ̄i´:::::::| /ヽ.,_ `ーヽ._/ /| .| 752 |      /      |(`'っ .!ソ:::::::::', '、:::::`ー`ヽ、_  `ン::/ / 753 |     /       /:ゝ、.つと):::::::::\\::::::::::::::::::: ̄:::// 754 | 755 | 756 | //THEORY: 757 | Key Functions: 758 | VPNToPhyPage() 759 | InsertToTLB() 760 | lrualgorithm() 761 | 762 | Slide 14: Diagram for how functions work together with thought process. 763 | 764 | In general: 765 | 1. check tlb for translation. 766 | 2. not in tlb? check iverse page table 767 | 3. in ipt! update tlb. not in ipt! call backing store. 768 | 4. tell backing store to update w direct insert if there are empty frames or LRU otherwise, ipt then updates tlb 769 | 770 | Process: 771 | 772 | 773 | 774 | 1. No valid page found in TLB. 775 | 776 | Translate 0x0, read: *** no valid TLB entry found for this virtual page 0! 777 | 778 | 2. Runs VpnToPhyPage() to list Inverse page table entries and determine if page can be found here: (here 'frame '/'IPT: ' is interchangeable w phyPage) 779 | 780 | ...IPT: 0 , pid: 0 vpn: 0 last_used: 0 validtity: 0 781 | ...IPT: 1 , pid: 0 vpn: 0 last_used: 0 validtity: 0 782 | ...IPT: 2 , pid: 0 vpn: 0 last_used: 0 validtity: 0 783 | ...IPT: 3, pid: 0 vpn: 0 last_used: 0 validtity: 0 784 | .....IPT w matching pid and vpn missing! <- not found. 785 | 786 | 3. Run DoPageIn() to move page from disk store to TLB n IPT 787 | 788 | paging in: pid 0, phyPage 0, vpn 0 789 | 790 | 4. Run InsertToTlb() , lists tlb pre update: 791 | 792 | TLB pre-update: 0 <- which tlb entry it will update, here it indicates it will update 'TLB 0', chooses the oldest entry in tlb if none free. which is the one next to the newest entry, ie: if previous insert is tlb 0, next is tlb 1 793 | ...TLB 0, vpn, phy & valid: 0 0 0 794 | ...TLB 1, vpn, phy & valid: 0 0 0 795 | ...TLB 2, vpn, phy & valid: 0 0 0 796 | 797 | 5. and prints out post update that in the TLB: 798 | 799 | The corresponding TLBentry for Page 0 in TLB is 0 <-phy value 800 | 801 | 6. post page in will look like this: 802 | ...IPT: 0, pid: 0 vpn: 0 last_used: 12 validtity: 1 <- indicates IPT 0 was last used at tick 12 803 | ...TLB 0, vpn, phy & valid: 0 0 1 <- 'TLB 0' was updated 804 | 805 | 806 | 807 | 808 | Translate 0x30, read: *** no valid TLB entry found for this virtual page 0! 809 | Exception: page fault/no TLB entry 810 | ...IPT: 0, pid: 0 vpn: 0 last_used: 12 validtity: 1 811 | ...IPT: 1, pid: 0 vpn: 9 last_used: 25 validtity: 1 812 | ...IPT: 2, pid: 0 vpn: 26 last_used: 17 validtity: 1 813 | ...IPT: 3, pid: 0 vpn: 1 last_used: 22 validtity: 1 814 | .....Caught IPT 0, pid: 0 vpn: 0 last_used: 12 validtity: 1 <- virutal page 0 found in IPT 0 or phy frame 0 815 | 816 | // Continue as per normal but without any paging ins. 817 | 818 | // Per normal being that a TLB entry will be updated with the caught entry in IPT 819 | 820 | 821 | 822 | // NOTE: LruAlgorithm() will prioritize validity over oldest frame, see further down below: 823 | 824 | Translate 0x528, read: *** no valid TLB entry found for this virtual page 10! 825 | ...IPT: 0, pid: 0 vpn: 0 last_used: 28 validtity: 1 826 | ...IPT: 1, pid: 0 vpn: 9 last_used: 25 validtity: 1 827 | ...IPT: 2, pid: 0 vpn: 26 last_used: 17 validtity: 1 828 | ...IPT: 3, pid: 0 vpn: 1 last_used: 22 validtity: 1 <- as we can see here, IPT is full 829 | .....IPT w matching pid and vpn missing! 830 | paging out: pid 0, phyPage 2, vpn 26 <-- NOTE: paging out is not always done, pageout only occurs due to dirty bit being set, which means it has been modified and must be written back to disk 831 | paging in: pid 0, phyPage 2, vpn 10 <-- Using LruAlgorithm() and DoPageoutPagein(), 'IPT 2' / frame 2 / phyPage 2 with last_used:17, which indicates it was last used at tick 17, is the oldest frame. 832 | 833 | // This oldest frame due to it's dirty bit is thus selected for page out in order to take in 'VPN 10'. 834 | // The TLB entry for vpn 26 is also immediately made invalid before update shown here: 835 | 836 | TLB pre-update: 2 837 | ...TLB 0, vpn, phy & valid: 1 3 1 838 | ...TLB 1, vpn, phy & valid: 0 0 1 839 | ...TLB 2, vpn, phy & valid: 26 2 0 <-- immediately invalid 840 | 841 | // Thus after the new VPN is paged in and IPT n TLB are updated: 842 | ...IPT: 2, pid: 0 vpn: 10 last_used: 28 validtity: 1 843 | ...TLB 2, vpn, phy & valid: 10 2 1 844 | 845 | // Note, without dirty bit set: 846 | 847 | Translate 0xc0, read: *** no valid TLB entry found for this virtual page 1! 848 | Exception: page fault/no TLB entry 849 | ...IPT: 0, pid: 0 vpn: 0 last_used: 49 validtity: 1 850 | ...IPT: 1, pid: 1 vpn: 26 last_used: 66 validtity: 1 851 | ...IPT: 2, pid: 1 vpn: 0 last_used: 61 validtity: 1 852 | ...IPT: 3, pid: 1 vpn: 9 last_used: 68 validtity: 1 853 | .....IPT w matching pid and vpn missing! 854 | paging in: pid 1, phyPage 0, vpn 1 <-- only page in. 855 | TLB pre-update: 0 856 | ...TLB 0, vpn, phy & valid: 0 2 1 <-- thus no immediate change to invalid. 857 | ...TLB 1, vpn, phy & valid: 9 3 1 858 | ...TLB 2, vpn, phy & valid: 26 1 1 859 | 860 | // Thus after IPT n TLB are updated: 861 | ...IPT: 0, pid: 1 vpn: 1 last_used: 71 validtity: 1 862 | ...TLB 0, vpn, phy & valid: 1 0 1 863 | 864 | // In a scenario where there are invalid entries in IPT: 865 | 866 | ...IPT: 0, pid: 1 vpn: 1 last_used: 71 validtity: 0 867 | ...IPT: 1, pid: 1 vpn: 26 last_used: 66 validtity: 0 868 | ...IPT: 2, pid: 1 vpn: 0 last_used: 76 validtity: 0 869 | ...IPT: 3, pid: 1 vpn: 9 last_used: 73 validtity: 0 870 | 871 | // The first invalid entry is automatically chosen to be replaced by LRU, regardless of how old it is, ie: 872 | 873 | 1. 874 | ...IPT: 0, pid: 0 vpn: 0 last_used: 109 validtity: 1 875 | ...IPT: 1, pid: 1 vpn: 26 last_used: 66 validtity: 0 876 | ...IPT: 2, pid: 1 vpn: 0 last_used: 76 validtity: 0 877 | ...IPT: 3, pid: 1 vpn: 9 last_used: 73 validtity: 0 878 | 879 | 2. 880 | ...IPT: 0, pid: 0 vpn: 0 last_used: 109 validtity: 1 881 | ...IPT: 1, pid: 0 vpn: 9 last_used: 111 validtity: 1 882 | ...IPT: 2, pid: 1 vpn: 0 last_used: 76 validtity: 0 883 | ...IPT: 3, pid: 1 vpn: 9 last_used: 73 validtity: 0 884 | 885 | 3. 886 | ...IPT: 0, pid: 0 vpn: 0 last_used: 109 validtity: 1 887 | ...IPT: 1, pid: 0 vpn: 9 last_used: 111 validtity: 1 888 | ...IPT: 2, pid: 0 vpn: 10 last_used: 113 validtity: 1 889 | ...IPT: 3, pid: 1 vpn: 9 last_used: 73 validtity: 0 890 | 891 | 4. 892 | ...IPT: 0, pid: 0 vpn: 0 last_used: 109 validtity: 1 893 | ...IPT: 1, pid: 0 vpn: 9 last_used: 111 validtity: 1 894 | ...IPT: 2, pid: 0 vpn: 10 last_used: 114 validtity: 1 895 | ...IPT: 3, pid: 0 vpn: 26 last_used: 114 validtity: 1 896 | 897 | 898 | 899 | 900 | 901 | //CASE 1: If IPT's pids were 0 and the paging in PID suddenly changes to a new PID (ie 1), all TLB entries immediately are made invalid.: 902 | 903 | Translate 0x0, read: *** no valid TLB entry found for this virtual page 0! 904 | Exception: page fault/no TLB entry 905 | ...IPT: 0, pid: 0 vpn: 0 last_used: 49 validtity: 1 906 | ...IPT: 1, pid: 0 vpn: 9 last_used: 46 validtity: 1 907 | ...IPT: 2, pid: 0 vpn: 10 last_used: 28 validtity: 1 908 | ...IPT: 3, pid: 0 vpn: 26 last_used: 44 validtity: 1 909 | .....IPT w matching pid and vpn missing! 910 | paging in: pid 1, phyPage 2, vpn 0 <-- paging in PID now 1, a new value 911 | TLB pre-update: 0 912 | ...TLB 0, vpn, phy & valid: 9 1 0 <-- All TLB entries set to invalid 913 | ...TLB 1, vpn, phy & valid: 26 3 0 914 | ...TLB 2, vpn, phy & valid: 0 0 0 915 | The corresponding TLBentry for Page 0 in TLB is 0 916 | 917 | //CASE 2: If IPT's pids are now 1 and the paging in PID suddenly reverts back to the old PID (ie: 0), all TLB and IPT entries are immediately made invalid. 918 | 919 | Translate 0x48, read: *** no valid TLB entry found for this virtual page 0! 920 | Exception: page fault/no TLB entry 921 | ...IPT: 0, pid: 1 vpn: 1 last_used: 71 validtity: 0 <-- ALL IPT entries set to invalid 922 | ...IPT: 1, pid: 1 vpn: 26 last_used: 66 validtity: 0 923 | ...IPT: 2, pid: 1 vpn: 0 last_used: 76 validtity: 0 924 | ...IPT: 3, pid: 1 vpn: 9 last_used: 73 validtity: 0 925 | .....IPT w matching pid and vpn missing! 926 | paging in: pid 0, phyPage 0, vpn 0 <-- paging in PID now 0, the old value! 927 | TLB pre-update: 0 928 | ...TLB 0, vpn, phy & valid: 1 0 0 <-- ALL TLB entries set to invalid 929 | ...TLB 1, vpn, phy & valid: 0 2 0 930 | ...TLB 2, vpn, phy & valid: 26 1 0 931 | The corresponding TLBentry for Page 0 in TLB is 0 932 | 933 | 934 | 935 | 936 | 937 |          _____.,.へ. 938 |     _,.-‐''''"´ / @ \_. 939 |   ,.'"  r___,.rニ'ー'´ ̄`ヽ!ヽ、 940 |   !_,.rソ´ i ____イ   `i ,`'ート、_ 941 |  r' i,イ /  ハ__/_、ハ  /__ハ  ',7」 942 |   Y´ /!/レイ     レ'!,ィ、 !/|ハソ 943 |   ,'  ,'  | " ̄`    -、!ハ Y´  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA。 944 | . /   / i ハ   ____` "i ハ! 945 | く  イ ./ | |>、  ` ´  人 ハ 946 | ノ^ー'!/,イ-ハト、.!`=ーr<´!.ハヽ! 947 | ^Y ,イ^ヽ、ゝ  \「7/`ヽ!Vヽ! 948 |  レ'ソ    Lヽ、  〈ハ〉  〉、   /7 ̄ ̄ ̄ ̄ ̄ ̄ ̄7 949 |  「`ー'^ー'^7 `ヽ/§ヽ!/ヽ .// ____________________ ./ 950 | .イ`ー--‐イ、_,.-r!、.§ 〉 /`// /  PC.冥界   // 951 | 7ヽr、__r'"´ / // '"´`ヽ、  .//  ̄ ̄ ̄ ̄ ̄ ̄ / 952 | i   イ   |  | |、_ ,、 ! .ハ //          / 953 | ヽ、     i  | ト=='===='i二二二二二二二二i -------------------------------------------------------------------------------- /Lab Notes - SmallEditAt - 9112022.txt: -------------------------------------------------------------------------------- 1 | NACHOS NOTES : SC2005 : NANYANG TECHNOLOGICAL UNIVERSITY : IDEALLY VIEW WITH MS PGOTHIC FONT SIZE 12 UTF-8 : BEST VIEWED IN NOTEPAD++ 2 |                        ,.、 3 |            _,,... -─-- 、.,_   /ヽ,ヽ, 4 |       ,.. '"´         `ヽ/::::/ヽ、> 5 |      /                /:::::::::/ヽ. 6 |     ,.'   ,            ヽ!`ヾイ:::::::::::! 7 |     ,'   /  / __ ! ./ | /|__」_ ',___!/Y´i ̄ 8 |    i.  ,'   7´i /|/   !/ !  ノ` ! | :  ! 9 |    i   !  /ァ'´`ヽ    ァ'"´ `ヽ! |.   | 10 |    !   ! /i   ()     ()   レ' |    | 11 |   .レヘレ',! `           - '/ !   | 12 |      /⊂⊃   rァ─- 、   ⊂⊃.!  i  | 13 |     ,'  ト、,   !    )   /.  /   !  ! 14 |     i ノイ i> .、.,,______   イi  /  ハ. | 15 |     .レ'´ レ'ヽ.  .,.イ〈 i____,.ノ ,|/レヘ/  V 16 |          `7 i::ヽレム /:::::::/`ヽ. 17 |          /  !::::::くン-'::::::::/     ':, 18 |        /  ,'::::::::::::::::::::::::i      i 19 | 20 | "I paid afew grand for this?" 21 | 22 | 23 | 24 | GENERAL NOTES: 25 | /* 26 | - Universal pointers used by many .cc files: see in system.cc 27 | 28 | IE: 29 | Thread *currentThread; // the thread we are running now 30 | Thread *threadToBeDestroyed; // the thread that just finished 31 | Scheduler *scheduler; // the ready list 32 | Interrupt *interrupt; // interrupt status 33 | Statistics *stats; // performance metrics 34 | Timer *timer // the hardware timer device, 35 | // for invoking context switches 36 | ^ All in system.cc 37 | 38 | Use these pointers in place of 'this' when calling outside of function's usual place. 39 | 40 | IE: 41 | original in timer.cc: interrupt->Schedule(TimerHandler, (_int) this, TimeOfNextInterrupt(), TimerInt); 42 | New: (void) interrupt->Schedule(TimerHandler, (_int)timer <-- universal pointer ,400,TimerInt); 43 | 44 | 45 | - If need be, copy functions into other .cc file portions between '#ifdef CHANGED' and '#endif'. 46 | 47 | IE: 48 | #ifdef CHANGED 49 | static void TimerHandler(_int arg) 50 | { Timer *p = (Timer *)arg; p->TimerExpired(); } <-- copied function 51 | .... 52 | #endif 53 | 54 | - WIP 55 | 56 | */ 57 | 58 | 59 | : // EXPERIMENT 1 60 | FUNCTIONS TO BE STUDIED FOR EXPERIMENT 1: 61 | /* 62 | thread.cc: Fork(),Thread(),Yield() 63 | scheduler.cc: contains functions used by Fork() and Yield() 64 | threadtest.cc: simplethread() 65 | 66 | */ 67 | 68 | // THEORY 69 | void 70 | SimpleThread(_int which) 71 | { 72 | int num; 73 | 74 | for (num = 0; num < 3; num++) { 75 | printf("*** thread %d looped %d times\n", (int) which, num); // runs a printf saying which current thread has looped how many times 76 | currentThread->Yield(); // yields the current thread, this means: 77 | // 1. The first thread in the ready Q is fetched. (nextThread = scheduler->FindNextToRun();) 78 | // 2. current thread is put back to end of ready queue (scheduler->ReadyToRun(this); , see scheduler.cc) 79 | // 3. The fetched thread is run. (scheduler->Run(nextThread);) 80 | 81 | // repeat untill each thread's simplethread function has run 3 times. 82 | /* 83 | like so: (main thread = 0 , '->' = yield()) 84 | 85 | 0 -> 1 -> 2 -> 0 -> 1 -> 2 -> 0 -> 1 -> 2 86 | 87 | see table.csv and output.txt 88 | 89 | */ 90 | } 91 | } 92 | 93 | 94 | Thread *t1 = new Thread("child1"); // constructs a thread named 'child1' 95 | t1->Fork(SimpleThread, 1, 0); 96 | // child1 thread, calling fork function, has the function simplethread queued into ready queue to run with arguement of '1' passed into function simplethread. 97 | // the '0' ^ above in Fork() represents if a thread should be joined or not ie: wait untill t1 finishes executing, '0' == do not join, '1' join. 98 | // see: Thread.cc Fork() 99 | 100 | Thread *t2 = new Thread("child2"); // repeat with child2 101 | t2->Fork(SimpleThread, 2, 0); 102 | 103 | SimpleThread(0); // main thread is now to run SimpleThread function with arguement 0 (ie: main thread is called thread 0) 104 | 105 | 106 | // NOTES ON HOW EXPERIMENT 1 ENDS: 107 | 1. With the last forked procedure yielding to the main thread 0 in the third loop, finish() is called. This runs: DEBUG('t', "Finishing thread %s #%i\n", getName(), pid); 108 | 2. It then sets threadToBeDestroyed = currentThread; which is itself. 109 | 3. It then runs sleep(). 110 | 4. In sleep(): DEBUG('t', "Sleeping thread %s #%i\n", getName(), pid); is run. 111 | 5. Since thread 1 n 2 are still in the Q, the while loop will not be triggered and it skips to: scheduler->Run(nextThread). 112 | 6. Under scheduler->Run(nextThread), DEBUG('t', "Switching from thread %s #%i to thread %s #%i\n",oldThread->getName(), oldThread->pid, nextThread->getName(),nextThread->pid); and 113 | DEBUG('t', "Now in thread %s #%i\n", currentThread->getName(), currentThread->pid); are run, this means the current thread is now 1. 114 | 7. Then: delete threadToBeDestroyed; is run, which calls the thread destructor on thread 0. 115 | 8. This deletes thread 0, as shown with: DEBUG('t', "Deleting thread %s #%i\n", name, pid); running. 116 | 9. repeat steps 1-8 with thread 1. 117 | 10. At thread 2, the aforementioned while loop will be triggered. This runs: interrupt->Idle(). 118 | 11. Under Idle{}, this will run: DEBUG('i', "Machine idling; checking for interrupts.\n"); 119 | 12. Since there are no interrupts, these will run: DEBUG('i', "Machine idle. No interrupts to do.\n"); 120 | printf("No threads ready or runnable, and no pending interrupts.\n"); 121 | printf("Assuming the program completed.\n"); 122 | 13. This signifies there is nothing left and the program ends. 123 | 124 | // HOW CODE RUNS: 125 | void 126 | SimpleThread(_int which) 127 | { 128 | int num; 129 | 130 | for (num = 0; num < 3; num++) { 131 | printf("*** thread %d looped %d times\n", (int) which, num); 132 | currentThread->Yield(); 133 | 134 | /* 135 | Tick 30: T0 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T0 (Sends it to the back of the Q) and fetches first item in Q which is thread T1. 136 | Tick 40: T1 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T1 and fetches first item in Q which is thread T2 137 | Tick 50: T2 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T2 and fetches first item in Q which is thread T0 138 | 139 | Tick 60: T0 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T0 and fetches first item in Q which is thread T1. 140 | Tick 70: T1 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T1 and fetches first item in Q which is thread T2 141 | Tick 80: T2 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T2 and fetches first item in Q which is thread T0 142 | 143 | Tick 90: T0 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T0 and fetches first item in Q which is thread T1. 144 | Tick 100: T1 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T1 and fetches first item in Q which is thread T2 145 | Tick 110: T2 runs printf("*** thread %d looped %d times\n", (int) which, num); , yield() queue's T2 and fetches first item in Q which is thread T0 146 | 147 | After 3 loops: 148 | Tick 120: T0 finish()->sleep()->(switch to T1)->delete T0 149 | Tick 130: T1 finish()->sleep()->(switch to T2)->delete T1 150 | Tick 140: T2 finish()->sleep()->idle()->nointerrupts = Kill Program. 151 | */ 152 | } 153 | } 154 | 155 | //---------------------------------------------------------------------- 156 | // ThreadTest 157 | // Set up a ping-pong between two threads, by forking a thread 158 | // to call SimpleThread, and then calling SimpleThread ourselves. 159 | //---------------------------------------------------------------------- 160 | 161 | void 162 | ThreadTest() 163 | { 164 | DEBUG('t', "Entering SimpleTest"); 165 | // each q will Q the function n id ie: simplethread , 1 166 | Thread *t1 = new Thread("child1"); 167 | t1->Fork(SimpleThread, 1, 0); // Tick 10 Q T1 168 | Thread *t2 = new Thread("child2"); 169 | t2->Fork(SimpleThread, 2, 0); // Tick 20 Q T2 170 | SimpleThread(0); // Tick 30 Run simplethread with main thread of id '0', main_thread will be referred to as T0 171 | } 172 | 173 | 174 | 175 | : 176 |           __,.、__,.、_ 177 |         _r'"7_,.!-‐'-.、7-、_ 178 |       r´ >'"       ̄`ヾ!、 179 |      rゝ'´/   /i.   i 、  Y 180 | . /|   /Y ./ /メ、ハ  /| ハ   | 181 | || / ノ/! イ ○ レ' レ_,. レ'ヽ,ゝ 182 | r'-'┐/ _,ノ ノ|  "     O |ソ|   /| 183 | `l^ヽ、ヘ⌒ヽr,ヘ.  /´`ヽ  "从 ',  / .| 184 |  ヽ、_ノ7  (Y,/」>!、_,/_,. イノヘ〉r// 185 |    `ー-∠ト、7:::|/ムヽノ:::i´`7"´__ン、> 186 |    ノ\_rヘく:::::::::::ハ::::::::/-‐' ̄ 187 |    r'ア'"::::`''ーァ二ニ=-、イ、7 188 |  ノ7:::::/::::::::/|}  ノ/⌒ヽ>、 189 |  }」/::::::::::::/:::}|___,ンi二二!-' 190 |   iヽ、_:::__ンニ='-‐=-'!、__.ノ 191 |   `7二`/´ 192 |   〈__ン 193 | 194 | // EXPERIMENT 2 195 | FUNCTIONS TO BE STUDIED FOR EXPERIMENT 2: 196 | // Thread.cc, thread.h 197 | Finish() 198 | Yield() 199 | 200 | //system.cc, timer.cc and timer.h 201 | TimerInterruptHandler() <--system.cc 202 | TimerExpired() 203 | TimeOfNextInterrupt() 204 | 205 | // interrupt.h and interrupt.cc 206 | Schedule() 207 | OneTick(); 208 | CheckIfDue() 209 | YieldOnReturn() 210 | 211 | // How functions interact with one another, based on part b): 212 | When a timer interrupt is to occur (ie: at 40): 213 | 1. OneTick() is run, which in turn runs CheckIfDue() as apart of a while loop. 214 | 2. CheckIfDue() then runs: DEBUG('i', "Invoking interrupt handler for the %s at time %d\n", intTypeNames[toOccur->type], toOccur->when). 215 | 3. As CheckIfDue() runs, it will eventually call the TimerHandler() from timer.cc with: (*(toOccur->handler))(toOccur->arg). 216 | 4. TimerHandler() then calls TimerExpired() 217 | 5. TimerExpired() then schedules the next Timer interrupt at present_time+TimeOfNextInterrupt(), this is shown when the interrupt->Schedule(..) section runs: DEBUG('i', "Scheduling interrupt handler the %s at time = %d\n", intTypeNames[type], when); 218 | 6. TimerExpired() then also calls TimerInterruptHandler() from system.cc with: (*handler)(arg); 219 | 7. TimerInterruptHandler() then calls YieldOnReturn(), which sets yieldOnReturn to true. 220 | 8. Since nothing further is called, we jump all the way back to CheckIfDue(), which then returns true. 221 | 9. As CheckIfDue() returns true, the while loop in part 1. will loop once, then in the next loop CheckIfDue() returns false since the pending interrupt has already been delivered. This breaks the while loop. 222 | 10. Since yieldOnReturn has been set to true, we enter the if else section in OneTick(), which then runs yield(). 223 | 11. When yield() is run, this sends the main thread to the ready queue and fetches the first thread in the ready Q to be run. 224 | 12. This allows for the forked threads to be run essentially. 225 | 13. When each forked thread ends, it runs finish(), which resets the next timer interrupt, and then calls sleep() 226 | 14. sleep() then calls for the next thread in the queue to be run. 227 | (I have no idea why, but only the last thread to be called to sleep before main is specifically deleted by a destructor. probably due to how scheduler.cc->Run()->SWITCH(oldThread, nextThread); snippet works.) 228 | (It is a mystery) 229 | 15. This process is repeated untill the main thread is run again. 230 | 16. Once at the main thread, the next bunch of threads will be forked. (Since we are now back at the main section of the code, see bottom for better explaination) 231 | 17. At some point the next timer interrupt will be due to occur, then we go back to step 1. repeat. 232 | 233 | 234 | PART A): 235 | 236 | // THEORY 237 | 238 | // Because part A wants us to trigger an interrupt every 40 seconds, this means at every 40 second interval: 239 | // The interrupt will be triggered 240 | // when first interrupt is triggered, all queued thread's functions (Simplethread) will be run one by one. 241 | // The second interrupt will be scheduled 40 seconds later. 242 | // each run thanks to for loop 'for (num = 0; num < 1000000; num++) {}' will take 10 ticks. 243 | // thus for each 10 tick one thread will be completed 244 | // At the second interrupt, we will jump back into the main code, where will fork the next 3 threads to Q (add thread's functions to Q). 245 | // The third interrupt will be scheduled 40 seconds later. 246 | // Each addition to the Q takes 10 ticks. 247 | // At the third interrupt, same thing happens as the first. 248 | // At the fourth interrupt, same thing happens as the second. 249 | // Repeat untill we reach SimpleThread(0). 250 | /* 251 | Tick 10 - 30: Fork (queue) threads 1-3 and their functions (SimpleThread) 252 | Tick 40: invokve interrupt, schedule next interrupt, switch to running queued threads (1-3) by calling yield(). 253 | Tick 50-70: run queued threads (1-3), at last thread, finish() calls sleep(), which switches us back to the main program for forking threads 4-6. 254 | Tick 80: invokve interrupt, schedule next interrupt, call yield() <- waste 255 | Tick 90-110: fork threads 4-6 256 | Tick 120: invokve interrupt, schedule next interrupt, switch to running queued threads (4-6) by calling yield(). 257 | Tick 130-150: run queued threads (4-6), at last thread, finish() calls sleep(), which switches us back to the main program for forking threads 7-9. 258 | Tick 160: invokve interrupt, schedule next interrupt, call yield() <- waste 259 | Tick 170-190: fork threads 7-9 260 | Tick 200: invokve interrupt, schedule next interrupt, switch to running queued threads(7-9) by calling yield(). 261 | Tick 210-230: run queued threads(7-9), at last thread, finish() calls sleep(), which switches us back to the main program. 262 | Tick 240: invokve interrupt, schedule next interrupt, call yield() <- waste 263 | Tick 250: run main thread with function SimpleThread ( SimpleThread(0);). Since nothing left, program completed 264 | */ 265 | 266 | // CODE TO BE EDITED 267 | 268 | //Under system.cc, set randomYield to TRUE: 269 | bool randomYield = TRUE; // timer is activated by this? i changed this. 270 | // This will permanently activate the function we need in Timer.cc 271 | 272 | // Next, in Timer.cc copy this section in Timer() into TimerExpired(). 273 | interrupt->Schedule(TimerHandler, (_int) this, TimeOfNextInterrupt(), 274 | TimerInt); 275 | // This will ensure TimerExpired will always schedule the next interrupt after a current interrupt is invoked.. 276 | 277 | // FInally, under TimeOfNextInterrupt(), set return to permanently 40, or comment out the section under if(randomize) and set it to permanently return 40, like so: 278 | if (randomize) 279 | return 40; // 1 + (Random() % (TimerTicks * 2)); <- original, changed to return 40 280 | // since randomize = true for timer to be activated 281 | else 282 | return TimerTicks; 283 | // This ensures, since random is set to True pemanently, that 40 ticks will always be returned when timer expires in an interrupt invocation. 284 | 285 | 286 | PART B): 287 | 288 | // THEORY 289 | 290 | // In part b), whenever a thread is finished running (ie: printf("Thread %d Completed.\n",(int)which);), they want the scheduled timer interrupt to be reset to the current tick + 40 ticks, ie: 291 | // if in tick 50, thread 1 finishes. the original scheduled timer interrupt at 80 seconds will now be at 90 seconds.. 292 | // This saves on ticks, as when the last thread that is not 'main thread' in Q runs, it also resets the scheduled next interrupt time, that means in the next 10 Ticks: 293 | // it can go straight to forking, without the need to spend a whole 10 Ticks invoke interrupt, schedule the next interrupt and running yield() . like in part a) 294 | // thus overall shorter time needed 295 | /* 296 | Tick 10-30: Fork 1-3 297 | Tick 40: Invoke Interrupt, next at 80, switch to running queued threads by calling yield(). 298 | Tick 50: Finish thread1, reset interrupt to 40 ticks later (Interrupt at 90) 299 | Tick 60: Finish thread2, Interrupt reset to 100 300 | Tick 70: Finish thread3, Interrupt reset to 110, at last thread, finish() calls sleep(), which switches us back to the main program for forking threads 301 | Tick 80 - 100: Fork 4-6 <-- Tick of 10 saved here 302 | Tick 110: Invoke Interrupt, next at 150, switch to running queued threads by calling yield(). 303 | Tick 120: Finish thread4, Interrupt reset to 160 304 | Tick 130: Finish thread5, Interrupt reset to 170 305 | Tick 140: Finish thread6, Interrupt reset to 180, at last thread, finish() calls sleep(), which switches us back to the main program for forking threads 306 | Tick 150-170: Fork 7-9 307 | Tick 180: Invoke Interrupt, next at 220, switch to running queued threads by calling yield(). 308 | Tick 190: Finish a thread7, Interrupt reset to 230 309 | Tick 200: Finish a thread8, Interrupt reset to 240 310 | Tick 210: Finish a thread9, Interrupt reset to 250, at last thread, finish() calls sleep(), which switches us back to the main program. 311 | Tick 220: Run main thread with function SimpleThread ( SimpleThread(0);). Since nothing left, program completed 312 | <30 ticks saved!> 313 | 314 | */ 315 | 316 | // CODE TO BE EDITED 317 | 318 | // Under threads.cc, find the finish() function portion, then: 319 | // Step 1. add in this code chunk after #ifdef CHANGED: 320 | #ifdef CHANGED 321 | static void TimerHandler(_int arg) 322 | { Timer *p = (Timer *)arg; p->TimerExpired(); } 323 | // This is a copy of a function that we will need to use. 324 | 325 | // Step 2. Under the finish() function, under both if else results, insert this one line above sleep(): 326 | (void) interrupt->Schedule(TimerHandler, (_int)timer ,400,TimerInt); 327 | // This snippet will tell the universal interrupt pointer (interrupt->) to : 328 | // 1. Feed the universal timer pointer (timer) to TimerHandler(). 329 | // 2. TimerHandler() (using universal timer pointer) signals that the current interrupt is expired and due to be replaced. 330 | // 3. That the next interrupt is to be 400 seconds from now, and that it is Type TimerInt (you may simply set this to 40 seconds instead) 331 | 332 | 333 | // Under interrupt.cc, check the schedule() function: 334 | // Add this snippet if you used 400 seconds from now: 335 | if(fromNow == 400){ 336 | delete pending->Remove(); 337 | fromNow = fromNow / 10;} // '400' signifies that this schedule call was from a thread finishing, and thus must be corrected. 338 | 339 | // Add this if you used 40 seconds: 340 | delete pending->Remove(); // This deletes the present scheduled interrupt. 341 | 342 | // The rest of the schedule() function will schedule a new interrupt at present time + 40, thus fulfilling our requirements. 343 | 344 | 345 | // HOW CODE RUNS: 346 | Part A) 347 | 348 | void 349 | SimpleThread(_int which) 350 | { 351 | int num; 352 | 353 | for (num = 0; num < 1000000; num++) {} 354 | printf("Thread %d Completed.\n",(int)which); 355 | } 356 | 357 | void 358 | ThreadTest() 359 | { 360 | DEBUG('t', "Entering SimpleTest"); // Interrupt Scheduled at 40 361 | 362 | Thread *t1 = new Thread("child1"); 363 | t1->Fork(SimpleThread, 1, 0); // Tick 10 Q T1 364 | Thread *t2 = new Thread("child2"); 365 | t2->Fork(SimpleThread, 2, 0); // Tick 20 Q T2 366 | Thread *t3 = new Thread("child3"); 367 | t3->Fork(SimpleThread, 3, 0); // Tick 30 Q T3 368 | 369 | // TICK 40 - INTERRUPT - NEXT SCHEDULED AT 80 370 | // YIELD() RUNS: T1 <- FIRST ITEM IN Q RETRIEVED, MAIN_THREAD ADDED TO BACK OF Q, RUNS T1 371 | // T1 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T2 372 | // T2 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T3 373 | // T3 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), T3 DELETED, PASS ON TO MAIN_THREAD 374 | 375 | // PROGRAM RETURNS BACK TO HERE 376 | // TICK 80 - INTERRUPT - NEXT SCHEDULED AT 120 377 | 378 | Thread *t4 = new Thread("child4"); 379 | t4->Fork(SimpleThread, 4, 0); // Tick 90 Q T4 380 | Thread *t5 = new Thread("child5"); 381 | t5->Fork(SimpleThread, 5, 0); // Tick 100 Q T5 382 | Thread *t6 = new Thread("child6"); 383 | t6->Fork(SimpleThread, 6, 0); // Tick 110 Q T6 384 | 385 | // TICK 120 - INTERRUPT - NEXT SCHEDULED AT 160 386 | // YIELD() RUNS: T4 <- FIRST ITEM IN Q RETRIEVED, MAIN_THREAD ADDED TO BACK OF Q, RUNS T4 387 | // T4 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T5 388 | // T5 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T6 389 | // T6 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), T6 DELETED, PASS ON TO MAIN_THREAD 390 | 391 | // PROGRAM RETURNS BACK TO HERE 392 | // TICK 160 - INTERRUPT - NEXT SCHEDULED AT 200 393 | 394 | Thread *t7 = new Thread("child7"); 395 | t7->Fork(SimpleThread, 7, 0); // Tick 170 Q T7 396 | Thread *t8 = new Thread("child8"); 397 | t8->Fork(SimpleThread, 8, 0); // Tick 180 Q T8 398 | Thread *t9 = new Thread("child9"); 399 | t9->Fork(SimpleThread, 9, 0); // Tick 190 Q T9 400 | 401 | 402 | // TICK 200 - INTERRUPT - NEXT SCHEDULED AT 240 403 | // YIELD() RUNS: T7 <- FIRST ITEM IN Q RETRIEVED, MAIN_THREAD ADDED TO BACK OF Q, RUNS T7 404 | // T7 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T8 405 | // T8 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T9 406 | // T9 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), T9 DELETED, PASS ON TO MAIN_THREAD 407 | 408 | // PROGRAM RETURNS BACK TO HERE 409 | // TICK 240 - INTERRUPT - NEXT SCHEDULED AT 280 410 | 411 | 412 | SimpleThread(0); // Tick 250: run this, program ends. as idle determines no threads, interrupts or anything in Q left. 413 | } 414 | 415 | Part B) 416 | 417 | void 418 | SimpleThread(_int which) 419 | { 420 | int num; 421 | 422 | for (num = 0; num < 1000000; num++) {} 423 | printf("Thread %d Completed.\n",(int)which); 424 | } 425 | 426 | void 427 | ThreadTest() 428 | { 429 | DEBUG('t', "Entering SimpleTest"); // Interrupt Scheduled at 40 430 | 431 | Thread *t1 = new Thread("child1"); 432 | t1->Fork(SimpleThread, 1, 0); // Tick 10 Q T1 433 | Thread *t2 = new Thread("child2"); 434 | t2->Fork(SimpleThread, 2, 0); // Tick 20 Q T2 435 | Thread *t3 = new Thread("child3"); 436 | t3->Fork(SimpleThread, 3, 0); // Tick 30 Q T3 437 | 438 | // TICK 40 - INTERRUPT - NEXT SCHEDULED AT 80 439 | // YIELD() RUNS: T1 <- FIRST ITEM IN Q RETRIEVED, MAIN_THREAD ADDED TO BACK OF Q, RUNS T1 440 | // TICK 50 - T1 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T2, NEXT INTERRUPT SCHEDULED AT 90 441 | // TICK 60 - T2 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T3, NEXT INTERRUPT SCHEDULED AT 100 442 | // TICK 70 - T3 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), T3 DELETED, PASS ON TO MAIN_THREAD, NEXT INTERRUPT SCHEDULED AT 110 443 | 444 | // PROGRAM RETURNS BACK TO HERE 445 | // nothing runs here: ticks saved 446 | 447 | Thread *t4 = new Thread("child4"); 448 | t4->Fork(SimpleThread, 4, 0); // Tick 80 Q T4 449 | Thread *t5 = new Thread("child5"); 450 | t5->Fork(SimpleThread, 5, 0); // Tick 90 Q T5 451 | Thread *t6 = new Thread("child6"); 452 | t6->Fork(SimpleThread, 6, 0); // Tick 100 Q T6 453 | 454 | // TICK 110 - INTERRUPT - NEXT SCHEDULED AT 150 455 | // YIELD() RUNS: T4 <- FIRST ITEM IN Q RETRIEVED, MAIN_THREAD ADDED TO BACK OF Q, RUNS T4 456 | // TICK 120 - T4 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T5, NEXT INTERRUPT SCHEDULED AT 160 457 | // TICK 130 - T5 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T6, NEXT INTERRUPT SCHEDULED AT 170 458 | // TICK 140 - T6 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), T6 DELETED, PASS ON TO MAIN_THREAD, NEXT INTERRUPT SCHEDULED AT 180 459 | 460 | // PROGRAM RETURNS BACK TO HERE 461 | // nothing runs here: ticks saved 462 | 463 | Thread *t7 = new Thread("child7"); 464 | t7->Fork(SimpleThread, 7, 0); // Tick 150 Q T7 465 | Thread *t8 = new Thread("child8"); 466 | t8->Fork(SimpleThread, 8, 0); // Tick 160 Q T8 467 | Thread *t9 = new Thread("child9"); 468 | t9->Fork(SimpleThread, 9, 0); // Tick 170 Q T9 469 | 470 | 471 | // TICK 180 - INTERRUPT - NEXT SCHEDULED AT 220 472 | // YIELD() RUNS: T7 <- FIRST ITEM IN Q RETRIEVED, MAIN_THREAD ADDED TO BACK OF Q, RUNS T7 473 | // TICK 190 - T7 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T8, NEXT INTERRUPT SCHEDULED AT 230 474 | // TICK 200 - T8 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), PASS ON TO T9, NEXT INTERRUPT SCHEDULED AT 240 475 | // TICK 210 - T9 RUN SIMPLETHREAD FUNCTION, FINISH(), SLEEP(), T9 DELETED, PASS ON TO MAIN_THREAD, NEXT INTERRUPT SCHEDULED AT 250 476 | 477 | // PROGRAM RETURNS BACK TO HERE 478 | // nothing runs here: ticks saved 479 | 480 | 481 | SimpleThread(0); // Tick 220: run this, program ends. as idle determines no threads, interrupts or anything in Q left. 482 | } 483 | 484 | : // EXPERIMENT 3 485 |        _,,..-=ニ二7 486 |      r'ア´ 487 |     ノノ   r'ア'"`ヽ.,_ _,,..-=-、    _,. -rァ 488 |    r'ァ⌒ヽ、i7::::::::;>''"´:: ̄ ̄`"''<´:::::::::!( 489 |    .||    r!:::/::::::::::::::::::::::::::::::::::::::::ヽ:::::/i' 490 |    ||      ^Y:::::;:::::i:::::::::/i::::::i:::::::;:::::::;::::Y (_ 491 |    ||    ./i::::::i:::/!--/ |:::;ハ_!_::i::::::i:::::::i r'ヽ. 492 |    !!    く:::L__ハ/-‐‐'  レ' _!__;ハ::::ハ:::::|_,ゝ:::', 493 |    ',',ヽ.   ヽヘ「7""         `レ7´)/:::iヽ;:::i 494 |    i´`とン' ´`ヽ!,人  「' ̄ `i ""7_/'´':::::::! i:::!    "What the fuck is going on" 495 |    ヽ.,_//」 、_,ノ:::::ノ>.、、,___,ノ_,,.イ:::!、__!7ノ__. レ' 496 |      i   ゝ-ァ'/ /)_iヽ/ /(/ゝ、.,_ノ   ̄「iー-、 497 |     ノ〈)    `  /::::ソ^ヽ、/」::_r' _/ /」      |つ-' 498 |    <.,  _____,,,... イ::::くr-、_」:::::::::Y^ヽ、    [] ', 499 |       ̄  レ'   l>-、::;;_______;;::」〉'ノヽ.   __ 〉 500 |    ((    r'ア'"/:::/ i ヽ;::::ヽ`''::ーァ、`''ー-┴'" 501 |         r'i:::/::::::/ .l   ';:::::::::::::::::!,」 502 |         `ゝ、:::::/  l   ヽ;::::::::::/」 503 |           └へ>、,_!_______,,..>ァニン! 504 |             'r-- 'i    ̄ヽ,_ノ 505 |             ヽ二ノ 506 | 507 | 508 | PART 1) 509 | TASK: After running 2 INC and 2DEC, the final result should be 1 due to race conditions.. 510 | 511 | //CODE 512 | void Inc_v1(_int which) 513 | { 514 | //fill your code 515 | int a=value; 516 | a++; 517 | currentThread->Yield(); // context switches here to next thread in Q (CS) 518 | value=a; 519 | printf("**** Inc thread %d new value %d\n", (int) which, value); 520 | } 521 | 522 | void Dec_v1(_int which) 523 | { 524 | //fill your code 525 | int a=value; 526 | a--; 527 | currentThread->Yield(); // context switches here to next thread in Q (CS) 528 | value=a; 529 | printf("**** Dec thread %d new value %d\n", (int) which, value); 530 | } 531 | 532 | void TestValueOne() 533 | { 534 | value=0; 535 | printf("enter TestValueOne, value=%d...\n", value); 536 | //1. fill your code here. 537 | Thread *t1 = new Thread("child1"); // thread is created 538 | t1->Fork(Inc_v1, 1, 0); // fork(function to be run, thread id given, not to be joined) 539 | Thread *t2 = new Thread("child2");// thread is created 540 | t2->Fork(Dec_v1, 2, 0);// fork(function to be run, thread id given, not to be joined) 541 | Thread *t3 = new Thread("child3");// thread is created 542 | t3->Fork(Dec_v1, 3, 0);//fork(function to be run, thread id given, not to be joined) 543 | Thread *t4 = new Thread("child4");// thread is created 544 | t4->Fork(Inc_v1, 4, 1); // fork(function to be run, thread id given, to be joined) 545 | currentThread->Join(t4); // join current thread (CT) to t4 546 | // this means CT progress will be paused untill t4 is finished 547 | 548 | //2. checking the value. you should not modify the code or add any code lines behind 549 | //this section. 550 | if(value==1) 551 | printf("congratulations! passed.\n"); 552 | else 553 | printf("value=%d, failed.\n", value); 554 | }//CODE 555 | 556 | // THEORY: 557 | Based on the pattern, the Q looks like this: 558 | t1,t2,t3.t4, 559 | 560 | since CT is joined to t4:, this means untill t4 is finished (where it runs printf), the rest of the code will not be run, ie: if(value==1) will not be checked. 561 | 562 | finish() moves to next thread in Q and deletes current thread. 563 | 564 | runs like so: (CS: context switch) 565 | 1. t1: a=1, CS->t2, queue t1, queue now looks like: t2,t3,t4,t1 566 | 2. t2: a=-1, CS->t3 queue t2, queue now looks like: t3,t4,t1,t2 567 | 3. t3: a=-1, CS->t4, queue t3, queue now looks like: t4,t1,t2,t3 568 | 4. t4: a=1, CS->t1, queue t4, queue now looks like: t1,t2,t3,t4 569 | 5. t1: value=1, run printf(), finish(t1)->CS->t2, queue now looks like: t2,t3,t4 570 | 6. t2: value=-1, run printf(), finish(t2)->CS->t3, queue now looks like: t3,t4 571 | 7. t3: value=-1, run printf(), finish(t3)->CS->t4, queue now looks like: t4 572 | 8. t4: value=1, run printf(), finish(t4)->CS->CT, queue now looks like: 573 | 9. since t4 is finished, were now back in CT, now the rest of code will be run, ie: if(value==1). 574 | 575 | for TestValueMinusOne, swap the inc & dec running function patterns: 576 | 577 | //CODE 578 | Thread *t1 = new Thread("child1"); 579 | t1->Fork(Dec_v2, 1, 0); 580 | Thread *t2 = new Thread("child2"); 581 | t2->Fork(Inc_v2, 2, 0); 582 | Thread *t3 = new Thread("child3"); 583 | t3->Fork(Inc_v2, 3, 0); 584 | Thread *t4 = new Thread("child4"); 585 | t4->Fork(Dec_v2, 4, 1); // indicate item is to be joined changed 586 | currentThread->Join(t4); 587 | //CODE 588 | 589 | or: 590 | -1 591 | 1 592 | 1 593 | -1 594 | 595 | thus it runs like so: 596 | 1. t1: a=-1, CS->t2, queue t1, queue now looks like: t2,t3,t4,t1 597 | 2. t2: a=1, CS->t3 queue t2, queue now looks like: t3,t4,t1,t2 598 | 3. t3: a=1, CS->t4, queue t3, queue now looks like: t4,t1,t2,t3 599 | 4. t4: a=-1, CS->t1, queue t4, queue now looks like: t1,t2,t3,t4 600 | 5. t1: value=-1, run printf(), finish(t1)->CS->t2, queue now looks like: t2,t3,t4 601 | 6. t2: value=1, run printf(), finish(t2)->CS->t3, queue now looks like: t3,t4 602 | 7. t3: value=1, run printf(), finish(t3)->CS->t4, queue now looks like: t4 603 | 8. t4: value=-1, run printf(), finish(t4)->CS->CT, queue now looks like: 604 | 9. since t4 is finished, were now back in CT, now the rest of code will be run, ie: if(value==1). 605 | 606 | 607 | PART 2): 608 | To Ensure Consistency in increment & decrement, semaphores are used. 609 | 610 | //CODE 611 | Semaphore *S = new Semaphore("S1",1); // Create semaphore S with name "S1" and init val '1' 612 | //2. implement the new version of Inc: Inc_Consistent 613 | void Inc_Consistent(_int which) 614 | { 615 | //fill your code 616 | S->P(); // Wait() equivalent 617 | int a=value; 618 | a++; 619 | currentThread->Yield(); // changed 620 | value=a; 621 | printf("**** Inc thread %d new value %d\n", (int) which, value); 622 | S->V(); // Signal() Equivalent 623 | } 624 | 625 | //3. implement the new version of Dec: Dec_Consistent 626 | void Dec_Consistent(_int which) 627 | { 628 | //fill your code 629 | 630 | S->P(); // Wait() equivalent 631 | int a=value; 632 | a--; 633 | currentThread->Yield(); // changed 634 | value=a; 635 | printf("**** Dec thread %d new value %d\n", (int) which, value); 636 | S->V(); // Signal() Equivalent 637 | } 638 | 639 | //4. implement TestValueMinusOne by create two threads with Inc_Consistent and two threads with Dec_Consistent 640 | // you should pass the checking at the end, printing "congratulations! passed." 641 | void TestConsistency() 642 | { 643 | value=0; 644 | printf("enter TestConsistency, value=%d...\n", value); 645 | 646 | //fill your code 647 | Thread *t1 = new Thread("child1"); 648 | t1->Fork(Inc_Consistent, 1, 0); 649 | Thread *t2 = new Thread("child2"); 650 | t2->Fork(Dec_Consistent, 2, 0); 651 | Thread *t3 = new Thread("child3"); 652 | t3->Fork(Dec_Consistent, 3, 0); 653 | Thread *t4 = new Thread("child4"); 654 | t4->Fork(Inc_Consistent, 4, 1); // indicate item is to be joined changed 655 | currentThread->Join(t4); 656 | 657 | //2. checking the value. you should not modify the code or add any code lines behind 658 | //this section. 659 | if(value==0) 660 | printf("congratulations! passed.\n"); 661 | else 662 | printf("value=%d, failed.\n", value); 663 | }//CODE 664 | 665 | //THEORY 666 | Semaphore's ->P() is equvalent to wait(). 667 | 668 | Semaphore's ->V() is equvalent to signal(). 669 | 670 | Due to semaphore inclusion, it runs like so: 671 | 672 | 1. t1: passes S->P() and enters critical , a=1, CS->t2, queue t1, queue is now: t2,t3,t4,t1 673 | 2. t2: fails S->P() as t1 - critical , sleep t2 thus CS->t3, queue t2, queue is now: t3,t4,t1,t2 674 | 3. t3: fails S->P() as t1 - critical , sleep t3 thus CS->t4, queue t3, queue is now: t4,t1,t2,t3 675 | 4. t4: fails S->P() as t1 - critical , sleep t4 thus CS->t1, queue t4, queue is now: t1,t2,t3,t4 676 | 5. t1: value=1, printf, passes S->V() and exits critical, finish()->CS->t2, queue is now: t2,t3,t4 677 | 678 | 6. t2: passes S->P() and enters critical , a=0, CS->t3, queue t2, queue is now: t3,t4,t2 679 | 7. t3: fails S->P() as t2 - critical , sleep t3 thus CS->t4, queue t3, queue is now: t4,t2,t3 680 | 8. t4: fails S->P() as t2 - critical , sleep t4 thus CS->t2, queue t4, queue is now: t2,t3,t4 681 | 9. t2: value = 0, printf, passes S->V() and exits critical, finish()->CS->t3, queue is now: t3,t4 682 | 683 | 10. t3: passes S->P() and enters critical , a=-1, CS->t4, queue t3, queue is now: t4,t3 684 | 11. t4: fails S->P() as t3 - critical , sleep t4 thus CS->t3 queue t4, queue is now: t3,t4 685 | 12. t3: value=-1, printf, passes S->V() and exits critical, finish()->CS->t4, queue is now: t4 686 | 687 | 688 | 13. t4: passes S->P() and enters critical , a=0, no usual CS + Queue from -> yield as no other threads in Q left. 689 | 14. t4: value=0, printf, passes S->V() and exits critical, finish()->CT 690 | 15. since t4 is finished, were now back in CT, now the rest of code will be run, ie: if(value==1). 691 | 692 | // FUNCTIONS TO REVISE: 693 | Thread() 694 | Fork() 695 | Yield() 696 | Sleep() 697 | Finish() 698 | Join() 699 | 700 | Nachos Semaphores Functions 701 | Nachos Lock Functions 702 | ^ Both are in synch.cc 703 | 704 | : // EXPERIMENT 4 705 |            ,. -ー- 、                 ,. -ー- 、 706 |    '         /.,riljljljh, ヽ _ ,,, .. --ー.ー-- .. ,,, _ /.,riljljljh, ヽ      / 707 |     '        ! il|l|l|l|l|l|l! i . . . . . . . . . . . . . . . . . . ! il|l|l|l|l|l|l! i       / 708 |     '.      ヽヾ!ilililiツ ,'. . . . . . . . . . . . . . . . . . .ヽヾ!ilililiツ ,'    /      / 709 |     ',        `'‐-,-‐": : : . . . . . . . . . . . . . . . : : :`'‐-,-‐"    /     / 710 |      ',         ./ : : : : : : . . . . . . . . . . . . . . : : : : : : ヘ.           / 711 |      ',        / : : : : : : : : : : : : : : : : : : : : : : : : : : : : ヽ      / 712 |       ',     ./ __,,..  ..-..--..─―..―..ー..-..  、、..__: :ヽ      _,、∧/ 713 | \      _,,. r;:'' "; ;:;:- '"´ ̄`ヽ、::::-─- '"´ ̄ `ヽ、; ; ``;ヽ.、  「 714 |       ,.r'" ; ; ; _:;ア´                     ヽ、 ; ; ; ; <   I HATE NACHOS 715 |     , ' ; ; ; ; ; ア´/  , '´   /     i    ',    i   Y; ; ; ; ; ; >. I HATE NACHOS 716 | ..,,_  i ; ; ; ; ; ;:/ /  /   i. 、,'  ハ  ,ハ   ,i   ハ_    iヽ;: ; ; ; ;>  I HATE NACHOS 717 |     ヽ、; ; ; ノ ,'  .i   ハ  i\/ ', / i  / i ,.イ´./i   !  i; ;/   I HATE NACHOS 718 | _____    `イ   i   i  ./ ァ'" ̄`ヽー/  | /,ァ''" ̄`ヽハ  ハ ∠_.    I HATE NACHOS 719 |     ∧ '" /| ノ ,ハイ   i'´'`i     レ'  i'´'`i.   ト| /ニi,_  ヽ7  I HATE NACHOS 720 | \∧/  Vi/  |_,. -‐ァi/    !__,リ          !__,リ   ' レ'  ̄!ヾ'、 ./へ 721 |  NO  MORE   ./=i.  `'' ー-         -‐ ''´  i==!i !i  i  \/V\/ 722 | NO  MORE   .∠, i. ""       `       ""!  .i!l !i. ',      ─--- 723 | NO  MORE    ./  i.       ,.'´ ̄ ̄`ヽ       .ィ  .!!l  !i. ヽ. 724 | NO  MORE   ./_ , ト.     i.,.-ー─-、.i    ,.イ!   .!ヾ='<   ',    - ..,,__ 725 | NO  MORE    ./  !::::ヽ   ヽ,    ノ   ノ'':::::i.   ', ‐ 、 ヽ  i       726 |  MORE      く   i::::::::`i丶.、.,, ̄ ̄ ,.. イ::::::::::::i    ,j   ヽ、ヘ/     \ 727 | NMORE       .>  .i:::::::::::ヽ、.,__ ̄ ̄__,.ノ:::::::::::::i.   ,ィ'.     ヽ.  ',   \ 728 |     !!!      <.ヽ   !::::::::::::::::::::: ̄ ̄::::::::::::::::::::::!  /       ヽ,  ', 729 | 730 | 731 |    \       ',     |       /        /      , ' 732 |     \      ',     |     /        /     , ' 733 |       \                      /     , ' 734 |          ァ' ⌒ヽ._,. --──-- ァ' ⌒ヽ.       , ' 735 | ` 、       ! (:::) i        .! (:::) i 736 |          ':、.,__,.ノ         ':、.,__,.ノ 737 |          /               ',        --  ── 738 |         _/-‐ニ=--────-=ニ、..,,_';、.,_ 739 |      ,..-'´ァ''"´               `"ヽ`ヽ、 740 | ─--r'"´:::::::/ .  | 、  !∧  ./!__」イ´ .   |::::::::::::::`ヽ. 741 |    ヽ、::::::/ '、  ! ,>t、 \/'´l'ハ`ヽ./ ∧::::::::::::::::::ノ 742 |       `ヽ   \| 7  l,ハ     !_ソ ノレ'  \::::- ''´ 743 |        ヽ  /レヘ、 ゝ'  '     ⊂⊃  !, '⌒ヽ. 744 |        レイ ⊂⊃  rァ'´ ̄`ヽ. u / ! 7    | 745 |         /  ,ハ、   !      ノ / / ,'    / 746 |         |    > 、.,_   _,. イ'|/ ./   / 747 |         |  /ヽ_,.. イ「こ__/ /____/    ,'-、_ 748 |         レ´/\::::::::::::::::::::::::::::::::_/    /`ヽ._ \ 749 |         /´`'ー--|:::ト∧ノ::i´ ̄ ̄     ,'   / `ヽヽ、 750 |        ,r/     r)/o-o、'、__,.、     ー!   /  / )Y 751 |       /、____,.イ7:::/ ̄i´:::::::| /ヽ.,_ `ーヽ._/ /| .| 752 |      /      |(`'っ .!ソ:::::::::', '、:::::`ー`ヽ、_  `ン::/ / 753 |     /       /:ゝ、.つと):::::::::\\::::::::::::::::::: ̄:::// 754 | 755 | 756 | //THEORY: 757 | Key Functions: 758 | VPNToPhyPage() 759 | InsertToTLB() 760 | lrualgorithm() 761 | 762 | Slide 14: Diagram for how functions work together with thought process. 763 | 764 | In general: 765 | 1. check tlb for translation. 766 | 2. not in tlb? check iverse page table 767 | 3. in ipt! update tlb. not in ipt! call backing store. 768 | 4. tell backing store to update w direct insert if there are empty frames or LRU otherwise, ipt then updates tlb 769 | 770 | Process: 771 | 772 | 773 | 774 | 1. No valid page found in TLB. 775 | 776 | Translate 0x0, read: *** no valid TLB entry found for this virtual page 0! 777 | 778 | 2. Runs VpnToPhyPage() to list Inverse page table entries and determine if page can be found here: (here 'frame '/'IPT: ' is interchangeable w phyPage) 779 | 780 | ...IPT: 0 , pid: 0 vpn: 0 last_used: 0 validtity: 0 781 | ...IPT: 1 , pid: 0 vpn: 0 last_used: 0 validtity: 0 782 | ...IPT: 2 , pid: 0 vpn: 0 last_used: 0 validtity: 0 783 | ...IPT: 3, pid: 0 vpn: 0 last_used: 0 validtity: 0 784 | .....IPT w matching pid and vpn missing! <- not found. 785 | 786 | 3. Run DoPageIn() to move page from disk store to TLB n IPT 787 | 788 | paging in: pid 0, phyPage 0, vpn 0 789 | 790 | 4. Run InsertToTlb() , lists tlb pre update: 791 | 792 | TLB pre-update: 0 <- which tlb entry it will update, here it indicates it will update 'TLB 0', chooses the oldest entry in tlb if none free. which is the one next to the newest entry, ie: if previous insert is tlb 0, next is tlb 1 793 | ...TLB 0, vpn, phy & valid: 0 0 0 794 | ...TLB 1, vpn, phy & valid: 0 0 0 795 | ...TLB 2, vpn, phy & valid: 0 0 0 796 | 797 | 5. and prints out post update that in the TLB: 798 | 799 | The corresponding TLBentry for Page 0 in TLB is 0 <-phy value 800 | 801 | 6. post page in will look like this: 802 | ...IPT: 0, pid: 0 vpn: 0 last_used: 12 validtity: 1 <- indicates IPT 0 was last used at tick 12 803 | ...TLB 0, vpn, phy & valid: 0 0 1 <- 'TLB 0' was updated 804 | 805 | 806 | 807 | 808 | Translate 0x30, read: *** no valid TLB entry found for this virtual page 0! 809 | Exception: page fault/no TLB entry 810 | ...IPT: 0, pid: 0 vpn: 0 last_used: 12 validtity: 1 811 | ...IPT: 1, pid: 0 vpn: 9 last_used: 25 validtity: 1 812 | ...IPT: 2, pid: 0 vpn: 26 last_used: 17 validtity: 1 813 | ...IPT: 3, pid: 0 vpn: 1 last_used: 22 validtity: 1 814 | .....Caught IPT 0, pid: 0 vpn: 0 last_used: 12 validtity: 1 <- virutal page 0 found in IPT 0 or phy frame 0 815 | 816 | // Continue as per normal but without any paging ins. 817 | 818 | // Per normal being that a TLB entry will be updated with the caught entry in IPT 819 | 820 | 821 | 822 | // NOTE: LruAlgorithm() will prioritize validity over oldest frame, see further down below: 823 | 824 | Translate 0x528, read: *** no valid TLB entry found for this virtual page 10! 825 | ...IPT: 0, pid: 0 vpn: 0 last_used: 28 validtity: 1 826 | ...IPT: 1, pid: 0 vpn: 9 last_used: 25 validtity: 1 827 | ...IPT: 2, pid: 0 vpn: 26 last_used: 17 validtity: 1 828 | ...IPT: 3, pid: 0 vpn: 1 last_used: 22 validtity: 1 <- as we can see here, IPT is full 829 | .....IPT w matching pid and vpn missing! 830 | paging out: pid 0, phyPage 2, vpn 26 <-- NOTE: paging out is not always done, pageout only occurs due to dirty bit being set, which means it has been modified and must be written back to disk 831 | paging in: pid 0, phyPage 2, vpn 10 <-- Using LruAlgorithm() and DoPageoutPagein(), 'IPT 2' / frame 2 / phyPage 2 with last_used:17, which indicates it was last used at tick 17, is the oldest frame. 832 | 833 | // This oldest frame due to it's dirty bit is thus selected for page out in order to take in 'VPN 10'. 834 | // The TLB entry for vpn 26 is also immediately made invalid before update shown here: 835 | 836 | TLB pre-update: 2 837 | ...TLB 0, vpn, phy & valid: 1 3 1 838 | ...TLB 1, vpn, phy & valid: 0 0 1 839 | ...TLB 2, vpn, phy & valid: 26 2 0 <-- immediately invalid 840 | 841 | // Thus after the new VPN is paged in and IPT n TLB are updated: 842 | ...IPT: 2, pid: 0 vpn: 10 last_used: 28 validtity: 1 843 | ...TLB 2, vpn, phy & valid: 10 2 1 844 | 845 | // Note, without dirty bit set: 846 | 847 | Translate 0xc0, read: *** no valid TLB entry found for this virtual page 1! 848 | Exception: page fault/no TLB entry 849 | ...IPT: 0, pid: 0 vpn: 0 last_used: 49 validtity: 1 850 | ...IPT: 1, pid: 1 vpn: 26 last_used: 66 validtity: 1 851 | ...IPT: 2, pid: 1 vpn: 0 last_used: 61 validtity: 1 852 | ...IPT: 3, pid: 1 vpn: 9 last_used: 68 validtity: 1 853 | .....IPT w matching pid and vpn missing! 854 | paging in: pid 1, phyPage 0, vpn 1 <-- only page in. 855 | TLB pre-update: 0 856 | ...TLB 0, vpn, phy & valid: 0 2 1 <-- thus no immediate change to invalid. 857 | ...TLB 1, vpn, phy & valid: 9 3 1 858 | ...TLB 2, vpn, phy & valid: 26 1 1 859 | 860 | // Thus after IPT n TLB are updated: 861 | ...IPT: 0, pid: 1 vpn: 1 last_used: 71 validtity: 1 862 | ...TLB 0, vpn, phy & valid: 1 0 1 863 | 864 | // In a scenario where there are invalid entries in IPT: 865 | 866 | ...IPT: 0, pid: 1 vpn: 1 last_used: 71 validtity: 0 867 | ...IPT: 1, pid: 1 vpn: 26 last_used: 66 validtity: 0 868 | ...IPT: 2, pid: 1 vpn: 0 last_used: 76 validtity: 0 869 | ...IPT: 3, pid: 1 vpn: 9 last_used: 73 validtity: 0 870 | 871 | // The first invalid entry is automatically chosen to be replaced by LRU, regardless of how old it is, ie: 872 | 873 | 1. 874 | ...IPT: 0, pid: 0 vpn: 0 last_used: 109 validtity: 1 875 | ...IPT: 1, pid: 1 vpn: 26 last_used: 66 validtity: 0 876 | ...IPT: 2, pid: 1 vpn: 0 last_used: 76 validtity: 0 877 | ...IPT: 3, pid: 1 vpn: 9 last_used: 73 validtity: 0 878 | 879 | 2. 880 | ...IPT: 0, pid: 0 vpn: 0 last_used: 109 validtity: 1 881 | ...IPT: 1, pid: 0 vpn: 9 last_used: 111 validtity: 1 882 | ...IPT: 2, pid: 1 vpn: 0 last_used: 76 validtity: 0 883 | ...IPT: 3, pid: 1 vpn: 9 last_used: 73 validtity: 0 884 | 885 | 3. 886 | ...IPT: 0, pid: 0 vpn: 0 last_used: 109 validtity: 1 887 | ...IPT: 1, pid: 0 vpn: 9 last_used: 111 validtity: 1 888 | ...IPT: 2, pid: 0 vpn: 10 last_used: 113 validtity: 1 889 | ...IPT: 3, pid: 1 vpn: 9 last_used: 73 validtity: 0 890 | 891 | 4. 892 | ...IPT: 0, pid: 0 vpn: 0 last_used: 109 validtity: 1 893 | ...IPT: 1, pid: 0 vpn: 9 last_used: 111 validtity: 1 894 | ...IPT: 2, pid: 0 vpn: 10 last_used: 114 validtity: 1 895 | ...IPT: 3, pid: 0 vpn: 26 last_used: 114 validtity: 1 896 | 897 | 898 | 899 | 900 | 901 | //CASE 1: If IPT's pids were 0 and the paging in PID suddenly changes to a new PID (ie 1), all TLB entries immediately are made invalid.: 902 | 903 | Translate 0x0, read: *** no valid TLB entry found for this virtual page 0! 904 | Exception: page fault/no TLB entry 905 | ...IPT: 0, pid: 0 vpn: 0 last_used: 49 validtity: 1 906 | ...IPT: 1, pid: 0 vpn: 9 last_used: 46 validtity: 1 907 | ...IPT: 2, pid: 0 vpn: 10 last_used: 28 validtity: 1 908 | ...IPT: 3, pid: 0 vpn: 26 last_used: 44 validtity: 1 909 | .....IPT w matching pid and vpn missing! 910 | paging in: pid 1, phyPage 2, vpn 0 <-- paging in PID now 1, a new value 911 | TLB pre-update: 0 912 | ...TLB 0, vpn, phy & valid: 9 1 0 <-- All TLB entries set to invalid 913 | ...TLB 1, vpn, phy & valid: 26 3 0 914 | ...TLB 2, vpn, phy & valid: 0 0 0 915 | The corresponding TLBentry for Page 0 in TLB is 0 916 | 917 | //CASE 2: If IPT's pids are now 1 and the paging in PID suddenly reverts back to the old PID (ie: 0), all TLB and IPT entries are immediately made invalid. 918 | 919 | Translate 0x48, read: *** no valid TLB entry found for this virtual page 0! 920 | Exception: page fault/no TLB entry 921 | ...IPT: 0, pid: 1 vpn: 1 last_used: 71 validtity: 0 <-- ALL IPT entries set to invalid 922 | ...IPT: 1, pid: 1 vpn: 26 last_used: 66 validtity: 0 923 | ...IPT: 2, pid: 1 vpn: 0 last_used: 76 validtity: 0 924 | ...IPT: 3, pid: 1 vpn: 9 last_used: 73 validtity: 0 925 | .....IPT w matching pid and vpn missing! 926 | paging in: pid 0, phyPage 0, vpn 0 <-- paging in PID now 0, the old value! 927 | TLB pre-update: 0 928 | ...TLB 0, vpn, phy & valid: 1 0 0 <-- ALL TLB entries set to invalid 929 | ...TLB 1, vpn, phy & valid: 0 2 0 930 | ...TLB 2, vpn, phy & valid: 26 1 0 931 | The corresponding TLBentry for Page 0 in TLB is 0 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | //under the file translate.cc, there is a function called Machine::Translate(int virtAddr, int* physAddr, int size, bool writing) 941 | // the 'writing' parameter essentially decides wether or not a page is dirty and that a pageout will occur. 942 | 943 | DEBUG('a', "\tTranslate 0x%x, %s: ", virtAddr, writing ? "write" : "read"); <-- section of the function that prints out wether a translation vpn is to be considered 944 | 945 | // in the output text, it is reflected like this: 946 | 947 | Translate 0xd6c, write: *** no valid TLB entry found for this virtual page 26! <-- this tells us vpn 26 is being written to and is likely to be paged out at some point 948 | 949 | // versus the normal text when it is not dirty: 950 | 951 | Translate 0xc0, read: *** no valid TLB entry found for this virtual page 1! <-- signifies this page will not be paged out. 952 | 953 | 954 | 955 | 956 |          _____.,.へ. 957 |     _,.-‐''''"´ / @ \_. 958 |   ,.'"  r___,.rニ'ー'´ ̄`ヽ!ヽ、 959 |   !_,.rソ´ i ____イ   `i ,`'ート、_ 960 |  r' i,イ /  ハ__/_、ハ  /__ハ  ',7」 961 |   Y´ /!/レイ     レ'!,ィ、 !/|ハソ 962 |   ,'  ,'  | " ̄`    -、!ハ Y´  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA。 963 | . /   / i ハ   ____` "i ハ! 964 | く  イ ./ | |>、  ` ´  人 ハ 965 | ノ^ー'!/,イ-ハト、.!`=ーr<´!.ハヽ! 966 | ^Y ,イ^ヽ、ゝ  \「7/`ヽ!Vヽ! 967 |  レ'ソ    Lヽ、  〈ハ〉  〉、   /7 ̄ ̄ ̄ ̄ ̄ ̄ ̄7 968 |  「`ー'^ー'^7 `ヽ/§ヽ!/ヽ .// ____________________ ./ 969 | .イ`ー--‐イ、_,.-r!、.§ 〉 /`// /  PC.冥界   // 970 | 7ヽr、__r'"´ / // '"´`ヽ、  .//  ̄ ̄ ̄ ̄ ̄ ̄ / 971 | i   イ   |  | |、_ ,、 ! .ハ //          / 972 | ヽ、     i  | ト=='===='i二二二二二二二二i --------------------------------------------------------------------------------