 All right good afternoon time for the quiz to review, so we should be well prepared for this. This shouldn't be too bad So let's start off with the hardest question So here's a forking question Everyone's favorite as season lies turning So let's consider the following code. It's valid to ask you to read code. So you have main fork fork fork and And then prints off at what gets returned from every single fork So some questions might be some are too long for the quiz So this should be actually harder than the quiz. So what's a reasonable set of outputs assuming the initial process gets process ID 2 So that's the first process that executes from your terminal or whatever and then some other questions might be Well, how many processes are there? What order of the prints is there a specific order of the prints and what relationships do the processes have? So anyone want to answer any of these questions like maybe how many processes are there at the end of this or total seven seven eight eight That was quick. Yeah eight. So and what would all the relationships be? Complicated. Yeah, well, let's go through it because this is gonna be harder that probably Harder than anything that would be on the quiz So let's answer it in painstaking detail. Where's my button? All right, so we have this Fork fork fork and then print with I'm not gonna write the print so Initially, we'll have process ID. Let's call it to So to would be set off to execute main will ignore, you know, setting up the C standard library and all the other garbage So process to would come in and call fork So let's start drawing our tree So we would have let's put it smack dab in the middle process ID to So it comes in calls fork. It would create a Yeah, new child new process. However, let's give it a number so we can keep track of it So let's assume it just gets process ID three So right after the fork Let's move this So right after the fork zeros or one No, PID is just a number Yeah, so fork will return the only difference is the return value the child process is going to get return value of zero and the parents Going to get the process ID of the child So in this case right after the call to fork we create a new process It would be process. Just give it a number. It'd probably be process three So if we have PID three Then in PID two, what's the value of first? three it just created a child we just said it was PID three for whatever reason and I'll draw a line that indicates parent-child relationship. So in process ID two For it gets returned a three. So that's what first is and then PID three. What is first? Okay, cool. We're on the right track. So now they're both right after the call Right after the first fork So can we say anything about the order that the processes get executed in? Nope, so up to the scheduler. Let's for argument's sake just keep on going with process two So process two because they're all independent would call fork again Whoops Process two calls fork again. Oh geez Too big of an eraser So process two calls fork again, and now it is right after The call to second and it would have created another child. So it would have created PID Say for Which would have been an exact clone of it at the time of the fork So if it's an exact clone of it at the time of the fork, what is first equal to? Three because it was three before the fork. So after the fork, it's going to be three It just cloned it. It's an exact copy. So First stays the same. It's three and then what would second be in PID four? Zero because this one was just created from that called a fork in PID two. What would second be? Four because that was the child and then we'd have here. I'll move it down a tad so that would be another child Okay, well, we can't say anything about the order again. We have like three processes on the go. So let's just wrap up Let's just wrap up process two So I will clean this up So four is right here And so is two So if two calls fork again, it would create another child It would probably be PID five What is the value of first for PID five? Three why copy of the parent, okay? Well, what's the value of second in PID five? Four why because it's still a copy of this parent and that existed before What is the value of third? Zero because it's the new child in PID two. What's the value of third? Five, okay, cool. So then process ID two it's done It could go ahead and call that print. We don't know what order it's gonna be. So let's just get out of the way So it would print first equals three second equals four third Equals five and this would be from PID two So just put that there so we know where it came from And then PID five could also print could also reach the print statement first would be three second Would be four and third Would be zero so we can give that a little check mark and now we can delete two So now we're assuming two is done executing Okay, well Let's assume the one that's furthest through and easy We just want to clear up space and get rid of them quick So it's probably easier to argue about what happens to process ID four So process ID four is about to call fork again and set third. So if it calls fork it will Create another child which would be the direct child of process four because it just called fork so process four Would spawn Another process probably process PID six whoops PID six what would the value of first be? Three what would the value of second be? Zero what would the value of third be? Okay, and process ID four that just created it. What would the value of third be? Six cool, so now they're both done we could print them off first equals third second Equals zero third Equals six. That's PID four taken care of Then for PID six it would print off first equals three second Equals zero third Equals zero Okay, so They're all done. We can stop arguing about those So now we have to argue about PID three So PID three has only just called the first fork and about to call the second fork So as soon as it calls the second fork again creates a new child It would be what we're on PID seven now What would first be equal to zero what would second be equal to? Zero it's the new created child in seven and then what would second be equal to in process ID three Seven because we just created it Okay, so at this point now we have Three here and then seven here So let's just argue about three first So three would create a new child it would probably be PID eight What would first be zero what would second be? Seven what would third be? Zero and in PID three what would third be? Eight so that's the process. I just created so now I can go ahead and finish these So three would print first equals zero second equals seven third Equals eight and then eight would go ahead and print first Equals zero second Equals seven third Equals zero so that would be check So now we just have PID seven to worry about Where's my race? So we just have process ID seven to worry about that's just going to call fork again We've done this like how many times we've done it like seven times now one more time shouldn't hurt So seven calls fork which creates probably PID nine So what is first zero and PID nine what is second? What is third? Zero and in PID seven what is third? Seven Okay, nine. All right, so let's check that off. So seven would print first equals zero second equals zero third equals nine and Then nine would print off First equals zero second equals zero third Equals zero and would these specifically be in this order? No, they could be in any old order if I wanted to print these in like somewhat specific order What could I do? Sorry uses Center for I Could but I'd have to share them between processes which we don't know how to do yet Yeah wait I could wait so If you want to throw a wrench in this question, you could say hey Is there any order if there's a wait? Which of course there would be you would wait for your parent or wait for your child to exit so the child would exit first Yeah, oh PID seven. Oh, sorry second Sorry, that is correct. Sorry second should be seven in this case Or wait, no, sorry. No, it's zero. Yeah, it's zero because it was a child. So it was created from the second call Yeah, I we did it right the first time. Yeah Yeah, so the PIDs would also be random But their relationships would be the same the tree would still look the same So it still look like this no matter what but the values are going to change But they're gonna change in a way that makes sense Where all the relationships are still the same Sorry Yeah, so three spawn seven and eight on with that arrows So three has two children three gets created after the first fork. Yes Which one's seven? Yeah, so eight was from the third fork Yeah, so the original process has three direct children The first process that that process created has two direct children and then one of them has one child and then one of the other child of The original parent has one children or one child Does that that makes sense to everyone? So we count eight We are in no particular order If we wanted to make this a Quiz question we could throw in a wait and say what happens and probably give you less forks Yeah, yeah, so that's a good question. We can just do that too So where's the hell's might be in so what about if I do something also? What about if I do something silly like this? Wait, wait, wait, sorry the final child the last child gets produced first Kind of well, so if we have three weights here That means every process after it's all the forks are done. They all call wait three times By default weight just waits on the first child to exit. So, oh cool free nice Where's my tablet So if we have this and all all the processes call wait three times How many processes actually have three children? One so we're gonna have a bunch of extra weight calls Are you? Yeah, so that's the good question is if you call weight and you don't have a child. Well, what happens? Well thankfully They wrote out what happens So if you just scroll to error, they give you a few errors so weight can fail if This is a non-blocking call and the process isn't terminated yet. It will say it's not terminated It will give you an error if the calling process does not have any Unweighted children so that means you don't have any children. So you just get an error and You'll also get an error if That process does not exist or it's not a child So you can't wait on a knit or something silly like that You can only wait on your direct children in some like old unixes You could wait on your grandchildren, but they quickly decided that was a horrible idea and stop doing that So only direct children Then you have then you essentially you'll have races with weights Right if two processes are trying to wait on one It's like what the first one that waits on it gets to see its value and the other one gets an error And then you're synchronizing between processes and Yeah Make our lives easier. So in this case that code would actually work except for The first process that actually has three children it would actually wait three times in return for the other ones It would just air so Waits like a C function that returns it returns the process ID that Finished zero if there's still children and it's non-blocking and negative one if there's an error Yeah, I'd be returned negative one each time and then set error no So for the first parent, you know the first In this process ID to it actually wait on all them. So it would definitely go last So there you'd get some type of order here So it would definitely go last and like process ID three that has two children It would make sure that seven and eight print before it, but Sorry Well seven eight directly and then nine because it's a child of seven But the third wait call would return an error because it's our children at that point But you would get some type of you'd get some type of order if we put in wait wait wait Okay, any questions about that? So we pretty much answered everything Good reasonable output not too bad So fork sucks the first time you saw it, but we've seen it like few lecturers now, so it shouldn't be too bad All right. Well, let's graduate and do a threading question so this is Saying if you have a global variable in I and three threads before he executes that about the value of I is zero and then You three threads so two are going to execute I plus plus one executes I minus minus and then the question is What are all the possible values of I after all the threads execute? So I'll give you a second to think about that and then we can go over it Yeah, it's just normal. No. Yeah unsigned it. No sign them. Sorry. It's a sign sign. Yeah Yeah, it's just a normal signed integer. I mean if it couldn't have negative one, it would be max Nope, so this doesn't have any lock. So I guess first question is is there a data race? Very very much so every single thread is reading and writing. I really like the fact that they're excavating beside us Makes things interesting all right, so Is it possible that I is one at the end of this? Yeah, which is what you would expect if you just had a single thread so Zero to one two then down to one could it be two at the end of this Could it be zero at the end of this could be negative one at the end of this? Yes, you don't know the order Okay, so we think it can be negative two Can't be three Okay, so I guess Yeah, so question might be well, how do I argue about so you're wondering how it is to so let's go over it So each thread will have these kind of three instructions read from memory Then increment or dick decrement and then write to memory So if initially I is zero Let's see someone give me an interleaving of threads that gives me the value two at the end of this So tell me how I could get a two out of this Everyone agreed that I could get it to so I hear Thread one starts Read it would read a zero That would ink it To a one now just write it. Okay, so I write it out Now I is equal to one Thread three reads it so it would read a One, I don't know so someone has to tell me what to do thread two reads okay, so So this is our alternate timelines, so thread two reads So it would read a one actually those Screw it. I'll just delete it. So we said thread two reads instead. Okay, so thread two reads the one Yeah, so we didn't actually need to do that doesn't at this point It doesn't matter which one reads first they're both going to read the same value doesn't really matter what one read sports Yeah, so at this point the only difference is who actually writes first So thread three would decrement So it would go to zero. So if I want to at the end of this Should thread three continue to execute? Yeah, so it would write out Zero so now I is equal to zero But now thread two is the only thread that has any instructions left it read a one So it would increment it to a two and then it would write Out I and now I is equal to two when we're done and then we can We also have the case here We can see how we would get zero at the end of this So we would get zero at the end of this if we just kind of Flip these around so if thread three came back later and wrote So if it looked like this then we can get zero So it just depends on the order they wrote and so The idea of this is they wrote they just read some still data All right Well, I guess for practice so that the most difficult scenario might be How do I get a negative one? Thread three reads I which is a zero, okay? Okay, I can Just deck Yeah, so now thread three wants to write out a negative one and then thread one can go ahead and execute read That zero it still hasn't been updated. It could ink which now is a one Then it would write out the one so now The global I is equal to one thread two could come along read one Ink it So now it's a two and it would write two No, I equals two But thread three stills. It's right left to do so it would write negative one and now I equals negative one at the end of this So it's just a whole bunch of different options But no matter what each thread individually Executes an order. You just don't know anything between them That's the only kind of challenge here Yeah Any questions about that? Yeah, start difference between the morning class. We're actually awake now. This is great Yeah, we had zero and just switch the order the rights at the end of the two example and then to get the value one You just execute one thread till it's done another thread till it's done And then the third threads till it's done and that's kind of what you would expect anyways All right. Any questions on that? We're good. We identified our data race We came up with a bunch of different answers cough All right, cool No questions at all about threads Monders are just vutexes that are made for you. They're kindly condition variables are decent So let's go Okay, well, let's whirlwind through everything and go back and I guess maybe visit condition variables or anything else So let's essentially I'll just go through the summary of all of the lectures and then point out what's important So Unix system calls they have a parent-child relationship. That's good to know Creating new processes with fork. I mean we just spent a good time going over a fork example there will I Mean if you're betting, there's probably a fork a fork question on the quiz And then after the fork both processes are exactly the same only differences of PID is different And we went over a whole bunch of different scenarios where we actually created eight processes Well, we had one process. So we created seven so That's the only difference. That's with fork. It's an exact copy Hopefully we've seen it enough time by now or that it makes sense the first time it probably made no sense Hopefully now it makes a bit more sense And then of course we have the scheduler that we can't say the order anything runs in and that's been true the entire time So you're responsible for managing those processes So the operating system makes sure that there's that may strict parent-child relationship So we saw zombie processes that are processes that have exited but no one's waited on them yet So they just have resources chilling around that the kernel can't delete yet And then we saw orphan processes because a parent process can exit before a child there's no way to kind of restrict that and With an orphan process. It'll just get reparented Likely to a knit and that will then clean it up So we kind of know what a knit does it just sits there and waits over and over and over and over again to claim any Children and clean them up So for quiz two they other section one doesn't actually know that much about zombie processes and orphan processes But they have encountered weight. So weight just waits for The first child to actually exit by default or you could wait a specific child And that will kind of ensure some order because you're waiting for why your children to die Any questions about weight or any of that? Yep Yeah, if you call weight with and without a specific PID, it's the first child that dies Which is annoying because for thread join there's no like equivalent of that too because for thread join We have to wait on a specific thread Yeah, then it's an error. Well, there's nothing to block on it's just an error Yeah, he'll just return an error and if you ignore it you ignore it. It's like programming in Java, right? You just ignore the error and carry on and bad things happen Yeah, so if you are a process Well, you can only wait on your direct children. So if you create them, you should know how many you have So then you should wait the appropriate number of times if you're a knit You don't know how many children you have because you keep adopting them. So that's why a knit is Basically, it doesn't just do wait until there's an error. It would just do while true weight well, but It doesn't have any data races or anything because the current programmers are good That's not like atomic. Yeah a thread because that was like p thread join, which was kind of like wait, but for threads Yeah Yeah, just it's there annoying a bi All right any more questions about weight Okay, so we saw it like IPC Like read and write Redirecting file descriptors, which was cool signals, which was really cool and that you encountered in lab 3 And signals were just like interrupts This really isn't covered on the quiz because the other sections haven't played around with file descriptors and They haven't had as much fun as us so Don't have to worry about this there might be like a little bit about signals But honestly wouldn't worry about this because section one didn't really do it Then we talked about threads. Oh God threat threads are the best so we saw threads Really relate them to processes They're lighter weight Because they just share memory by default. They're all in the same address space as the process So all the memory is shared the only thing that is private to you are your registers and your stack Yep. Yeah, so you don't even have to exact for that So what a process gets created as Part of like the executable file format It'll say how much memory it initially needs and then the kernel will just set the site for that process and make sure it's Available when it's executed and if it doesn't have enough memory, it's going to go not launching that out of memory and Then while the process is running their system calls like that BRK thing we saw that Essentially just extends the heap you just essentially just ask the kernel for memory For the threading example last time we ran out of memory. That's whenever we had too many bank accounts and then just Then the kernel was like malox, okay, but then when we tried to Axe when we tried to actually read or write memory that was past our limit It just killed the process straight up Well, we were accessing within our virtual memory, but we didn't have enough physical memory on the machine So the current was just like you have by but the allocation was fine maloc was Returned success and gave us a memory address Yeah, but maloc just uses the kernel system calls and if the kernel lies to it, what can it do? And we'll see virtual memory after this And yeah, whatever you start a process to it just has a single thread. That's it single kernel thread Yeah, we should know a bit about exec Vee. It just transforms the process That's it doesn't create a new process or do anything special just transforms it Reinitializes it loads everything Why why might it might have to reinitialize everything if The only way to create a new process is a fork and then it would exec to start a new process And when guess why it would probably want to reinitialize the memory and everything like that Yes, but that would reinitialize the registers not the memory so imagine if you had you know your bank account open and Some or something like that and then that forked an exec Well, you probably don't want that process to be able to read your memory. So Probably want to just wipe that out and yeah to make sure everything's independent What if you don't want so exec will clear everything and reinitialize everything for you Okay, any other questions Okay So we saw there's both processes and curl threads for parallelization We kind of talked about the relationship between kernel threads to user threads For the purpose so your lab was all user threads P threads were all kernel threads and really don't worry about anything in between The major thing here was that now we have Threads all in the same address space. So we have synchronization issues, which are everyone's favorite so We saw one easy way to get rid of data races was have a critical section where we had a mutex or spin lock that ensures mutual exclusion so only one thread can execute that critical section at a time and That prevents a deadlock because a deadlock needs two concurrent accesses with one being a right So if we only have one thread in one block of code at any given time There's no data races because there's only one thread accessing it So we saw that you don't really have to worry about how to implement the locks The quizzes are more how to use them because the labs were how to implement them And Read write lock the other section does not know so don't worry about read write lock, but it's pretty much just for performance reasons Then we saw a center fours so we saw a mutual exclusion with mutexes and the center fours Which were just a value and then you can increment it using post Yeah, it's Called post you can decrement it using weight and it will just block if that value zero and wait for it to Increase to at least one so it can decrement not go below zero So it will just work like that you can use them to ensure some type of order But this is mostly ordering you can use a center four as a lock like we kind of saw but You might use them for ordering and in which case there's still potential for data races Which you still have to solve and of course you can use mutexes So then we saw more advanced locking like the condition variables They were kind of clear for more complex condition sign signaling We saw like locking granularity in that paralyzation example like how much code our lock was blocking and how many locks we had affected how fast it ran and When we had less account there is more locks getting away of each other and was slower But again, you must prevent dead locks if you have multiple locks going on and be able to identify Cases where dead locks could happen Okay, so let's quickly so probably the hardest thing from that was That producer consumer example so Let's read it again so Ignore this center four because it's just keeping track of how many things get produced and consumed So if we have something like this does everyone kind of understand why this is written as it's written So remember this was like a producer consumer where we had a buffer with a certain amount of slots And we didn't want to miss any data So if every slot was filled we wanted to stop our producers from producing any more data Until a consumer came along and freed up at least one slot Yep, yeah, so p-thread condition weight essentially will atomically put yourself in a queue called has filled and Put yourself to sleep until someone signals you or wakes you up with a broadcast so you so the Lock has to be Held when you call weight and whenever it returns from weight. It also still has the lock Yeah walls blocking so atomically it would add itself to the queue and give up the lock and then block itself and Then later when something signals it before it returns from the weight it would grab the lock again Which is important so in this case if N is so if all the slots are filled it puts itself in a queue that's essentially waiting for at least one filled slot and Then doing this over and over again. Why would it want to do it over and over again? Yeah, so Another thread when I woke up it's going to grab the lock again if something, you know if something What am I waiting for yeah, if something filled a slot and then signaled me by the time I wake up Does that mean that the slot still filled? No, so I might wake up from the condition variable and That condition might not be true anymore. It might have been true at the time of the signal was sent But by the time I wake up That doesn't mean it what it's still true So if I replace this while with like an if or something and didn't check it again I might be screwed and Outside of the scope of this course for some operating systems weight just might like randomly return to you But assume for the purposes of this course weight just doesn't randomly return to you But with some so so if I change it to this something very bad could happen Right. All right any other questions? Yeah When it's woken up it like removes itself from the queue and Then grabs the lock again No, because it has to grab the lock again before it returns and you don't know when that's gonna happen Yeah, this is essentially this critical section and That like Q also has to be protected and it would be in a critical section Yeah Yeah, and it would be in a critical section too because it's waiting on some condition and you don't want data races on your condition either So you're gonna have a critical section anyway, so it may as well take advantage of it. Yeah, you implemented one, right? So turning off interrupts only works if that's the only source of it It would probably use something like an internal lock like how we implement the blocking mutex Probably do something similar to that Because you're allowed to just turn off interrupts and that essentially acts as turning off interrupts for lab 3 Essentially is a lock and then enabling them again is unlock Yeah, that only works for single core and if interrupts are your only source of concurrency so in the hardware Like a real interrupt you have no control over when it comes in and like if that went to your process and Messed up your process and you have no control over it if you're the currently you can do that though But that also might just be too slow if you just disable interrupts globally That might not be a great idea Right, so We'll go over it quickly. So Ash, and I will be on discord. I asked also have office hours after this Although forewarned if you follow me back to my office. I need to help flipping my desk back over Yeah, long story So Ash and I will be on discord during it if you want a direct message us again Discord will be muted. No talking to each other blah blah blah We'll announce anything problems there are but hopefully there will be none There's gonna be two true false two multiple choice which by the looks of the time commitment aren't really multiple choice And two short answer that are more slaves like free form so Yeah, which means they're not really multiple choice so What like one minute each for true false and then ten minutes each for the multiple choice so You can probably guess what they are given today's review And There probably won't be any issues because you will have like 16 different options So one of them is gonna be right. Yeah It's a natural multiple choice But there's 16 choices What? Yeah, we have to battle that one comes but we didn't do multiple answer because the corkis Grading scheme is so stupid with it that like it varies wildly depending on how many right answers there are So there's like two right answers and it's worth four marks Each of those are worth two points and then if you select the wrong one It's negative two so you pretty much get zero or full marks anyways So if you choose one right one wrong you get zero Yeah, so read so it's probably something like a data race or something like that So read it carefully and reason about it. You have a lot of time to go back over it All right, any other questions? Yeah, I had to fix it. I Flipped it myself. It was intentional. All right. Well, remember I'm pulling for you