 All right. His hater here. I played a song in class, and he didn't come. That's one of your classmates, by the way. So if you like that song, you should download it. I don't know. It was probably some way to pay him for it. All right. Welcome back from spring break. I hope you guys got a break of some kind. Maybe your idea of a break is working on assignment too. Maybe it's not. But I hope you guys are sort of rested. So this is what was midterm week, and now is kind of the week in which the midterm is going to happen. All right. So today, we're going to continue talking about VM. Whatever we don't do today will push to Friday. I think in a sense, we're a little bit behind where we were last year. What I'm going to do is just, obviously, today was scheduled as review. We're not doing review. Friday was scheduled as the day we would go over the midterm. I think instead of doing that, I'm just going to record a screencast where I walked with the midterm solutions. You guys can prove that on your own time so we can continue covering material in class. So a couple of announcements. So everything through the end of today is fair game for the midterm on Wednesday. When you're reviewing the midterms that are up on the web page, please keep in mind that the stopping points are a little bit different every year. So there may be material on some of those that has not been covered in class. If there is, it won't be on. Those questions won't be on the midterm. Tomorrow, Jenga is going to do a midterm review during recitation. And then on Friday, we will not have a recitation because we don't want to split the recitation and have half of it do a different material. And there's no reason to review for Wednesday's midterm on Friday morning. OK. What else? So the midterm, 50 points, 50 minutes, 15% of your final grade. Same format as previous years. 10 multiple choice questions, six short answer questions of which you can choose four, and two long answer questions. Hopefully you guys have been, I can tell, that people have been looking at the sample exams online. Please do that. So the approach to doing the exam will be the same as the preterm. We'll distribute a seating chart. You guys find your seat. Come in, we'll start the exam. Unfortunately, the class before us is also holding a midterm on Wednesday. So we'll do our best to get in here as quick as we can, set up the room, and we'll try to use as much. I'll try to give you guys 50 minutes. So we'll try to use as much. If we start five minutes late, we'll try to run till 55 after. Please be here on time. Be here on time. Check the seating chart. Know where your seat is. The faster you guys get in here, get your stuff down in the front of the room, book bags, and backpacks, and all that stuff. If you don't need to bring that stuff, don't. If you do, leave it down here up at the top. The faster you guys get into formation, the faster we can start. So please don't come in here and be like, oh, where am I sitting? I mean, that's why we send the email. But the seating chart, that's also why the seating chart will be posted on every door that we can find that leads into this room. OK, so one of the slightly things we're going to do differently this year is that a portion of this year's midterm will be drawn from previous midterms. So I think there are now five or six or seven example midterms online. All but one of them has solution sets posted as well. And to be honest with you, I'm having a harder and harder time coming up with questions for the midterm. And so I've just decided let's just pick some oldies but goodies from the previous exam. Now, I'm not promising they'll be identical. There may be a small change in the wording just to make sure you guys are awake and aren't just literally haven't literally memorized. Honestly, if you can memorize all the answers to all the midterm questions that I've given, that would probably be useful for you. Not going to discourage you from doing that. But just to make sure that that's your only approach and you're not like Rain Man or something and have photographic memory, we'll might change up the questions a little bit. OK. And then one of the long answers may be from our previous here. No promises here. At least two of the short answers will definitely be from our previous here, maybe more. And the multiple choice questions, I mean, those are stupid, but I have like hundreds of those, but they're not designed to be hard. Any questions on the midterm format? Yeah. No. No, no, no. You will be assigned a seat. That seat will be a different seat. But the seating chart will be identical. And the general approach, if you come in, there's an exam on your desk. You're staring back at yourself, that sort of thing. That's the same. OK. Any other questions? All right. I mean, a lot of the success of getting the midterm to work in this room in an hour time slot is predicated on you guys doing your job. Come here, find out where you need to be, get in the room, get your stuff, put away, and get to your seat. If you're slow doing that, it slows everybody else down. And your classmates don't have that time to spend on the exam. So please try to be here on time and ready to go. OK. So I just want to briefly touch on the assignment 2, assignment 3 late credit policies that we put into effect. So for assignment 2, you're still allowed to continue to submit assignment 2. If you do so, you will receive that score minus a 25 point penalty. That's not entirely true. When we do the grading for these assignments, I promise you that we will calculate it to get you the best score possible. So if you submit it before the deadline, you got 150 or something, and wait, that's the most points you could get. So if you did that, you wouldn't be submitting again. But let's say you got 100 and you 3 submit, and you get, I don't know, you get 120. We'll give you 100 points because it's bigger than 120 minus 25. So don't worry about that. We will make sure that regardless of how you do this, you get the best score possible. If you take the solution set, however, we will take off 50 points, and we will not count any of your resubmissions. Clearly, obviously we can't allow you to resubmit the assignment 2 solution set. That wouldn't be fair, because that gets a good score. So OK. On assignment 3, assignment 3 is broken into three deadlines this year. If you miss the first two deadlines, we'll take 10 points off. That's commensurate with the amount we took off for assignment 2. If you miss the last one, unfortunately, that's the end of the class. So we can't push that one very far, and so that one is immobile. That one, we're just not going to grade your submission at that point, because at some point, we need to put the grades together and actually finish things up. So assignment 2 seems like people found that assignment to be challenging based on the submissions that we received. I do want to point out, so in previous years, and maybe this got around about this class, there were points on the assignments that were fairly easy to get. We had to answer these silly questions about the code and stuff like that. And maybe somebody told you that it was possible to pass this class without actually writing any working code. So that is not going to be true this year. If you don't submit any working solutions for any of the assignments, you will fail. I promise. That's part of the class. That's half of the points in the course. So I think that's reasonable. I mean, if you don't submit any working assignments, you're going to get something that's under 50%. I think that's a reasonable failing mark. There were a bunch of people that didn't submit assignment 2. I don't know what your plan is for passing this class. Maybe you're going to submit something again later. That's fine. But if you got a 0 on assignment 2 and maybe didn't turn anything for assignment 1 either, I would think about what your plan is going forward, because that is not going to be a winning strategy this year. There's lots of points left for assignment 3, sure. But if you haven't been able to do the first assignment 1 and assignment 2, I think your prospects on assignment 3 are pretty grim. Anyway, I don't know. Maybe you're miraculously going to actually sit down and do some work over the next couple months. That's up to you. I'm just saying, when we send out midterm grades over the next week, I am certainly very concerned about people in the class that have not submitted any working code at this point. Any questions about this? Yeah? Yeah. TAs are always there to answer any and all questions you might have about assignment 2, assignment 3, assignment 1, life in general, et cetera. Carl has a lot of interesting things to say about life in general. You should definitely talk to him about that. So yeah, absolutely. So the TAs are there. There's no limit on what the TAs are there to offer. They're there to help you guys out. And please, I beg people in the forums and other places, please start coming to office hours. I know you guys are focused on the midterm for the next few days, fine. But on Thursday, please come to office hours and ask us about assignment 3. You will do so much better. I'm also trying to fix some of the logistical issues surrounding office hours. So we will have more tables. Imagine that, like places to sit, that I think will appear at some point. I don't know. I mean, they are coming. Maybe they need to be made in some faraway country and then shipped here at great expense. I don't understand why it's hard to get tables. But there are tables that have been requested. And we are also considering moving office hours down to the second floor lounge. How many people would like that? Prefer to have office hours down there. How many people have tried to sit in those orange chairs and do anything? OK, you guys may regret that. Yeah, I don't know what that furniture is for. It's not useful furniture. So we might do that, but we're going to try to get some more furniture down there too. What I would really like to see is 30, 40, 50 people in office hours working with the TAs around them to answer questions. So you're not bottlenecked. You can be there. You can do things. And as soon as you have a problem, you can get some help and sort of go on. OK, any questions about anything? I know that back from spring break, and it feels like spring in Buffalo? Yeah. I mean, it's a penalty that's applied to your overall assignment. But you can add up all your assignment scores and take 50 points off. Remember that that does freeze your assignment to grade at whatever you earned before the deadline. That's the trade-off there. Any other questions? OK, so last time, oh yeah, the end of the semester. But keep in mind, assignment three is worth 300 points. So again, I mean, if your strategy is to work on assignment two into the assignment three deadline, I would say there are probably better strategies than that. All right. OK, so when we left off a long time ago, we were talking about we had set up our virtual world, where the memory addresses that we provided to processes were not real, and then created a problem that we had to solve with the kernels now in charge of translating these addresses to either physical memory or things that behave like memory. We talked about the fact that there's a special piece of hardware called the MMU or Memory Management Unit that's heavily involved in this process. And we talked a little bit about the policies for involved in loading the MMU. So until this point, we've been talking about how the kernel interacts with hardware. But when we finished up on Friday before the break, we found ourselves in this place where when, remember, most of the time the MMU, we hope, can translate virtual addresses, virtual page addresses, to physical pages without the kernel's help. But from time to time, it does need help because it doesn't know what a particular process's virtual address is supposed to map to, or even if that address is valid at all. And so at this point, it calls the kernel for help. MMU says, I don't understand. I don't know anything about virtual address OX 10,000. In this case, is this address valid? Where is it supposed to map to? At this point, the kernel has two options. What are those two options? MMU says, process A tried to dereference virtual address OX 10,000. The kernel can do one of two things. One is that's not a valid address. The process doesn't have permission to use this virtual address, in which case, what will probably happen to the process? It'll be killed. This is a segmentation fault. That's what you would see if you use an archaic language like C. This would be reported as a segmentation fault. And if you think about it, that's the only thing I can do. If the process virtual address isn't valid, what is it supposed to return? Who knows? So I just kill the process. What's the other thing I can do? Hopefully, the happier, more common case. Yeah. I have to tell, while not the offset, remember, at this point, the offset's already stripped off. So the MMU is asking about virtual pages. So it says, what physical pages is virtual pages translate to? The common case is that the kernel says, oh, yeah, the process is allowed to use this virtual address. And here's the place it points to in physical memory. So it has to load that translation into the TLB or into the MMU and allow the instruction to continue. And what will happen is the instruction gets restarted. And now the MMU knows how to translate that address. And whatever is going to happen, happens. And the problem here is that we need to be able to do this look up quickly. So when we get to this point in the kernel, we're already on what people refer to as a slow path. This is not what we wanted to happen. What we wanted to happen was we wanted the MMU's cache to be able to answer this question. But it can't. It couldn't. So it called us for help. But we're already slow at this point. And we don't want to get slower by spending a lot of time trying to figure out where this particular process virtual address actually maps to. And so it's really important that the kernel be organized about storing this information. And there were two requirements. One is obvious and the one that we've been talking about, which is speed. I want to be able to answer this query. You can think of this as the MMU issuing a query to the kernel. For this process, where does this virtual address map to? What's the other requirement? It's a trade-off here. I want to be able to do this quickly. But what else needs to be true? Yeah. What's that? Say that one more time. No, assume a fixed page size. The page size is a parameter of this. But we assume the page size is already chosen. Yeah. OK, yeah. Sure, it has to be right. That's a good point. Not something I put up in here. But how about a third requirement? I could just guess. Hey, who knows? Just try that page of physical memory. See if it works. That would be interesting. You can try that for assignment three. I don't know what would happen. Actually, I do know what would happen. Randomness. There's one more requirement. Yeah, this data. So essentially, we're going to talk about today as a very pretty basic case of data structure design. But this data structure is going to primarily live in memory. And so if I use a lot of memory to store this data structure that allows me to perform these virtual address to physical address mappings, the more memory I use for this, the less memory I have to provide to processes. And remember, despite our focus on the kernel here, the whole point of the kernel is to allow processes to get work done and make progress. So if the kernel is hogging all of the memory for itself, that's not very helpful. And like I said, this is a case of a simple example of a data structure that we're going to use in this fairly simple search algorithm. So the data structure is called a page table. The data that's inside the page table is called a page table entry. Last time we talked a little bit about page table entries. They need to store various information, like if the page is in memory, what the page memory address is, if it's in memory, and if it's somewhere else, and it's to store some information about where it is, so the kernel can find it. We'll talk a little bit more about where else it could be either later today or on Friday. So remember that each process has a separate page table. This is part of what we said before, which is that a virtual address on its own is meaningless. A virtual address has to be translated in the context of a process. So if I take the same virtual address and translate it in two different process page tables, I will probably get a different result because they may have the same virtual address. They may be using that virtual address. But hopefully, in the common case, that virtual address points to two different locations in physical memory. Just answer that. Okay. So let's talk about three different approaches. And this is sort of, hopefully, you guys have some vague memory of what went on in 250 and 331, so we can do some very simple analysis of the data structures and the speed trade-off that we face here. So here's one approach. One approach is I create a flat array. That array holds all of the virtual addresses, all the virtual pages for the process. And I can very easily convert the virtual page number to an index into that array. And I just use that index to look up the page table entry, and I'm good, right? So here's how this would look. I have a flat page table for each valid page in the address space. Here's me looking up virtual addresses in the code segment that points to the page table entry for that segment. I could also, this would be even worse, I could have an array of actual page table entries. But in this case, I've done the slightly less stupid thing and had an array of pointers to page table entries, since that's a little bit smaller. Okay. And here, this is pretty straightforward, right? I mean, I just, I convert the page number into an index into this page table, and I use that to look up the page. So the speed is pretty clear. This is a one. The process of converting the page, the virtual page number into an index is constant time. What about the size? How big is this data structure going to be? So from a speed perspective, this is awesome. How large is that array going to be? Yeah. Nope. So it's not the amount of memory I've allocated. It's as large as all the virtual memory that the process could allocate. So that's the problem with this. This flat array has to scale with the size of the virtual address space. But most of that address space isn't used. And so most of this array isn't used either. And so it's a huge waste of space. For a 32, I think this is a 32 bit system with two gigabyte address spaces, maybe four gigabyte, four megabytes per process of address space, of page table. Sorry. Most of those entries, so look at this. This is, I mean, and this is zoomed in. I mean, these are a lot bigger than they would be in a normal process. Most of the space in here is empty. It's unallocated. And so most of the space in my page table is also empty. So not only are these huge, they're also extremely wasteful because there's a lot of unused entries in the page table. The other process that has to, the other problem with this is that this data structure has to look contiguous in the kernel. And in certain cases that can cause problems. When you guys start working on assignment three, you'll realize why this can be a problem for your kernel in certain situations. Most of the time that's not an issue, the kernel can have a page view of memory that's similar to what a process would use. But in certain cases, there's times when if things are contiguous in the kernel, they actually have to be contiguous in physical memory. This is different than a process view. A process can have a big part of its address space that looks contiguous, but is actually built out of a bunch of different discontinuous pieces from physical memory. So this is great for speed, terrible for size. So this is sort of one end of our spectrum. Let's go to the other end. So imagine instead of using a big flat array, I organize my page table entries into a linked list. And here's how that would look. I have a bunch of PTEs, put them in some order. Maybe I ordered them from, it makes sense to order this list in terms of virtual address. It makes it a little bit easier to search. Now when I look up an entry, I need to sort of iterate through the page table until I find the entry that matches the virtual address that I'm looking for, or when I can figure out that there's no such entry and the process isn't allowed to use that virtual address. So, okay, size, how large is this data structure? Yeah, yeah, well related to this, related to something about the process's use of memory. What does this scale with? What is the number of page table entries scale with? What's that? N. What's N? I like that answer. It's very vague. N could be anything, right? N could be the right answer. But what is N? How many, I mean, if I look at the bottom there and then I look at that list, how many entries am I gonna have? Scales with N where N is what? Well, let me use allocated, right? So this, yeah, I mean this is close enough. So this scales with the amount of virtual memory that the process is allocated. That's good. Before it scaled with the total amount of virtual memory that the process could ever allocate. And that's an enormous amount, that's bad. Here, this is on some level the most compact data structure I can have because it perfectly mirrors the amount of virtual address space that the process is allocated. So, oh N, you were right, where N is the number of valid pages in the process's address space. And now compactness, oh wait, sorry, let's go back to the speed. So compactness, this is scaling with N. Speed, and this is probably, well, whatever. I mean, this is O-N without doing anything fancy. Because to find an entry in this table, if it's ordered, I have to search over, you know, on average N over two entries. If I don't do anything fancy and keep skip lists or pointers or whatever. I mean, there's plenty of ways to optimize this. But a simple linear search is gonna give you O-N performance. So, speed, not good. This is kind of the worst possible case for speed, where I'm scaling with the number of entries in the address space. Compactness is the best case. So this is my other extreme. The flat page table gave me great performance, terrible memory usage. This gives me great memory usage, terrible performance. So as usual, there is a compromise solution, which I'm about to describe, but I feel obligated to point something out, which is that this solution for assignment three is great. Better than great. It works. And it'll actually outperform some of what I'm about to show you in certain cases. So as always, when you're building something for the first time, keep it simple. Stupid, right? It's the second S and kiss. And don't over-design things. Just pointing that out, the next thing is more of a modern solution, but it is not something that you have to build for the VM assignment in this class. So how can I get some of the best of both worlds? I want something that's faster than my linkless solution to lookups, but also more compact than the big flat array. And what I'm gonna do is I'm gonna use something that's called a multi-level page table. This is really kind of a treat. What I do is I take the virtual page number that identifies the page in the virtual page in the address space that I'm trying to translate it, and I break it into multiple parts. So over the sake of this example, let's say that we break it into two equal parts. Remember, the memory address, we're talking about a 32-bit system. The memory address is 32 bits. How large is the offset for a 4K page? What's that? 12 bits, there we go. Two power 12, 12. Yes, I like the second answer. Two power 12 is too big. Yeah, so it's 12 bits. 12 bits of my address are offset. How many are left to be the virtual page number? 20. So if I divide into two equal parts, how many bits do I have? 10. I've got 10, 10. Upper half, lower half. I'm gonna use each one of those as an index into a different level of my page table. The first one I use on a first level index, that takes me to a second level index, and then I use the second one as an index into that second table. The nice thing here that makes you feel like the universe is with you, with this solution, is that it turns out that those indexes are two to the 10 times 32 bits. Turns out they're 4K, because each one of them contains a four byte pointer to a second level array. So each level of my tree is 4K, which fits perfectly into a page. Yeah, works out nicely. On most systems they do another level of this now, especially because addresses are getting wider. So usually you have like a three level page table, but this is enough to get the approach across. Okay. So let me show you how this looks. I have my address space, I have a single first level table. Now every process needs this table as soon as it allocates a single page of virtual memory. Now, so what I'm gonna do is I'm gonna take the top 10 bits, when I do a lookup, I take the top 10 bits and I convert that into an index into the top level table, and then I take the second 10 bits, the bottom 10 bits, I shouldn't say bottom, it's really the second, because the bottom 12 bits are the offset. I take the second 10 bits and I use that as an index into the second level table. And this points to a page table entry. And if I can jam my page table entry into 32 bits, this might be a page table entry. Questions about this? Does this make sense? This is awesome. You guys know something about trees now. This wasn't always the case. All right, so the, now let's think about the sort of the lookup, oh hello, the lookup complexity and the space complexity of this solution. So I think you can see that there are, this allows me to do constant time lookups. The constant has to do with the depth of the tree. In the case that I just described, I have to do two indexes to get to my page table entry. Maybe a third if you call, if there's pointers in the second table and I have to follow a pointer, but it's two or three. That's nice, that's way better than the O of N I was getting with my linked list page tables. And it's almost to the constant lookups that I get with one big single table. The compactness here is a little bit trickier to analyze, but this is another place where we fall back on the fact that process address spaces are sparse. What's the worst case sparsity here? Can anybody think about what pattern of page allocation would make the space usage of this data structure the worst possible? Let me go back to the diagram. Right? So the first level table, the first level table is on some level a shrunken down view of the process's address space. So this entry probably covers this whole code section. How many entries does this first level table probably have that are valid for this process? What would be a guess? Valid entries. Hopefully not two power 10, that would be terrible. It's way too many. Valid entries, for this process, given the example address space that's up on the slide, how many valid entries in the first level table would there be? There's at least one, because there's one example up on the slide, but how many? Somebody make a guess. Yeah? Five, five's a reasonable answer, right? Could be three, could be five. Here's one, here's one. I mean, there has to be one for the heap. And the heap is far enough away from the code segment that I'm gonna need another entry in my first level table. And then I've got three stacks here. Now, it really depends here on how far those stacks are apart from each other. But let's just pretend they're far enough apart that they all get their own entry in the first level table. And so the maximum I could have here would be five. I guess if the code segment was really big, I might have two. But that would be a pretty enormous code segment. So I have five entries in my first level table that are valid. How many second level tables do I have? If I have five valid entries in my first level table, that means I have how many second level tables? Five, yeah. So you can imagine doing a lookup of something in the heap segment that would hit here and that it would have its own second level table that I would use to do the second level lookup. So when I look at the compactness, can someone see, how do I make this approach use the most memory while using the smallest amount of virtual memory in the process's address space? What's the worst possible layout for this approach? This would be a good midterm question. There is a layout here, yeah. Okay, so I think you misunderstood best, right? That is the best in terms of allowing me to map the most virtual addresses with the fewest amount of kernel memory. What about the worst? Yeah, yeah. Yeah, let's say I have one, imagine that each one of my second level tables has only one valid entry in it. So I have a valid page here and here and here and here and here and here. I have like two to the 10th of those, 1024 pages that are all far enough apart that I need 1024 second level tables. The nice thing about this is that that's not how process address spaces are laid out. There are chunks of things that are, have some logical connection to each other like the code, the heap and the stacks with large amounts of empty space in between them. And so in practice, that case where I have this incredibly sparse address space with tiny little bits of data that is as far apart from every other piece of data as possible doesn't happen. And so despite the fact that in the worst case, this can approach the flat array in terms of the amount of space I need, in most cases it doesn't. In most cases, what happens is my second level tables have a lot of valid entries. And so I'm getting a lot of mileage out of a single second level table. Any questions about this before we go on? Yeah, data structures for the win. They're actually useful sometimes, imagine. Okay. Any questions at this point before we go on? So we've pretty much finished our conversation of how we provide the illusion of virtual memory and now we're gonna start talking about something else. It's also memory management related. I need lingering doubts or questions before we continue. Yeah, okay, good. Okay, worst case scenario here. I wish I had a slide for it, maybe next time, right? So in the worst case scenario, really what I want here, so I always need a first level table. Does that make sense? I can't map a single address without a first level table. But, so when I'm thinking about minimizing the amount of memory that's needed for this data structure, what I'm thinking about are the second level tables. Now, if this data structure is working well, what happens is that each second level table has a lot of valid entries in it. Does that make sense? Because that means that that second level table is mapping a lot of virtual address space. That's what I want. What that means, if this second level table has a lot of entries, it means that there's a chunk of contiguous virtual address space right there, right? Does that make sense? So there's this connection between the utilization of the second level tables and things that are close together in virtual memory. Now, if I can create a case where I have 1024 second level tables and each one of them only maps a single page, what that would mean is I would have one valid entry in here and then I'd have another second level table over here and it would have one valid entry. That's the worst possible case. It means that I'm only mapping, I don't know, four megs of memory and I'm using four megs of kernel memory. It's not good. And how that happened, but that, in order for that to happen, I have to have an address space that is weird. That's just not how ELF loads things, right? So ELF is designed to put things close together. So ELF says, okay, I'm gonna create a chunk of code right here and then the heap starts a long way from that and I have the stack area. I just don't lay out an address space where I have these little stripes of pages. That just doesn't usually happen. You could, you could do that. You could certainly write a program and fiddle with the layout so that it did that. I don't know, that's an interesting question. It's a way of sort of, it's like a memory bomb, right? Run that on Timberlake and see what happens. In your fork bomb loop, right? Because while you're running a fork bomb, you might as well hit the memory subsystem too. Hey, why not? All right, good question. Okay, so I went through this. Okay, so now, let's talk about running out of core. So there are cases, one of the cool things about this. Now, this is another one of those interesting topics that sort of is part of the canonical operating systems class, that I'm not sure how relevant it is anymore. Why is this, why is the idea of running out of memory maybe starting to become someone of an anachronism? Why might we not be teaching about this in 10 years? What's happened in the past 10, 20 years? Yeah? Memory's got a lot cheaper. Well, by whatever means, you just have so much more memory now than you used to. I mean, the amount of memory that are in machines is just skyrocketing. So, the days where you had like a little machine with like four megabytes of memory or something, okay, fine. I mean, that machine had to do some stuff to deal with the case where it ran out of memory on a machine with 32 gigabytes of memory. Now, there's certainly cases where you can run out, but I would argue in a lot of consumer grade machines, I don't know what you would be doing to cause that to happen, right? Unless you're watching like 64 videos simultaneously or something like that. I don't know, do people do that, is that a thing? Maybe that's the next step for you guys that are so good at processing lots of inputs is to literally be able to watch two movies at the same time. Anyway, so, I don't know, but talking about this gives us a fun way to illustrate how powerful this virtual memory abstraction is, okay? So, we've been talking about cases where we've been running along, the kernel is translating virtual memory addresses to physical addresses. But at some point it's possible that we can actually run out of memory. That happens when the amount of physical memory is exceeded by the total amount of virtual memory that's been allocated by all the processes on the system. One of the reasons this happens is that our Atrus-based abstraction has kind of encouraged processes to think that they have all this available memory. You're making it look like to the process, hey, I've got four gigabytes of memory that I can allocate, this is awesome, I'm just gonna go hog wild and allocate some big unnecessary data structures for no reason. So, that's part of the problem, but you also have, you know, we've talked about in the past, processes that have huge code segments that contains every possible function that Excel might ever want to run. And so it's possible at some point that I get to the point where I'm out of core. I don't have any more physical memory left to satisfy requests by processes to allocate virtual memory. And this is a dumb diagram showing exactly what I just said. At some point, let's say the process wants to grow its heap at this point because it's doing more allocations through malloc, I'm out, what do I do? So, there's two options when I run out of memory. One is to start failing, and this can happen. So it's possible, you know, the kernel is under no requirement to satisfy every fork request. And if you run something like a fork bomb on a real system, this is eventually what will start to happen. It just says, nah, no way. And it probably hits this limit a lot earlier than it would normally because you probably have some limit on the amount of memory you as a user are allowed to allocate. So I can fail, that's one thing. Or I can create more space. And number two is frequently a pretty attractive option. One of the reasons for that is processes create a lot of memory that they don't use. Or they allocate memory that they use briefly and then they stop using. And so, unless I'm on a system that's really, really heavily loaded and there's a lot, and this can happen. So there's cases where Linux, for example, will realize that there's enough activity in the memory system going on that it can't do number two, right? Number two relies on the fact that there is available memory that I can safely repurpose that's not active enough. If there isn't, and the system can tell that, it really has no choice other than to start failing requests or in the case of Linux, figuring out what process is to kill so that it can reclaim memory, yeah. Yeah, it's sort of like garbage collection, not quite, right? Garbage collection, there's so much more structure and rigor to it. It's not a bad analogy, but let's put it this way. Let me make the analogy to garbage collection. So, in garbage collection, what I try to do is figure out memory that I know is not going to be used anymore. Number two, what we're gonna start talking about today is like guessing what memory is not gonna be used anymore, right? So if a garbage collector looked around and it said that object hasn't been used for a while, there's still a reference to it, but I'm going to move it out of the way, right? Number one is like the garbage collector saying, I'm just going to kill your objects and then the next time you try to use them, you're going to crash, right? Not what you wanna do in most cases, but in certain, when I have no other options, sometimes it is the right thing to do, right? I think I mentioned this before. If you look at the logic that's run by Linux to figure out what process is to start killing off, this is called the out of memory killer, it's totally bizarre, because it's guessing, right? It has no idea, right? It's possible that the thing it killed off is the exact thing that you wanted to continue to run, but it doesn't know, right? So it uses all these heuristics, like how much memory is it using, what priorities are running that, blah, blah, blah. But it's really just guessing, because at that point, you know, you're in a bad place. You just have to hack your way out, okay? So remember when we introduced the idea of virtual addresses, we talked about the fact that virtual addresses give us this ability to transparently move things around behind the processes back. Because the process has to rely on the kernel to translate these addresses on demand, if the kernel wants to do something like, yeah, let's move a page out to disk, it can do that by manipulating these virtual address mappings. So, and all that's required to do this is that the process not notice, kind of, right? The process might notice, it might notice that one memory address, one memory operation that it thought would happen really quickly took a long time. But as long as memory that I used last time, like memory, continues to act like memory, the process won't notice. So I need to preserve the contents, and I need to make sure that a stream of reads and writes to a memory address behave the way that I would expect. But virtual memory allows me to relax the assumption that the memory that the process is using has to actually be located in memory the whole time. And, you know, of course, obviously I need to preserve the contents. So if you do a bunch of writes to a memory location and you write 10, and then the next time you read from it, it's zero, this is a problem. Your program will stop working. So the process of moving data back and forth to the disk as needed is sort of, someone is referred to as swapping. You know, the idea is I'm going to take a page of memory and swap it out to disk, and then swap it back in when it's needed. So there's normally a special place on this. How many people have ever set up a Linux machine? So you've done this, right? Maybe you didn't notice it, or maybe Ubuntu did it magically for you. But when you created your Linux machine, you created a swap partition. That's a place on disk that Linux uses to do this. So Linux does not swap into a file. There's no, if you look around on your file system on Linux, there's no file that holds swapped data. And there's reasons for that that have to do with efficiency. Linux instead takes a portion of your disk and reserves it for use in this process. It's called the swap file. I think Windows does something similar now, although I think Windows used to swap to a file, which was weird, it's scary. So, okay, call the swapping. Now here's the goal. The goal is that if I swap effectively, if I do a good job of choosing what to move out when and what to move in when, I can make your computer feel as if it has a lot more memory. I can make it feel as if it has as much memory as the size of memory, plus the size of the swap disk. And all of that space is as fast as memory. That's the goal. Now, if I do it badly, it can make your computer feel like what is actually memory that you paid a lot of money for, less than you used to, but still fair amount compared to disk, is as slow as the disk. And so this is the place in our story about virtual memory where policies used to drive this process start to play a major role. And so the first thing we're gonna do is we're gonna talk about the mechanism that's used to move things back and forth. And this is also a great chance for us to review the basics of address translation because if you can understand swapping, you understand the normal sort of address translation process. And then we'll talk briefly about some algorithms that we use to drive this process. Because if, like I said, if you do this well, you make the computer feel like it has a lot more memory. If you do it poorly, you make the computer feel like the memory it does have is super slow. We, of course, we don't wanna do that. All right, I'm gonna stop for today. I will see you guys on Wednesday. Good luck on the midterm. Please be on time. Please review the seating charts. And let's get to our seats and get started on Wednesday.