 Good morning. Good morning. All right, so it's Wednesday. The midterm is Friday, OK? Canceled? No. I mean, you're welcome to act as if it is canceled. And in that case, your grade on the midterm will be canceled. So OK, so what I thought we would do today is I wanted to take some time to review. I saw this practice in Sam yesterday. So just let me do some course notes. I don't have any slides today. We're just going to go through the exam again. You guys are welcome to ask any questions you want to about the material, right? So we cover a couple of things. Yeah, just throw him off you if he does that, or kick him or whatever he needs. He's feeling a little randy this morning. OK, so a couple of course notes. So I think finally we have an assignment one solution that makes sense. So if you guys downloaded it previously, please download it again. Thanks for pointing this out in the forums. Actually, the forums have been really helpful. I went on there a couple of days ago and I wonder if there's anything interesting happening. And someone had pointed out that solutions that was wrong. So that was awesome to find out. I mean, it wasn't awesome to find out, but it was nice to find out. So anyway, the link now to the solution that should work, the code that you get should work, and it solves some problems that people have noted in the forum. So please, I would suggest everybody take a look at that code, even if you're going to use your own code. Just download it. Look at sync.c side by side with yours. Make sure things look kosher. It might do some error checking that you don't do, which might be good to emulate. Or there might be a corner case that our tests haven't hit yet that it fixes. It's not that much code to look at. Really, again, the things that you should have working, if you want to sign it to to work, are locks and condition variables. As long as you didn't break our semaphores. If you didn't change your semaphores, those are the two things to focus on. So today, because of the class, the way I want to do things today, can anybody who is sitting somewhere where they can't easily talk to somebody else, like you, you, you, that's it. Can you guys move closer in? Because I want you guys to spend some time discussing the exam problems with somebody today as part of class. So if you're sitting by yourself, find somewhere where you can kind of join a group. So as far as the midterm, so the midterm will cover. So essentially, again, the practice midterm I sent out is identical in format to the actual midterm. Except for the fact that I didn't write any multiple choice questions. The multiple choice questions on the midterm are drawn exactly from the slides, right? Like here is how I created the questions. I went through the slide decks for the class and I found things that were on the slides and I use them to create multiple choice questions. And usually, the multiple choice questions borrow like the same language from the slide, like they're identical. So if you know the material on the slides, the multiple choice questions are designed to be easy, right? They're really not designed to be tricky or hard. They're not complicated, right? The short answer questions are also designed to test your understanding of material that we've gone over in class, right? So the short answer questions will ask you to do things or to demonstrate some knowledge in English, right? By writing down a couple of sentences. But those are also sort of drawn directly from the material. The long answer question is going to ask you guys to think a little bit, right? So it's going to ask you to apply some of the principles that we've learned in class. Nice suit, by the way, look at the chart. Hope you dressed up for the midterm, too. So that's going to ask you to synthesize some knowledge, right? So this isn't just a regurgitation exam, right? The multiple choice questions are highly regurgitative. The short answer questions are mildly regurgitative and the long answer question is designed to not really be regurgitative, right? It's designed to ask you guys to apply some of the design principles we've talked about over and over again in class, right? But what I'm hoping is that there's not too much stuff on these exams that's really highly mechanical, right? So there's one question on the practice exam that's a little bit mechanical. Maybe that will give you guys a little chance to do something easy, but most of it is designed to get you thinking, right? So what I thought we would do today is quite simple. So I had the exam, again, the point values on the exam are designed to estimate the amount of time that you should allocate for each problem, right? So there will be six short answer questions of which you are expected to answer four, right? You will get a maximum of 20 points for that section. There are two options for the long answer, each one is worth 20 points, right? You pick four questions from the six. If you answer more, since they're short, we will grade the extra ones and we'll give you the sum total of your best four scores. On the long answer question, you know, we're not gonna grade both. So if you decide to answer both, that's five, but we will simply pick probably the first one on the exam and grade them, right? All right, any questions about the format? All right, so I learned, some of you guys are in Steve's class, I learned this new word recently, it's called invigilation. I'm not even sure that, I don't know, I've never heard that word before, okay? But let me make it clear how the exam on Friday is going to work, right? I'm gonna hand out the exam, it's a 50 minute exam, all right? I'm gonna hand it out at nine a.m., right? Nine o'clock on the dot. And at 9.50, if your exam isn't in the box outside the classroom, your score is gonna start to fall rapidly, right? So I think what I'll do is I'm just gonna deduct five points for every one minute that your exam is late, okay? Given that you probably are gonna earn about one point per minute, if you do the exam well, my suggestion is don't take that penalty, okay? It's your, I'm gonna put a clock in here, right? It could even have seconds if you guys want. I'm happy to set it a minute ahead or something. But there's no other way to do this. When I did the preterm exam, there were people in this room who I had to literally grab the exam out of their hand, right? I'm not playing that game on the midterm, right? If it's not in the box, you're gonna start to lose points, right? It's very simple. And if you show up late, tough, okay? So some of you guys are still drifting in today. Whatever you did today to get to class at whatever it is, nine, 10, you need to do that minus 20 minutes on Friday if you wanna be here at nine o'clock, right? Because that's when the exam will start and I don't care when you get here, right? If you come in at 9.30, you know, I hope that you can answer the questions rapidly, right? There's a small group of you that are gonna take the exam tomorrow. That exam is also gonna be given at nine a.m. You know, it just has to be at some time. I will email you if you are taking the exam tomorrow because it will be gone Friday. I think most of you guys have contacted us already. If you need to take the exam tomorrow for some reason, please email the core staff. I will contact the people who I think are taking the exam tomorrow and tell them where the exam will be, right? That's gonna be the exactly same format. It's a different exam, right? So, you know, I mean, there's no real advantage to taking it tomorrow, right? There's no advantage to taking it Friday either, right? I would like people to take it Friday, all right? Any questions about formatting, coverage, et cetera, et cetera? The material covered on the exam is essentially up through Monday. I don't think that there's a question about page replacement algorithms, but there are questions that relate to swapping, right? So, swapping is something we kind of covered last Friday and then reviewed a little bit on Monday, right? So, I would look at the notes all the way through Monday, right? But, again, as far as I know, there's nothing on page replacement, although I can look and make sure. All right, so here's what I thought we would do today. Again, there's, I don't know if we'll get through the whole exam, right? But what I thought I would do is, why don't we, how many people have looked at the practice exam already? All right, cool. So, why don't we do this? I'm just gonna put up a question on the board, and I don't know what the best way to do this is. Maybe I should play like a minute worth of classical music to stimulate your brains. But maybe we'll just have a minute for you guys to look at it, and then I'll give you guys a chance to discuss, you know what, screw that. Let's just discuss amongst yourselves, right? So, why don't I put up a question, and I'm gonna give you guys a minute or two to talk about it in small groups, and then we'll go through the question together, okay? So, the first question on the short answer, again, there's no multiple choice on this exam. So, explain the trade-offs inherent to the scheduling quantum length. What happens when it gets very short? What happens when it gets very long? So, the short answer questions on the exam, you know, I gave you guys a lot of room on the practice exam, I'm gonna do that on the midterm as well, just in case you guys like to light really big or something, but these are designed to be answerable in three or four sentences, right? So, if you find yourself writing pages and pages, then either you're probably overthinking the question, or you're overthinking your answer, right? So, okay, so, five points for this question, first question, I'll give you guys, I don't know, two minutes, discuss amongst yourselves in sort of local groups, which involves talking, which means the room should get loud. Keep talking. Brain music. Yeah, I know, it's like being in an apartment store. Yeah. What do you think? Come here. What do you think? Come here. Hey, hey, you're my kiss, you're my kiss, thanks. All right, 30 more seconds. You got a little extra time on this one, so you're probably done with 30 more seconds to think about it. All right, 10, nine, eight, seven, six, five, four, three, two. All right, who wants to take a stab at the question? What's a trade-off inherent scheduling quantum length? Okay, how about this? Who wants to talk about what happens when it gets really short, right? Context with overhead means what? Efficiency is a little why. No, tell me why, though. I mean, you guys are on the right track, you've got the right, you mentioned some of the right things, right? But what are the two things that are competing for time on the CPU here, right? One is the actual time it takes to schedule a process, and then I let it run, right? So if it takes, let's say it takes 10 time units to schedule a process and I only let it run for one time unit, right? So now essentially, or let's say it takes nine time units. So 90% of the time on the CPU is being spent at schedule and in only 10 of it is being spent on actual work, right? Okay, so what about the other direction, right? This sounds terrible. I mean, why not just give processes these massive long quanta, right? That way I can, I mean, that's clearly the way to minimize the overhead of scheduling, right? To make the quantum longer and longer. Who wants to tell me what happens when that happens? So why will responsiveness suffer? That's true, that's correct, but why? Because what's the thread that I'm worried about here? What's the thread that's gonna cause responsiveness to suffer? What's that? Well, no, the thread that yields very quickly is not the problem here, right? What causes the problem, doubt you. I'd say kernel or something clicks on a word. Kernel is not gonna, if it's too long, then it's gonna take too long for the user to actually get the program running. Right, so what kind of thread is it that's blocking the thread that I want to run? Ben, it's a CPU bound thread, right? If threads always yield before their time quantum is up, then I don't necessarily have this problem. But some threads are doing something that's CPU intensive and are never actually going to call yield or execute a system call, and they're just gonna sit there, churning out digits of pi or whatever, until they hit the edge of their quantum, right? So that's the thread I'm worried about. On a system with no CPU bound threads, longer time quantum might make a lot of sense, right? But the thread that causes this problem is the thread that runs to the end of a time quantum, it blocks, right? So okay, so let me, you know, we've done one question, I'm gonna move on to the next one, but let me just make some observations. So you guys are really good at telling me what's gonna happen, right? But on the exam, I want you to tell me why, right? Like tell me, you know, come up with an example, come up with a scenario, right? In which one of these things is bad, right? So with a short time quantum, you might, you know, you wanna say, well, you know, because context which overhead is fixed and the scheduling overhead might be fixed, if I reduce the time quantum, then clearly the ratio of time spent scheduling and time spent actually doing useful work starts to get low, right? And vice versa with the other scenario, right? But the CPU bound thread is the problem, right? That's the thread that's gonna exhaust this quantum. All right, questions about this question before we go on? Okay, next short answer question. All right, this one's kind of mechanical, right? Okay, base and bounds. So I'm giving you a segmentation base and bounds tables for the currently running process and I'm giving you five pseudo assembly instructions. These are not actually MIPS, but you can tell what they are, their loads and stores. So, five of these, five minutes. Tell me what's gonna happen. I'll give you guys two minutes to think about it starting now, go. What are the two arguments? There, sorry, sorry, just that's a good, that's a good clarification. There are not two arguments, those are numbers. The comma is just showing you where the thousand's place is. Sorry, that is confusing, I should have fixed it. Also note that this problem uses decimal arithmetic, okay? That's intended to make it easier. So, let's go through the question. All right, we've got a segmentation table and as somebody pointed out very carefully, those commas are not there to indicate arguments, they're there to indicate the thousand's place. I'll make sure I clean this up for the actual exam. Okay, right, base time arithmetic, design to make things easy. Operation number one, a load from address 1200. What happens, anybody? Somebody raise their hand who wants to translate this instruction. Right back here, okay. Yeah, it will load from. 650, okay, how did you do that? Okay, right. Okay, exactly right, that will do a load from physical address 650. Did everyone understand how this happened? Can I go through that? Yeah, sure, exactly. First thing I do, right? What's the virtual address that's being used? It's virtual address 1200, right? First thing I need to do is I need to find if there's a segment that exists that contains this virtual address, right? So, the segment contains the virtual address, if the virtual address is between the base virtual address for that segment and the base plus the bound, right? The bound is the size, okay? So, in this case, I look at my segment table and I find out that there does exist such a segment, right? In fact, that segment is the first one in the table. It has base virtual address 1000 and bound 500, meaning that this map's virtual address is from 1000 to 1500, of which 1200 is in between there, right? So, now I verify that the address is valid, okay? That's step one, right? The next thing to make sure, right, is that the process has the permissions necessary on this segment to do the instruction that it's trying to do, right? So, the permissions that are up here for this segment are that it can be read from, right? And this is a load, so I'm fine, right? Now, I know that the segment exists and I can use the segment the way the instruction wants to. The third thing I need to do is actually translate the address, right? So, now it's just math, okay? I've got address 1200. I subtract off the starting virtual address. I get an offset from inside the segment of 200 and I add that to the base physical address to get 650. Yeah? In a question, it is also possible that two segments have overlapping regions? It is bad, actually it is possible, right? I thought about that and I didn't do anything that mean, right? But, yeah, so we didn't necessarily talk about that, but there's no reason that a segment couldn't overlap another segment, either in the virtual address space or in the physical address space. So, I could have two segments with different permissions, different parts of the virtual address that point to the same physical memory. I don't know why, but you could. All right, I don't want to do all of these because these are kind of dull, all right? But, let's do the second one, okay? This is a stored address, 140,000. Who wants to translate this address? Tell me what happens. Somebody on this side of the room. Yeah? Sure. If the first one did not have to read permission, then what would have happened? Well, we'll get, let's do one of those later, but let's do the second one, right? Stored address, 140,000, okay? What's the first thing I need to do? Check if there's a segment defined. So, is there a segment defined that contains this address? Yes or no? Yes, yes. Which one is it? The second one, right? This contains virtual addresses from 130,000 to 150,000. The bound is 20,000, okay? Now, this is a store. Does the process have permission to write to this segment? Yes, okay, so the address could proceed. That's nice, all right? Now, what happens? How do I translate this address? What's the first step? Two steps, math. Yep, I get my offset into my segment and that gives me what, Robert? 10,000 and then what do I do with that 10,000? Add it to my base, no, not to my bounds. Add it to my base physical address and that gives me what? 21,000, all right? Is it easier to do these in decimal? Because I can do them in hex on the exam. Okay, good. Because I've always felt, you know, I hated doing hex math on exams, right? Unfortunately, as a computer scientist, you probably should have been learning base 16 all along, right? If this was the matrix we would have identified you as future computer scientists at birth and we would have made sure that you were taught base 16 arithmetic as if it was completely natural, right? Most of us aren't, okay. Let's do one more. Let's do, let's do number five, all right? Store to address 1,000, right? What happens? Five. What's the fault? Why? Because the process does not have permissions to write to this segment. This address, the segment is in the segment table, right? But I don't have permissions to do a store. I only have permissions to do loads, all right? Very quickly, what happens to these other addresses? What about this guy? Boom, why? Because it's not a store. There's no segment for this, right? What about this guy? Will this guy succeed? No, why not? Out of bounds, right? This segment only covers 83,000 to 84,000 and this is going to 84,100. No, no. The first two do not, the last three do, right? The last three all raise exceptions. Now on a test, like the one you're gonna take Friday, I would want to know what, why, right? So it is not sufficient to answer this question by just telling me what, when I'm asking you, what would happen, right? A TLB exception is part of it, but I would also like you to tell me, this would raise, sorry, a segmentation violation in this case, there's no TLB, this is a segment table. So this address cannot be translated, or more precisely, this instruction cannot execute either because the address can't be translated, it's not in the segment table, or because the process is trying to perform an operation that's not allowed in this segment, or in one case, essentially this address boils down to the same problem. There really isn't a segment defined that contains this address. It looks like it might be in this one, but it's not, right? So really it's the same problem we had in this guy. This guy is almost clearly not in any segment, right? Because unless this one had a massive, massive bound, it wouldn't contain this guy. But this guy is, it really has the same problem, right? Despite the fact that there's something that looks like it might contain it, right? Okay, question's about this question, right? I don't like this kind of problem, right? There will be a problem like this on the exam, there might be two of them, but I think these problems are boring. Okay, maybe you think they're great, but I don't, so, and I get to write the exam. All right, so let's go on to the next problem, okay. All right, here we go. So this, I'll let you guys read this, but let me summarize quickly. One disadvantage of multi-level page tables is the page tables themselves can get big, right? Remember we pointed out, can you guys, can we have one conversation? Thanks, you guys have been talking for about 10 minutes now, it's irritating. All right, so the page tables themselves are fairly large, right? Remember my top and second level page table structures are 4K, assuming I have 4K pages on a 32-bit wide architecture, okay? So once possible solution here is for the kernel to actually swap the page tables themselves out to disk, right? So page tables are just memory, right? I can move memory to disk, okay? So what the question asks is, it points out that, yeah, I can do this and I can reduce the amount of memory and use, but why would this cause problems, right? And specifically, think about the process of translating an address in the kernel if some of the page table structures themselves are on disk, all right? I will give you guys the requisite two minutes and don't let the music lull you into a state of stupor. You need to talk about the question. Come here, doc. What's that? It is. It is? It's like just smiling. That always works. Hey. Ooh, no. No. Are you gonna help them cheat on the exam? You're gonna help them? Are you gonna give them answers? Go. You're like Sean. Hang on, Sean. Let's keep going. Anybody want to, what, okay. So what, what is the difficulty? Anybody come up with the difficulty they want to propose? Okay, disk is slow. That's a fact, right? So, but, but what does this mean in this particular case? Right? Okay, so it might cause more swapping. Why? Why would it cause more swapping? Right, but, okay. So, so think specifically about the process of translating a virtual address. All right, Robert? Right? If it's out on disk, you pull it there. Okay, but also what do I have to do? Right, so, so what, what was I doing when I was looking up in the page table? I was handling a what? A page fault. Now what happens if one of the pages is on disk that's part of the page table itself? What am I gonna generate? A multiple what? Page fault. Page fault. So the process of handling a page fault can now generate page faults. Okay, do you guys understand why this is? A process faults on a virtual address, right? That virtual address is in memory, but the data structure that I need to find that out is not, okay? So now when I do a lookup into my page table that causes another page fault, right? Which I now have to handle recursively, right? So how many, so now if I, let's say I have a two level page table. How many disk IOs might I have to do in the worst case to translate? So normally if I'm swapping in a page, right? How many disk IOs do I have to do? Or how much disk IO do I have to do? I have one page, it's out on disk. The process tried to use it. How many bytes do I need to read in? 4K, assuming 4K pages, one page, right? Now let's say I have two level page tables and all of the page tables for the process are on disk. How many IOs might I have to do? Three times 4K, right? Because I might need to read in the top of the first level structure of the page table and then I might need to read the second level structure of the page table as well, right? But the specific, if you answer this question and all you did was mention the fact that handling a page fault could generate a page fault, that's all you would need here to get full credit, right? You'd partial credit for talking about the fact that clearly it's slower because I have to do more IO, but the real challenge here is that when I'm on the page fault handling path I can now generate another page fault. That page fault has to be handled and then I have to return to handling the first one. All right, any questions about this? This problem's a little harder, I think. Right, so, right, but normally the way I do this, if I put these, if I actually swap them, what I do is I use kernel virtual addresses, right? And I essentially allow the same mechanism, right? So the kernel is just using memory, right? So the kernel would just do a read from the top level page table. And what would happen is the TLB would, you'd have the same procedure, right? The TLB would say to the kernel, hey, I don't know where this address is and the kernel would have to fix that problem first, right? So you're right, I couldn't move them in some other way, but this is assuming that I'm using the virtual address mechanism, yeah. Yep? The second level of this. So, but in general, like, I mean, if a process is an actor for a long period of time, right? And the process has memory that is devoted to page tables, right? That's the situation where I might want to move that memory out, right? It's really the same procedure as swapping, right? It's the same cost-benefit analysis. Will I need to use the page tables for this process for a long enough period of time to justify putting them on disk, right? And freeing up that memory, right? Okay, I put a couple of questions on here that I wasn't sure how hard they were. I think this one was one of the hard ones. So just be glad it's on the practice exam and not on the real exam. All right, so let's do another one. Where did, stop it, okay. All right, let's do one more of these and then let's look at the long questions, all right? So, okay, I'm just gonna put this one up here. You guys can work on this. This is essentially another one that's basically straight from the slides. Let's do this one, this one's more interesting, okay. So, a 32-bit wide architecture allows a system to map to address at most four gigabytes of memory, right? That's how wide the address is, right? But the machine on my desktop has 16 gigabytes of it, okay? So, what the question I asked you to do is propose a solution for this issue, right? On a 32-bit wide architecture, a process can address at most four gigabytes of memory. And if you remember how MIPS is set up, the top two gigabytes of that virtual address are reserved for the kernel, right? So, the process can't even use that. So, the most a process can address is actually two gigabytes. On some architectures like x86 under Linux, it's actually three gigabytes, the kernel sneaks into the top one gigabyte. But the point is that on some level, I can have more, now I can have more physical memory than I can address using a 32-bit wide address, okay? So, the question is, how do I solve this problem? Or, there's another option in the question which is, tell me why maybe this problem isn't quite as serious as I might make it out to be, all right? Two minutes, go. I'm just turning the volume up and out. I have no idea what's playing in any moment. All right, let's talk about this, this problem. Okay, so, I've got a machine has more than four gigabytes of memory. It has 16 GB of memory, right? So, how can I support this? How am I gonna put all this memory to use? Okay, so I could use a 64-bit virtual address space. Now, what are the implications of this? Well, so my page table entries are now probably at least, what? Twice as big, right? What other pieces of hardware are gonna be affected by this? No, the registers on my machine are gonna have to be 64-bits wide. What else is gonna have to be 64-bits wide? Specifically it's involved in page fault handling. The TLB, right? Which is probably going to mean that I have to use a smaller size, right? Because I'm essentially, one of the things that TLBs do is just, part of the limitations on their size is the number of bits that they're actually matching, right? So if I ask it to match 64-bits, then that's gonna be a larger challenge and I'm probably gonna have to have a smaller TLB, right? So the fetching could be easy. No, no, so the point is that my page tables might get bigger. The TLB has to hold wider addresses. Yeah, I can move some stuff around. I might move some, so what about how many levels am I gonna have in my page tables now? Potentially to translate an address, right? Let's say I use my old approach and I have some sort of offset and let me think about how to do this in a pretty way. Let's say my offset is now 14-bits and so my top are both 25-bits wide, right? So those are starting to get really big, right? So I might need to go with like a four or five or six level page table, right? And that's starting to get kind of disgusting, right? But let me ask you something, 64-bits, how many bytes of memory can you address with 64-bits? I think it's like more than the number of atoms in the universe or something like that, right? Or it's getting close, right? So do you really need that many bits? I like that, yes. We're going to 64, right? It's like the shape, you know, the razors, you know? We're going to seven blades. All right, well, okay, no, no, no, but we have a dissenting view in the back of the classroom. So you're saying you don't need it because it's by process. So again, I'll ask my original question. How do I put my 16 GB of memory to use? So even if I'm using 32-bit addresses and my virtual addresses for the process are 32-bits wide, if I have multiple processes, I can still get 16 GB allocated. I can't allow one process to allocate all of that memory, but even if a process can only address two GB, eight processes might be able to share all that memory. Now what's the one complication here, right? What does the kernel have to be able to do in order to run a system with 16 GB of memory? Well, let's say I can do this, right? But more specifically, what does the kernel have to do? If I have 16 GB of memory, the kernel had better be able to what? We've already talked about ways to keep track of this, but I'm saying what does the kernel have to be able to do? It has to be able to address all of memory, right? So if I use a 32-bit wide address, I can only access four GB of memory. So there has to be some change to the hardware architecture that do to allow the kernel to address larger pieces. So for example, I might have like one additional register on the hardware that gives me a view, right? So if I have 64 GB of memory, I might break that into four GB chunks. And then in order to address the second four GB chunk, I set a register and then my address space starts from zero. So it's essentially bit extending the 32-bit wide address slightly, right? But the point is that even if the process continues to use 32-bit wide addresses, the kernel needs a way to address all of the memory. And essentially, whatever mechanism you use, it's equivalent to essentially bit extending the address slightly. Not all the way to 64 bits. That's a little extreme, right? But maybe to 40, right? All right, so let's do, let me see what time this is. Okay, we only have about 10 more minutes. So I'm gonna just jump ahead. The next question I think is one that you guys can look at on your own time. This is a little more straightforward. And then let me, so let's look at the long answer questions, right? So you're gonna pick one of these to answer. And again, I want you guys to spend a little more time thinking about how to answer this question, right? So one of the things on the exam that I'm gonna send a caution, you is don't get bogged down in the short answer questions. Pick four, right? Three or four sentences trying to answer them as clearly as possible and then move on, right? Because there's a lot of points in this section, okay? So let me describe both problems quickly. And then we will, I'll give you guys a few minutes to talk about either one, all right? So the first one essentially allows you to ask you to look at fork, right? Fork is expensive, right? And a big part of the expensive fork is copying the address space of the process, right? So what this question allows us to do is to propose a way to address some of that cost, right? And specifically it gives you a hint, right? One of the design principles that we've talked about in this class, right? Remember we looked at this when we looked at demand paging. If you wait to do something you might never have to, so doing things right up front, what you might wanna do is find a way to wait to do them as long as possible, okay? Second question here is on priority inversion, right? And this one is a little bit longer, but let's see if I can get both on the slide and their entirety. Yes, okay, great. So, and I won't describe this one in detail, but the point is this describes a system that uses strict priorities to schedule threads and it asks you to describe a scenario when that might have a bad interaction with scheduling primitives. So what I'm really asking you to do here is first of all, describe a problem that can occur if I have strict priorities and I combine them with a resource that's guarded by a lock and the second part of the question is, propose a solution for that problem, all right? So, unfortunately we don't have too much time and I wanna have at least a few minutes to discuss these. So why don't I give you guys five minutes to talk these over and then we will reconvene, all right? And starting now, go. Do you already know the answer? What's that? You know all the answers. Give me a little hairdo. I didn't bring the ribbon for it. What ribbon? There we go. Looking good, chitch. Let's talk about this for just a second. We don't have much time, unfortunately. But let's just mention a few things about this. So does anybody wanna provide some insight into question one? Did anybody start thinking about question one? Yeah, Isaac. Oh, it's a copy and write. Yeah, so what do you do? What does copy and write do? Well, okay. You're very close. You just read the very end, right? So here's the idea, right? The idea is that when I run a fork, I don't copy the address space immediately, right? I could copy all of the memory pages. Every page it's used by the parent, I could copy it all to separate private memory for the child, right? But if the child calls the exact, immediately, I'm just gonna blow away that address space anyway. I'm gonna have to set it up again, right? So I don't wanna do that. So what Isaac said, the first part of this is right. I don't copy the address space immediately, right? What I do is I create pointers to the memory that's in use by the parent. So the parent and child on our sharing memory pages, right? Now, the problem is that the memory is supposed to look private to the child. So how do I need to set up those pages so that the child can't change them? Right, right, right. But the point is that I need to set these up as read-only pages in the child so the child cannot change them, right? And actually, I need to set them up as read-only pages for the parent, too, because as soon as either the parent or child changes the page, then I need to do the copy. But the point, the only thing that Isaac messed up was that this is done on a page granularity, right? It's not done for the whole address space. I do it one page at a time. And what can happen is that for processes that fork and don't call exact, they actually share a lot of pages. And one of the types of pages that they share are code pages, right? Those pages are never written to. And so it's possible that those pages will be shared by the parent and child for the life of the process, right? Data pages I might need to create new copies of, but code pages might never change. And if I run an exec, then the likelihood is the all I do is I do a read from a code page that's shared with the parent and then I immediately call exec and I've avoided doing all this extra work, all right? Second question, anybody have an insight here? Can anybody describe the situation that can occur that will block the high priority thread from running indefinitely? No, someone other than Isaac? Yeah, D. Yeah, I have resources that have been blocked by the voice that I would have sent. There's a needle-level process, a level of priority that blocks the process from running. Right, so this is exactly, and let me try to explain this again. This is very clear, so I'm not gonna explain it more clear. I'll just explain it again. The lower priority process grabs a lock, okay? At the beginning of time, the higher priority process is not runnable. The lower priority process grabs a lock, okay? Immediately or soon thereafter, it's preempted by the medium priority process, which continues to run indefinitely. At some point, the high priority process becomes runnable, but in order to make progress, it has to grab the lock. The lock is being held by the lower priority process, but the lower priority process can't run because the medium priority process is always runnable. Imagine the medium priority process is just sitting there doing some long running computation, right? So now what do I have? The high priority process is ready to run, but because it cannot preempt the resource, it can't get the lower priority thread unblocked. So the higher priority thread is now waiting for the lower priority thread, which is exactly what we didn't wanna happen when we set up this priority system, and we said the priority's gonna be imposed strictly, okay? So what's a solution to this? What's one possible solution? So there's an idea called priority inheritance, well, how does that work? All right, how would I apply that to this problem? Dachi? So give a high priority to the lower one so they won't finish it. So I think if you flip that around, you'll have a more convincing answer, right? So there are times in which I wanna elevate the priority of the lower process when, when the higher priority process is waiting on a resource that, so implementing this requires some mechanism for understanding that this is happening, right? If you just try to do this on a system, it doesn't work. The system has to recognize that the high priority process is blocked by a lower priority process and elevate that priorities, that process's priority. So what happened in this case is, as soon as the high priority process began waiting on the lock that the low priority process held, the low priority process would inherit the higher priority process's high priority, and this is really confusing, even as I'm thinking about it, right? And it would finish, right? It would block the medium priority thread. As soon as it drops the lock, its priority goes back to low, right? And then the high priority thread could continue, right? So this is a way to solve this problem, right? All right, questions on these long format questions, right? These are drawn from examples that people might have heard about already, so it's possible that you'll know something about this. I think the ones on the exam are actually a little bit more obscure, yeah. So if people need to leave, go ahead and leave, and I need to start packing up, but I will go over the solution again. So the solution is essentially allowing the low priority thread to have its priority boosted while it holds the lock, right? Because the high priority thread is waiting for it, and so the low priority thread gains high priority, finishes running, and then drops the lock. Come up and ask me if you need another explanation.