 I'm going to save the next song for next time. It's a fun video to show along with it from last year. So thanks for taking the midterm. How many people feel like they did well? Reasonably well? OK. Was it too hard? Perfect. Someone's looking for extra credit down here in the front row. It was the best midterm ever. Maybe feel so good about myself. You guys must have forgotten about it already, which is great. So today we're going to finish, hopefully, talking about paging and get through swapping. It's a good way to reinforce what we talked about. And to some degree, some of the things we've been doing with virtual memory have all led us to this point. So this is one of the things that's fun about it. I mean, that's a lot of other benefits. And this is also preparation for assignment three. So assignment three is out. The write-up's been out for a while. Scott's finalizing some changes we're going to push upstream just to fix a few things. So go ahead and get going on that. Let me just point out a couple things about assignment three. So it's worth a lot of points. It is hard. But I will point out, particularly for people who are sort of thinking about what to do about assignment two, the first checkpoint. So we're distributing assignment three. We'll see how this works in three pieces. The first part is to get your core map allocation working so that Kmalik can allocate and free kernel pages. The second part is to implement paging. The third part is to implement swapping for those of you that are working in pairs. The first part of the assignment, which we are giving you two weeks to do, so it's due two weeks from today, which I think is April 8th at 5 p.m. That part of the assignment does not require a working assignment two kernel. So that's something you can do entirely independently of assignment two. So if you're still working on some of the assignment two stuff and you're still hoping to get some points for that, you can kind of overlap those two parts of the assignment and make a decision potentially about taking the assignment two solution set a little bit later. Once you start to work on paging, that's something that does. So essentially all the first checkpoint assignment three core map tests run in the kernel. They actually don't even run in user space, so you don't need anything in user space working to get them to work. So if you want to go ahead and do that, be my guest. And then around April 8th, you can make a decision about what would take the penalty for the solution set or not. The other thing I want to point out, we published these deadlines, sort of a new idea for us to split up assignment three, mainly so people don't wait until six weeks from now to start. Hopefully you guys have learned your lesson from assignment two on that if there was a lesson to be learned. So however, the first part of assignment three is significantly easier, I would argue, than the other two. There's a couple of groups that are already done with it. So is there some people saying, oh, few. I'm going to start two weeks from today, right? Please don't do that. But also, please don't stop there for some reason and feel like, oh, OK, I'll just take a break. Go on, finish the Cormap allocator. Go on to work on the other parts of the assignment so you don't run out of time for the other two pieces. I think if we did this again, we'd probably do it like 1, 3, 2, or something like that. But I didn't want to change the date. I think I thought there would be mutiny if I moved to deadline earlier, so we didn't do that. OK, any questions on assignment three? Any questions on the midterm? Yeah? What's actually assignment three? OK. Yeah? Yeah, yeah, yeah. So we have, this is one of the things Scott's working on. We have targets. We have test 161 targets for each checkpoint, right? I think he's almost done with the first one. The first, I mean, I can tell you what the first one is, get everything from KM1 to KM6 working. The mythical KM6. You guys don't have KM6 yet. It's coming. So we're going to add a couple of format tests. But if you get everything from KM1 to KM4 working, you should be done unless you're doing something weird. But yeah, so that's the first checkpoint. But yeah, we will like, I mean, it's essentially going to be assignment two. There will be a target. That's 161. We'll do all the right stuff. We are going to start dropping in on assignment three some tests for memory leaks. Just warning you, so you may end up fixing some things from assignment two anyway. At this point, I think it's fair game. I didn't want to backport this into the earlier targets. The first part of the assignment is all allocation and deallocation tests anyway. So there's really no need for memory leak tests there. But for the second and third targets, we certainly will look at this. When you're done with this assignment, your kernel should run forever, which is kind of cool. So if you leak memory, it won't do that. And all the users of your little kernel will be unhappy, right? So yeah, good question. Any other questions about assignment three or the midterm? OK, yeah. And like I said, we will try to get the midterm graded by two weeks from today. Maybe it's not a long time. I don't know. I tried to give the TAs enough time to do it, so that's what we've promised. I hope that deadline won't slip. If it's earlier, it's earlier. It does take a little while to grade. There's a lot of you and a lot of exams and a lot of bad handwriting to look at. Thank Ali in advance for all the translation. He's all the virtual translation he's going to be doing trying to figure out what on Earth you meant and map it into some sort of reasonable space. OK. So last time we got to this point where we started to consider the question of, what do we do if we run out of physical memory? And up until this point, what would cause us to run out of physical memory? Let's say your kernel boots and has a certain amount of physical memory available. Don't worry about the memory that's used by the kernel itself or used internally by the kernel. At this point, what would cause us to run out of physical memory? How can I, if I had a bunch of programs to run, how would I get a general sense of how much physical memory they would require and when I would run out? Yeah. Yeah, because right now, the only place a virtual page can be is in physical memory. That's the only place we allow it to be. It has to be in physical memory. Now, we've talked about other places it could be, and we'll get to that today. But until now, if a process needs a certain amount of virtual memory, those virtual pages have to have mappings to physical pages. Now, eventually, I think between today and Monday, we'll talk about two ways to relax that claim. Both of them are pretty useful. Now, when we run out of core, we can either start failing allocations or we can create more space. And this is what we're going to talk about today. This is where things get kind of interesting. Now, in order to create more space safely, the memory has to, we need to do this in a way that the process won't notice. The memory has to look like memory when it uses it and look like memory again later. If we introduce some delays along the way, that's OK. This is kind of like context switching. We're giving, in the context switching, we gave the thread the illusion it was the only thread running on the CPU, but we did stop and start it periodically. Here, we're giving the process the illusion has a lot more memory than is potentially available. With the price being that from time to time, a memory access might be a lot slower than it was anticipating. OK. And we do this using virtual memory. This is the, again, this is the tool that gives us the ability to do this, because the kernel is in control of those virtual to physical or wherever translations. In order to use a virtual page of memory, the kernel has to load it into the TLB. And how would I stop? Let's say there's a page of memory that I want a process to stop using. How do I do that? It's already in the TLB. How do I get a process to stop using a page of virtual memory? What do I do? I'm the kernel. I could give out an orange for this answer. That would be weird, right? Doesn't Steve do that? He could sell like Jolly Ranchers or something. It's unhealthy. That's bad for you. You ruin your teeth. What do I do? So what did I do to give the process permission to use the virtual address? What was the kernel required to do? Start calling out people. Guesses? Yeah? Did I put the page table on you? Well, I had to have an address in the page. I'm going to start eating this because you guys are so quiet. It had to have an address in the page table, right? I need to be able to use the page table to perform the translation. But then once I found the translation, what did I do with it in order to allow the process to use that page? Yeah? Yeah, I load it into the TLB, right? And then the MMU does the work for me. So if that's how I give a process a permission to use a virtual page, how do I take that away? Remove the entry from the TLB, right? OK, we just did this. So now when I move things in and out of memory, the typical place that operate systems use to store them when they're not actually memory is the disk. The disk is, how is the disk different than memory? I shouldn't have put such a big piece in my mouth. It's slower than main memory. So I have to be careful about how I use it. If I put the wrong things on the disk, then I have the possibility of making the main memory seem as slow as the disk. If I put the right things on disk, I can create more space in main memory and make the disk look as fast as RAM, right? This is not working out well. I'm going to stop. My mouth is really dry, though. OK, so the goal of doing this is to make sure that the process doesn't notice. And what we're going to talk about on Monday are the policies and algorithms that drive the choice of what to put where. Today, we're going to talk about the mechanism for moving things around, OK? Now once this starts to happen, so when we started today about 10 minutes ago, there was one place that a virtual page could be. It could be in physical memory. Now there are two places it can be. It can be in physical memory, or the operating system may have transparently moved it out to disk to a swap file or a swap area for safe keeping in order to create more main memory to use for something else. And this, now when the process tries to use the memory again, this leads to two different cases. And we distinguish between two kinds of memory-related faults. A TLB fault is pretty much what we've been talking about up till now. What this requires is that the operating system load an entry into the TLB and tell the TLB how to translate the address. However, in this case, the location of the page is in physical memory. The contents are in physical memory. The TLB just doesn't know how to translate that particular virtual address. We have a second case now, which is called a page fault. And the page fault occurs when not only does the TLB not know how to translate the virtual address, but the contents of the page are not in memory. And there's actually two subcases here. In one case, the contents are somewhere else. And in the other case, the contents have not been initialized yet. So can someone think of an example of the second case? I'm tempted to reach for the orange, but I'm not going to. Give me an example of a case where I would have a page fault in uninitialized memory. Yeah, Maroc. So a segmentation fault gets trapped here, right? Because in there, I realize I look at my page tables and I say, the process can't use this memory. And so it would just be killed. What's a case where I have some initialized memory that I'm going to initialize and then allow the process to use? K-malloc. I like that. It's close. It's one letter off. Malloc. So the heap. When I need more heap, we talked about how S-break will request that the operating system move the breakpoint and allow the process to have more virtual memory. That virtual memory doesn't have any contents in it. So what is the operating system put in it when it starts up? Let's say I need more heap and I call S-break and I give you some virtual memory and then I have a page fault in that area. What do I put into that memory? What's that? Nothing and garbage are not the same thing. So nothing is a good answer. Nothing, what is nothing in computer science? I like that. No, we're getting closer. Zeroes. I just filled it with zeros. Empty space. No active transistors here. Just off. Garbage, now K-malloc on your system uses these weird values, dead-beat values. That's for debugging purposes. Normally, when I give a process a page, I fill it with zeros and hand it over. So that's an example of an initial left page. So it's important to note here that page faults are a subset of TLB faults. There is no way for a page fault to not be preceded by a TLB fault. Because if the page, here's the problem. If the page is somewhere else or if the page contents are uninitialized, like the example that we just described, there had better not be an entry in the TLB for it. And if there is, the problem is that the operating system will never get involved and can't do anything here. So one of the requirements for moving pages out to disk is to make sure that, as someone would be pointed out just a minute ago, I take away the permission from the process to use them by making sure that all the entries in the TLB are gone. OK. Yeah, there we go. And but not every TLB fault will generate a page fault. So it's impossible. It is impossible to have a page fault without a preceding TLB fault. It is possible and hopefully common to have TLB faults without a page fault. Yeah? Yeah. Uh-huh. And that's going to be slow, right? Yeah? What's the problem? Yeah. Let's come back to that. So you're implanted processes that are sharing memory? Yeah, I have to be careful about that. So we'll come back to this. So there are these cases where, while I'm in the process of swapping something out, a process can try to use the memory, and then I have a problem, right? It's not a bad problem. It's just kind of sad, because I'm doing all this work to move the contents to disk. I'm just going to come back. I'll get to that later. I think we have that example. It's a good question. Bridgesh is about 10 slides ahead of me. OK. So now let's think about actually moving a page to disk. So what are the things I have to do? I've got a page of memory, 4k, 8k, whatever. And I want to move it to disk. Let's say the page is in the TLB. What are the different things I have to do? Don't worry about getting them in the right order. But what are some of the things that I need to accomplish? Yeah. Yeah, I better be able to find it later. Where am I going to put that information in the page table? Yeah. So I need to make a note in my page table of where I'm about to put this page, because if I don't, I will never find it again. Or it'll take a long time. Probably the former. What else do I need to do? Yeah. Yeah, I need to make sure the contents of the page are copied to disk. What else? Yeah, I probably did that before. I probably did that when I started up. The nice thing about pages, of course, is that the on disk structure is very simple. I have the same benefits of paging in terms of fragmentation on disk than I did in main memory. Yeah, what else? Yeah, I better get the translation out of the TLB. That's required so that I can stop the process from using the page. That's really the first step. The first step is make sure the process is not using the page anymore. Then I'm going to copy the contents to disk. Why can't I do this first? Why can't I reverse the order of these two steps? Yeah, if I try to start copying things out before I remove the TLB entry, it's possible that the process, or thread, or processes that are using this page are going to alter the contents before I'm done. And it's there what I might end up with is a weird garbled mix of some of the contents from the page and old contents that have been overwritten. So I need to make sure I stop any accesses to the page before I start this process. I update the page table entry to indicate where it is. And here is our diagram. So awesome. So the first thing I'm going to do, so here I have a case where this process has a code page. It's mapped in the TLB to some page in physical memory. My page table entry also refers to that place in physical memory. So I'm going to delete the translation. This prevents the process from using the page from this point forward. I'm going to copy the contents to disk. Clearly, these are important contents. And then make sure that I update my page table entry. The last two can be done in either order. So this is kind of a progestious question, I think, which is what happens if so clearly it takes a long time to copy all this blah, blah to disk. What happens if I get a TLB fault now? So it's ugly, shall we say. What we will suggest you guys do for assignment three is to finish swapping out the page and then swap it right back in. That's what's sad about this, is that I went to all this work to get the page onto disk. And now I'm doing all this work to get it right back into memory. Keep this in mind when we talk on Monday about page replacement algorithms, because the goal of algorithms to choose pages to swap out. So when I'm low on memory, I've got to find a page to swap out. The page that I want to choose is not a page on which there is going to be an access during the swap out operation. That is an indication that I have chosen a terrible page to swap out. Because essentially, again, I'm done swapping it out. Now I've got to swap it right back in. I suspect on some systems they have a sophisticated way of kind of canceling things halfway through and keeping the page in memory. But in general, to answer your question, when I'm in the process of copying the contents to disk, and this takes a long time, right? This is a slow operation because the disk is slow. I need to mark this page in physical memory. And usually I do this in the page table entry or in the core map. I need to mark it so that other processes know that this page is sometimes just referred to as it being busy. There is IO going onto the page. And so I need to wait for that to complete. Yeah. So what would happen from the perspective of the process? So this guy was using this. I took it out of the TOB. He's running along. He does this again. If he tries to access this page, he has to sleep. This process has to sleep not only until the swap out is done, but until the swap in is done. And then I can restart. But yeah, absolutely. And we'll come back to this when we talk about swap out in about two seconds, right? Which is that if a process tries to access, remember, the process has no idea that this is going on. I did it. I didn't tell it anything. And a lot of times the first step here is also unnecessary because frequently pages that are not used are not going to be in the TOB. If they're not used by definition, the process is not trying to translate those addresses. And there's no reason for them to be in the TOB. So in a lot of cases, if I find a good page to a victor to swap out, that page will not have a TOB entry. And that's a good thing. It indicates I'm picking a page that's not hot, right? That's not being used actively. These are good questions. Like I said, if you can understand this process, you probably understand most of what you need to know about virtual memory. OK, so now we're done. So now we start to think about what are the parts here that are slow. So we're moving the translation from the TOB fast, slow, really fast, like one instruction, right? Copying the contents of the page, super, super slow. Updating the page table entry also fast. It's like a single memory operation. So the cost here, because when we talk about the page table algorithms, sorry, page replacement algorithms, we're going to talk about cost-benefit calculations, the cost here is really that IO. It's doing the work to synchronize the page contents on this. Now there's a trick that we'll come back to that you can play that the OS frequently plays to try to get rid of step number two. And some of you guys may see what I can do here, right? So actually, you know what? I'm going to talk about it right now, apparently, according to the slides. I forgot. So the problem with swapping out is that I have this IO that I have to do. Now what's the other problem with this taking a long time? If I'm swapping out a page, why am I doing that? Like, why am I going to all this trouble at all? Yeah. I ran out of memory. Why did I run out of memory? Or what happened that caused me to run out of memory? I mean, I wouldn't be out of memory except for something trying to allocate memory. Somebody tried to grow their heap, or someone tried to call exec, and I had to load in a bunch of code from the L file, or somebody tried to call fork. So something happened that created memory pressure on the system, probably caused by an application, and that application is now stopped waiting for me to do all this stuff. So that's not good, right? I've got something that you wanted to happen. You launched an app or something, and you're sitting there, and it's spinning at you, because it's doing this stupid stuff. So it would be better if I could plan ahead a little bit here. There's a couple of ways that operates this. I'm just frequently do this. One way is that frequently all the time, the operating system is trying to move things out to disk and trim unnecessary pages. So you can imagine, at all times, the OS wants to have some buffer of memory on the system. In general, memory is expensive and super fast. So I want to use as much of it as possible, but I want to leave a little bit of a gap there at the top just in case. What's one thing that that helps me with? What's one thing that you guys do all the time that causes an enormous amount of completely sudden and unpredictable memory pressure on your system? It's a very common thing. You guys do it regularly. What's that? I'm even thinking worse than that. What's the worst? Wait. Yeah, you guys launched something, like suddenly the OS is like, oh my gosh, they're running Microsoft Word. It's happened. I was worried about this day. And suddenly you have this huge program that you're trying to load and they're like, oh, okay, sorry, didn't realize you're gonna do that. Clearly, I can't predict the future and you double clicked on the icon like two microseconds ago. And that's an argument for leaving a little bit of a buffer of memory there at all times because that means that when you try to start a new application, that OS doesn't have to suddenly clear all the space and memory to make room for it, which is slow. So a lot of times I leave a little bit of a buffer. The other thing that I do is I try to make swapping out itself fast. So what was the slow part of the swap out process? Copying the page disk. Is there a way to not have to do this when the swap out actually happens? Is there a way that I can prepare the system to do this? How do I do that? Yeah. Yeah, just do it earlier. Not that hard. This is sort of the opposite of procrastination based technique that we'll talk about later, right? In this case, planning ahead is actually a good thing. So when your system is idle, the operating system goes through memory. It looks for pages that are what it's called dirty. There are contents in memory are different than the contents on disk. And it just writes them out in the background. When I was in college, like every budding computer scientist, I slept right next to my computer. Actually, it's because I was in a small room. But it was irritating because at night, I'd leave the computer on at night because it was doing important things, obviously. But at night, the disk would constantly make this little sound, like, you know? Can you hear that? I tried doing it louder. And it was because of this, I'm pretty sure. Because Linux is just constantly firing up the disk again. I don't know what was causing dirty pages, probably something stupid. And I guess there's some ways to turn that off. Computer scientists all over the world have slept better since Linux introduced a way to disable this. But that's what was going on. Constantly in the background, I was just trying to make sure that the contents on disk are in sync. And then what, and you should also note that it helps when I do this if every page has a dedicated place on disk. Because now, I actually have two copies of every page. I have the on disk copy, and I have the in memory copy. And so I'm kind of synchronizing the contents periodically. And now what happens is when I do the swap out, I can emit that stuff. All I have to do is remove the TLB entry if there is one and update the page table entry. That's it. So it's a lot faster. Any questions on this? Kind of a neat technique. Yeah. Yeah. Wait, what was small? If you have only 800 pages, why are you allocating the core map for 10,000 pages? Okay, the core map, just to be clear, only has to map physical memory, right? So the core map should scale with the size of physical memory. If you have small physical memory, you have a small core map. You only need one entry in your core map. Remember, the core map is that inverted page table. It maps the page table maps from virtual addresses down to page table entries. The core map to some degree maps from physical pages back up to page table entries, or at least to some information allows me to find the page table entry. Yeah. Ooh, good question. So how do I figure out whether or not the contents of the page are the same as they are on disk? So I could, there's some bad ways to do this, right? So for example, I could read the entire page into memory and compare the contents. What else can I do? Yeah. Yeah, that, yeah. So essentially, when I write the page to disk, it is by definition clean. Now, there's a problem, which I don't think anyone has acknowledged here, which is that if that page is in memory, the process can be using it transparently. The OS has no idea. So at minimum, it's possible that I can only necessarily do this for pages that are not in the TLB at the time that I'm doing the cleaning. Now, it turns out that in some cases, the MMU can help me here. So it turns out for OS 161, your simulated MMU has a way to load an entry as read only. What happens when an entry is loaded as read only is that if the process attempts to write, to modify that contents through a store operation, it triggers a trap into the OS. And if I wanted to, I can use that as a moment to mark that page as dirty. Because now I know there's been actually a modification to the page. In other systems that don't have that capability, what I would have to do is whenever the page was accessed and it was loaded into the TLB, I marked it as dirty because at that point, I have no visibility into the page and how it's being accessed, and so it may be modified. Does that answer your question? Yeah, so there is an interaction here between my ability to see changes to the page and the capabilities provided by the MMU. Okay, yeah, sorry. It depends, you know, I think, so when you initialize a Linux system, Linux allocates a swap area at disk. That swap area is just a region of disk. There are arguments for not doing this in a file. And when we talk about file systems, you'll see why. File systems have a lot of capabilities built into them that swapping does not require. So for the swap file, for example, I know that every single page in it has is 4k, right? So it's in a 4k array. Files are not like that, they have names and they change size and they move around. So there's a bunch of stuff that the file system maintains that the swap file doesn't need as far as on disk data structures. And so I would argue it's a better design decision to just allocate some space on the disk for it and let the OS manage it itself. If you wanted to, you can put it in a swap file that sits on top of the file system, but there's inefficiency associated with it. Sorry, I'm sorry. Yep, depends on how you configured it. I think Linux allocates an equal amount of hard drive space as memory. So by default, yeah, you would have eight gigabytes wasted of your hard drive. But yeah, I mean, your hard drive is probably like four terabytes anyway, so you may not notice the law space. It's a good, that's a good question, yeah. Any other questions? Okay, let's do swap in, so much more interesting. So now if I want to, so first of all, when do I need to swap it? What triggers a swap in? Yeah, how does it express to me that it needs it? Is there like, does it say I need it? I really need that page. Yeah, but what triggers a page fault? What does it have to do? It accesses the virtual address, right? It does some sort of memory operation on that virtual address. Remember, the process has no idea that this went on. I think it's really important to emphasize. I did all this. To the process, I've still got, you know, virtual address OX10,000 and it looks the same to me. I know it's been a while since I used that address, but at some point the process is gonna use the address again and this is gonna trigger this whole chain of events. And this is what's nice about this because if the process never uses that address again, if this is some weird menu of the program that you stumbled into and you will never return to, that page will never come back. And again, we'll talk about this in Monday when we talk about page replacement. Okay, so we have to swap it in when the virtual address is used because at this point, remember the whole contract here is that we make sure that memory behaves like memory. So I know I did all this weird stuff and moved it out to disk and things like that, but now it has to be memory again. And so I need to bring it back in and make it look like memory. I need to actually make a memory. It is now required that it be memory again. So this happens pretty much internally, but this is kind of an interesting requirement which is that the instruction that was executed has to be stopped. What happens here? What's the mechanism for getting into the kernel? Remember when we talked earlier in this semester, we talked about traps, we talked about interrupts. There's two times that the OS gets to run. There was another case, yeah, exceptions. And when we talked about exceptions, we said, oh, there's these silly things like divide by zero. This is the most common exception. This exception happens millions of times on your system all the time. And it's completely benign. There's nothing that the process did wrong. It just, the exceptional condition it created is that this entry is not in the TLB. And it turns out in this case it's actually not even in memory. So we have some work to do. So I need to stop the instruction. I need to allocate a page in memory. So this is interesting. What can potentially, so I'm trying to swap a page in. If I'm under a lot of memory pressure, what could this cause to happen? I'll swap out. Yeah, so this gets, so has anyone, we'll talk about this later, has anyone ever had a system that they would have described as, I'm trying to say this in the most awkward way possible. Has anyone ever had their system thrash? You know what that feels like? Yeah, if you have an experience that you should just be happy that you were born at the time and place where you were born and your system has like 32 gigabytes of memory or something insane, right? But back in the good old days when their memory was expensive and we didn't have as much of it, systems would do this more often. So thrashing is a state that also has no precise definition, which I love. It is generally defined by a state in which a system is making no progress because it is just paging memory in and out. So if I get really low on memory and a lot of things are going bad, I can get into a state where the OS is not doing any useful work or I should say that the programs running on the system are not doing any useful work because all they are doing is triggering memory, page fault after page fault after page fault. You can, it used to be with the old mechanical disks you can hear a system that was thrashing because where mine was doing that, this is like, I mean it's just, the disk is just going nonstop and it's also sitting there, it's unresponsive, lots of things go wrong. This is when Linux triggers that out of memory killer because things are bad, right? Stuff went really wrong. I don't know how I got to this point, I made some bad life decisions but it's time to start killing off processes because things are not gonna get better. Like it will just thrash until you pull the power plug or I just find some processes to kill that create some spare memories so I can start to make progress. Okay, so hopefully that doesn't happen. Hopefully there's some pages and memory that I can use for this. I locate the page on disk, copy the contents in, update the page table entry to point to the new physical page that I allocated, load that into the TOB and finally restart the instruction. Now, once I've done, whatever instruction triggered this page fault should be able to complete safely because now the TOB knows how to translate this and more importantly, the contents are actually in memory. Any questions about this progression? We'll do the diagram. So here's the case, I'm on disk, process tries to access this virtual address. The first thing that happens here is a TOB fault. Remember that the page faults are all subsets of TOB faults. If this page were in memory, I'd be in good shape. I'd just load the TOB entry and I'd be done but it's not. So I've got to find some physical memory, copy the contents in, update the page table entry, reload the TOB and restart the instruction. And that is how we do this. Note also in case you hadn't been convinced of this before that this means that the same virtual address can very, very commonly map to a bunch of different physical addresses during the lifetime of the process. If a, so a virtual address that was located at physical address foo, if that goes out to disk and comes back, it's probably not going back in that same page. It's probably gonna end up somewhere else. To the process, it looks the same. To the system, it's in a different part of physical memory. Any questions about this? Okay. So earlier we talked a little bit about a technique that allowed us to prepare. I'm gonna clean pages before they are used. Sorry, before I'm gonna move them out to disk so I don't have to do the IO, that's a great idea. Now let's talk about sort of a complimentary strategy which is where it allows the operating system to avoid doing work that it doesn't have to do. So let's say I do something like run exec. Now, exec has this blueprint for how the address space is supposed to look. And that blueprint includes all of the code, has to include all the code that the process could ever execute. Everything it could potentially need, every function, every button that you click on, whatever, has to be there. And so one thing the operating system could do is just take this huge chunk of code and find a whole bunch of pages of physical memory and copy all of that code into the physical memory page by page by page by page. Essentially that's the impression that I've allowed you guys to have about how exec works. What's a different approach? How can I, so in that approach, if the process has 10,000 code pages in its code section, how many pages am I gonna copy and during exec? As 10,000 code pages, exec will need to allocate how many pages of physical memory to hold the code section, 10,000. I can't get away with it, I've gotta find a home for every code page. What's the problem? Yeah. I'm never gonna use all of them. Like there's code pages in there, there may be code pages that contain code that the compiler just couldn't figure out will never be executed, right? I mean, compilers try to do dead code elimination, but if you are good enough at fooling them, they can, there can be whole blobs of code in your project that will never, ever be executed. Sad, lonely code that will never, ever be executed. And you're gonna load that into the address space because hey, why not, right? I've got plenty of memory and free time. This is a fun exercise. What's a different way to do this? Yeah, yeah, I like lazy loading. I feel like the web has taught everybody about lazy loading, I'm so happy about that. Yeah, like why not just load it lazily? It's a, we call it on-demand paging, but you could call it lazy loading for the OS. It's a totally legitimate way to think about it. What the kernel's gonna do, so what do I need to do? I mean, I need to be able to find that stuff if you want it. So this is important. The kernel needs to make sure that if you try to run bin whatever, and I'm not gonna load it, I need to make sure that I don't lose those pages. Now one of the things I could do is on systems that allocate swap for every page of memory, I can put the contents in swap right away. Say no problem, I've got that page in swap for you all ready to go, you just tell me when you need it. And then, so now what happens? So I can complete exec potentially, and only allocate a couple of pages of memory to the process. Maybe like one for its stack or something like that. When do I have to allocate those pages of memory though? So I'm lazy, I don't do it up front, but when, how can the process force me to allocate those pages of physical memory? Let's say again, they're from the code section. What can the process do that forces me to bring those in? Yeah, I execute a code path that hits that page. If I do that, then okay, now it's gotta look like memory again and so now I'm gonna have to go get it. However, what results is that over time, the process gradually brings in pages that it needs and does not consume any memory for pages that it doesn't. Now it's possible, we'll talk on Monday about how we choose what to throw out on disk. It's possible that if I bring in everything, I still might move those inactive pages onto disk eventually, but this way allows me to make sure that they don't even end up in memory in the first place. Same thing with pretty much any kind of allocation that a process makes. If a process says I want four megabytes more heap, fine, I'm gonna give you permission to use that part of your address space, but I'm actually not gonna allocate any memory for it right away. I'll wait till you use it and see if you make good on that because maybe you have a buggy malloc library that really actually isn't going to use any of that. Or maybe you've just allocated a four megabyte array of which you are only going to use the first value. That happens, right? Okay, so in on-demand paging, we wait as long as possible to manifest parts of the address space, pretty much anything we can. We don't load that page into memory until we're required to and this allows us to avoid fetching or creating space and that's actually the big part. Not necessarily a question of loading it because if I move it to swap, I still have to do some IO. It's a question of finding the memory potentially, particularly on a system with that doesn't have much. So I think we just talked about this. Same thing, right? As soon as I touch things, I fill in stuff and with the heap, as we talked about before, new heap pages are filled with zeros. So the heap is even easier. With the code, I have to make sure that I have the contents of the code page somewhere. With the heap, I don't because heap pages start out as zero and so I can create heap pages, just like boom, magic, right? There's a heap page, yeah. Yeah, absolutely. So from your perspective, what's the performance difference? Let's say I gave you two systems. One that did on-demand paging and loaded things as lazily as possible and a second to load everything up front. How could you tell the difference? That's the easiest way to tell the difference. What experiment would you run? Yeah? Slower while the on-demand paging would be slow, really slow when you're trying to do the swapping but then faster when you're done. Well, both are going to slow down when you have to do IO, right? But what's the, there's a very simple experiment you can run to figure out which is which. Very quickly, yeah. Exactly, app launch. On an on-demand system, app launch is fast and you pay for it as you use the application. So as you do little things on the application, there's a little more IO, things are a little slower but it starts really quickly. The one that loads everything up front starts really slowly but then every interaction with the system is a little bit faster. Now, given that you are humans with incredibly well evolved but very slow, slow brains and nervous systems, which one do you think you appreciate more? Yeah, you want things to start up fast. You don't notice that there's like a little, tiny little delay because, you know, something had to be fetched from disk or whatever. You just don't notice that. You know, you're happier because it came up quickly and you could start interacting with it, right? Particularly on systems that suffer from memory pressure because if I have to actually move things out to disk in order to bring in things from disk, now things just got twice as slow. All right, so we will talk on Monday about page replacement policies. Have a great weekend.