 Oh, why that is? So today's the last lecture of pre-spring break. So we're going to finish talking about paging. And then last time, we had sort of got to the point where we could talk about other places that pages might go. So we'll talk about swapping. And then at some point today, maybe at 2.30 or so, I'm just going to run out of stuff to talk about and stop. And at that point, you guys are welcome to hang out, look at the periodic table, or you can leave. But it's a good stopping point right before break. I don't want to start talking about page replacement algorithms yet, because we'll do that after the midterm. So everything I covered today is fair game for the midterm exam, which is the Wednesday after we get back from break. So it's the last week for assignment two. You guys have a couple more days. I can tell that people are aware of that. So the midterm, again, will cover everything up through the end of today whenever that happens. No recitation Friday morning, no class on Friday. We will be, some of you guys will probably be in the lab in office hours. So we'll have sort of the turn in party. Mega office hours. I've asked as many of the ninjas and TAs that are available to come. We'll do that on Friday at noon, starting in and around my office on the second floor. So please come. My students and the ninjas will be around to help you guys out. And we'll try to get you as far as we can before 5 p.m. rolls around. Any questions about logistics of this? OK, we have a couple new members of the 8080 club. It's like every time somebody joins the 8880 club, their name gets harder to pronounce. So Harishankar, all right, and Aniket. All right, congrats, guys. And I'm not even going to try that one. Aaron Koo, is Aaron here? He was here last time, all right. So yeah, these guys are done. So there are now eight groups that have reached this milestone. Just pretty impressive, out of 64 groups in the class. And there are a number of groups that I know that are very close. I think if you look at the number that are above 75, it doubles. So you guys are doing great on the assignment. I'm pretty happy. Oh, wait, hold on. There's even somebody else. Kushboo, are these guys here? All right, congratulations. And ladies, Girish, all right, congrats. Yeah, almost forgot you guys. So yeah, three new members today. OK, so going back to Monday, so remember that we decided that we were going to try to strike a middle ground between being able to translate any virtual byte into a physical byte. And the problems associated with segments, and we chose a single translation granularity, which we refer to as a page. And pages were designed to be small enough so that there's limited internal fragmentation. So all the contents on a single page are sort of connected to each other in some way. They sort of all belong together. One part of one data structure, all the variables that are in use by a single function is it's executing a code path that maybe spans an entire page without any jumps or breaks. So this was good, right? And we chose 4k as the size. But we also wanted it to be large enough, so this was this tension here, to allow the TLB to translate a reasonably large amount of virtual memory without having to use too many translations. Because remember, the TLB that we're going to use to help us has this property that it doesn't scale while the number of entries goes up. So we can't make the TLB arbitrary large, so this is the two constraints. So these are what has pulled these things in this direction. So what is the property that I was just describing? What do we call that? Because there's something that helps us here. And that's the fact that process memory accesses are not sort of randomly distributed throughout all of their allocated virtual address space. What did I call this? What do we call this? Property. Yeah? Yeah, execution locality, or you can think of it as spatial locality, space with being within memory. So if you look at a running process and you look at its memory accesses, they're clustered in a couple of different places, not all in the same place, because it's using code pages and it's also using it's stack and it's maybe using the heap or something. But they're clustered in a couple of different places. And it's that clustering that allows a small cache, the TLB, to do a really nice job in terms of reducing the latency to translate memory addresses without having to use a large number of translations. So we chose 4k. And again, this is sort of a historical choice. I think there are much larger pages now. I should have looked this up. I know that there are these superpages on newer architectures that can be multi megabytes in size. And again, that sort of makes sense, right? The amount of memory on your system is increasing. And you can imagine. But given what we've talked about, I mean, we wouldn't really expect, for example, a single code path to go a megabyte worth of instructions without branching or doing something that would cause me to potentially not want to load the whole megabyte page. So what sort of things, because on some of these systems now you have the choice of page size. So what are some types of applications that might benefit or want to use a large page size? Yeah. Yeah, media, right? So when you start playing that song, unless you have some sort of disorder that you don't like to listen to more than the first 10 seconds of a song, you're probably going to listen to that song and listen to the next song. And parts of that song, if you think about MV3s, I think MV3s are like a megabyte per minute in terms of space. So if a program is reading those into memory to decode them, it may want to just read a big chunk of the file at once. And so I can put that media content on its own page. I page that into memory. And when the program is done with it, all of the contents are really not needed anymore. And I can remove them. We'll talk about how to do that today, the removal part. OK, so now when we're starting to talk about pages, virtual address is now split into two parts. So what are the two parts of the virtual address that I care about when I'm doing page translation? They call. You already answered a question. Anyone remember? Yeah. Yeah, so I have the virtual page number. And I need to be specific here because I also have physical page numbers, virtual page numbers, map to physical page numbers when the page is in memory. And then the rest I just call the offset because that's the offset within the page. Just set that. Great. So we talked about the TLB. We know that the TLB is what allows most, hopefully, most memory addresses to be translated without involving the kernel. But how do TLB entries get there in the first place? Not a trick question. Who knows the answer? Anybody? Yeah. Yeah, the kernel puts them there. DOS puts them. And when a process tries to access something that's not in the TLB, this generates an exception that the operating system has to handle. Either handle that one of two ways. Either I tell the TLB how to translate the address or an error has occurred and the address is not translatable. Or today we'll talk about something else that can happen, which is I might have to go do some work in order to make that virtual address into something that can be translated. We talked about what the OS needed to find. So last time we talked about paging data structures that the operating system uses to quickly locate information about virtual pages. What do I need to know about a page? The TLB says, hey, I've got this virtual address. I don't know what to do. I can identify the virtual page number pretty easily. And then what data does the kernel need to find? Need to locate something. Let's say it's the simplest case and this page is in memory. What do I need to discover? What do I need to tell the TLB? The TLB says, I don't know how to translate this virtual page number. I need to tell it. Yeah, so I'm going to tell it sort of implicitly. But again, assuming that the translation is valid and can proceed, what does the TLB want to know? Yeah. Just wants to know a physical page number. Just tell me the physical page number. And again, when you guys get to assignment 3, which I'm sure you're all looking forward to at this point, that is all you have to do for assignment 3. That's it. All you have to do is tell the TLB when it asks, where is a particular virtual page? All you have to do is tell the physical page number. That's it. Simple. Literally, that's all you have to do. It just takes a while. Just one function to write. So I need to be able to store information about this page. And we also suddenly need to be able to locate it quickly. Because at this point, I'm slow and I don't want to get slow. So the data structure that you guys that we sort of got to, finally, that was the most mature data structure. Systems people have a preference for very simple data structures. Probably notice that as we go along in the course. It's because we're like a little dumb. So we don't like the complex data structures. We don't like a Fibonacci heap or something like that. We just want something simple. Two-level tree. That's already a little taxing on us mentally. So the data structure we're going to use to translate paid addresses is a multi-level tree. On some systems, these are actually three levels deep. But let's just keep it simple, and we'll do two. We split the virtual page number to two parts, a top half and a bottom half. Again, we can split it in different ways, depending on the architecture. We use the top of the top 10 bits as an index into the first-level table and the bottom 10 bits as the index in the second-level table. And because of the sparsity, right? Because of the compactness of address spaces, I can potentially map a lot of virtual terrain with a small number of data structure, while still allowing me to look up virtual to translate virtual to physical addresses pretty quickly. So this is a nice approach. All right, questions about this stuff before we keep going? Today, we're just going to keep talking about how to do some of these things, so it's sort of a good review. We'll come back to this. OK, so at this point, we get to something where this gets sort of fun and magical, which is, so far, we've been talking about how to allocate and how to control the physical memory that's on the machine. But in a lot of cases, I want to give processes, be able to give processes the illusion that there's more memory available than there actually is. So I want to allow all of the processes on the system to overallocate memory. If I took all the virtual memory that was allocated by every process on the system and added it all up, it would be bigger than the amount of physical memory. So if all those processes tried to map all those addresses at once, I couldn't actually fit them into the amount of physical memory I have. And this can happen pretty easily, right? So imagine I'm running along, and a process starts running, and they give it a couple of pages for its code, and then a page for its stack, and maybe a couple of heap pages. And then later, it says, oh, I want some more heap, and now what do I do? So this is a very natural consequence of the way processes operate. And in particular, remember that in a lot of cases, there is potentially a lot of dead, unused, rarely used stuff inside a processes address space that has to be able to be in physical memory, but you may not want it to be in physical memory. So the idea of being able to overpermission memory is both an opportunity for the operating system to make more memory available to processes, than there actually is, and also a response of the fact that processes don't always do a very good job of using the memory that they ask for. So what can we do when we run out? Let's say we get to that point, and a process tries to allocate more memory. What do you guys do? So I should ask you this question. What have you been doing? You've been writing fork. Fork tries to allocate a lot of memory. So what do you guys do right now when this machine runs out of memory? What should you do? What's that? I mean, that's essentially what you're doing. You're going to fail. You're going to fail the system called fork. It's not going to work. And there's a bunch of different places where we talked about the different ways that processes can allocate memory through exec, fork, sbreak, and map, things like this. And so there's a variety of different places where, if you were required to back every virtual page with a physical page, you might have to fail. A process might try to exec. And the exec binary might take up more space than you have available, and you might have to fail. Or fork, fail. So you can just fail all these system calls. And that's a completely OK thing to do, I guess. A little unfortunate, particularly when you're talking about system calls like sbreak, because we talked about this before. I mean, what is probably going to happen if you decide to fail sbreak? Do you think the program is going to be like, oh, came out like return null? I won't draw that menu. I mean, something's going to fail. The process might crash. It might just not do what the user wanted it to. So usually, I don't want to do this. When I'm creating new processes, maybe I can get away with failing, because then it's like, OK, sorry. You have to kill some of your existing processes before you create new ones. But when a process is already running, and I've already made some investment in it and wants more memory, it's not a good idea to say no. I don't want to say yes. But the other option, which is more interesting, is I can create more space. And the way I'm going to create more space is I'm going to move some things out of memory to make space for the new allocation. So you go back to this figure. Instead of saying, no, you can't have more heap. I'm going to look at memory. I'm going to say, I need some more space. I'm going to grab a page. I'm going to throw it somewhere else. And then I'm going to give the page that I'm now freed to this allocation iconopathy. So and remember, just to sort of try to bang this home as much as possible, this all goes back to this idea that I've introduced a level of indirection. And I have this type of control. Because the fact that processes have to translate every address that they use gives me the ability to do stuff like this. So this is something else that we've enabled by, through virtual address translation. It gives me the ability to make these changes, like I said before, behind the processes back. As long as I follow some basic guidelines. So in order for this to work, so here's the deal. I don't want the process to notice. I want to be able to grab memory away from it and then put it back later. And I don't want the process to notice. Or let me be more specific. I don't want the process to fail. I don't want something to go wrong. I don't want anything to behave in a way that the process didn't expect. So what do I need to make sure I do in order to do this? What are the requirements for being able to take a virtual page away from a process and then give it back later? Yeah. That's a good question. So the process shouldn't be executing? So here's, well, we'll get to this in a minute. So I certainly have to be careful if the TLB, for example, already knows how to translate that page. Because in that case, I have a little bit more work to do. We'll come back to it. The process could be running. Again, it's possible that the process is your word editor and the system is gradually realizing that you don't use a lot of those menus and it's gradually taking things away as the process is running. So that can happen. But what do I have to do? Yeah. What's that? Right, so that's a great point. When I put it back, I'd better be sure that it goes back in the same place. That would be weird if suddenly something that I thought was at 0x10,000 is now at 0x12,000. I don't think a process would react to that very well. So that's a good requirement. What else? Yeah. Yeah, I've got to know where it went, but why? So I need to preserve the contents. So this is pretty critical. And this is something that you guys will do wrong. Guaranteeing for assignment 3. So if you take a page away from a process, it's like, it's OK. I'll give you this back later. But then you give the process some other page that has some other stuff on it. Not going to work out very well. The process is like I wrote down some really important stuff there. You took it away and you give it back to me and it's all full of gobbledygook. I don't know what this is. This is in the same page that I gave you. So the way I put this is the last time the process uses the page it needs to behave like memory. And the next time the process uses the virtual address, it has to behave like memory. In particular, it has to preserve contents in the same way that memory would. So if the machine reboots, then all bets are off. I don't have to give you back the same page. I don't even know what we're talking about. My memory's been totally wiped. But as long as the process is running, when I take a page away, I need to make sure that when I give it back, the page is in the same condition as it was when it left. And that means I have to preserve the contents of that page. So in theory, you can imagine putting those pages. The question is, I need to move them out of memory. So where are they going to go? I could write them down on a piece of paper and then copy them back into the terminal prompt later. I mean, you could do that. That would be, I think there's an RFC for people that implemented the IP protocol using carrier pigeons. You can do that. It's slow, so I wouldn't advise it. But I need to record the content somewhere. The typical place I do this is on disk. So I take the page contents. I write them down somewhere on disk. I make a note of where they are. The next time the process needs the page, I need to move them back from disk and find memory for them. And we call this process of moving data back and forth between memory and the disk swapping. Keep in mind, there's a lot of times that the OS is moving data back and forth between the memory and the disk for a variety of different reasons. For example, data from a file. Or when we get to file systems, we'll talk about file system caching, but swapping refers specifically to the process of swapping memory. So a page of memory that was allocated by a process gets moved out to disk in order to create space and then later when needed, it can be moved in back again. Now here's the problem. The problem is that memory is fast. Really fast. Not as fast as the processor, but whatever. There's only one processor, it's really expensive, it only has a couple registers and a little bit of cash. So memory is this, I have a suggestion for you. How many people have ever had a relative that's come to them with some sort of computer problem? My computer is slow, whatever. Let me tell you what to do. Put more memory in the computer. That works every time. I guarantee it. It is the easiest fix-all for any computer-related problem. Just put more memory in it. If the computer already has 128 gigabytes of memory, then you're in trouble. But most of the computers that your grandfather asked you about are not gonna have 128 gigabytes of memory. They might have 128 megabytes of memory, in which case you can make a big difference. But I promise you, this will make your family members happy and it requires very little thinking on your part. Just buy more memory. Memory is really fast and swapping when done poorly will make the system seem really slow. But the overall goal of swapping is to make the computer feel like it has potentially as much memory as the amount of memory and actually has plus the entire disk. But then all of that storage space that processes can pretend now is memory is actually as fast as memory. Now, if you don't do a good job of swapping, and we'll talk about what we mean by doing a good job and algorithms for doing this when we get back after the midterm, if you don't do a good job, what you can end up doing is causing the computer to feel like memory is now as slow as the disk. How many people have ever had to deal with a system like that that's just totally locked up for some reason? When your system is locked up, again, I will guarantee you the reason is because of IO. It's always the reason. Whenever your system seems slow, 99% of the time, it's not because the CPU is too busy. That's very, very rare. It's more because the disk is sitting there pegged. Can you still hear? It's too bad now with these flash drives. They're so quiet, right? But those old spitting disks, it was like your computer would slow down and you could hear it going. You know, and you're like, ooh, I don't know what's going on, but that was usually a bad suck. That usually accompanied a lack of responsiveness, shall we say. And again, we will come back to algorithms for doing this, but today we're just gonna talk about the mechanics of doing this. Now the fact, now that we start moving things around, but actually, so just let me make one more note, but again, these pages could go anywhere. I could swap the pages across the network. I could put them anywhere. They just have to be somewhere that I can get them late. Okay. Now that I'm moving pages to other places, I have to distinguish between two different types of paging related faults. So what we've talked about so far are TLB faults. TLB faults are created when the TLB doesn't know how to translate an address because you haven't told it what to do. So that's a TLB fault. A page fault is created when the contents of the page are not in physical memory. So TLB fault, TLB needs help, translate an address, page fault, you put the page somewhere else. It's not even a memory anymore. If you have implemented your system correctly, every page fault is preceded by a TLB fault because once you have put the page somewhere else, the TLB cannot translate virtual addresses to offsets to the disk. It doesn't know how to do that. So in order for the TLB to translate the address, it has to be a memory. Therefore, if you moved it out of memory, you sure had better tell the TLB that it doesn't know, you sure better remove the translation from the TLB so the TLB can't continue to translate it. So right, if the contents of the virtual page are not in memory, there can't be a translation for it. Now on the other hand, not every TLB fault generates a page fault. Why not? Oh, okay, so there could be times when I'm creating pages on demand, that's true, but what's the more common case? Yeah. Yeah, the page is a memory, right? Just because the only time the TLB doesn't know how to translate a page is not because I moved it to disk, sometimes it's just because I switched between a couple of processes and so I cleared the TLB, other times it's because the TLB ran out of entries and so I was forced to overwrite one of the entries I already loaded for a process, so something else. So hopefully, right, I mean, this is what we want to be the common case down here, our TLB faults. Page faults, it can be more problematic because as you probably guessed, they can be a lot more difficult to handle and a lot slower. So now let's talk about the two processes, the two things that we have to do in order to be able to swap. We have to be able to swap pages out, meaning move them from memory onto disk and then we have to be able to swap pages back in. So in order to swap out a page, what are some of the things that I have to do? Already mentioned a couple of them, yeah. I have to remove the entry from the TLB, absolutely. What happens if I don't remove the entry from the TLB? Process can continue to use whatever that TLB entry points to and usually the reason I'm swapping out a page is because I'm about to take the physical memory and use it for something else. So if I don't remove the entry from the TLB, what normally happens is I have unintentional page sharing that starts happening between two processes which usually leads fairly quickly to some sort of disaster. Not something I want to happen, okay. What else do I need to do? Remove from the TLB, it's a good start. So I have swapping out to disk. What else do I need to do? There's a couple of other steps. Remove the translation, copy the contents. Not, oh hello, there's money on the floor, all right. Copy the contents to disk, right? I need to actually move the data, otherwise it won't be there when I need it. And the final thing is I need to update my own internal data structures because at some point in the future I'm gonna have to find this page again and figure out where it is on disk so that I can copy the contents back in the next time the process tries to use this page. So here's my clever diagram for doing this. Here's my address space. Imagine that these are the entries that are currently located in the TLB. I have a code page that I'm about to swap out. The code page is loaded in the TLB which has mapped it to some physical page in memory. And my page table entry up there also notes the fact that this particular page is located in physical memory. The first thing I need to do is throw this out of the TLB. Next I'm going to start writing it to disk. Now let me ask another question. So what would happen? Is it okay to write it to disk first and then remove the entry from the TLB? Is that okay? Does the order of operations here matter? Somebody is nodding, why? What can go wrong if I don't remove it from the TLB first? I mean I'll get around to it, come on. I'm just gonna write it to disk first because that's the slow part and then I'll get around to removing it from the TLB later. What's the problem with that? Yeah. Well the writing's not gonna be interrupting, right? But what's gonna happen is it's possible that the process is gonna continue to use this page. And so when I'm done, what I want to be true is I wanna make sure that when I'm done writing the page to disk, the contents and memory are identical to the contents on disk. If I allow the page to continue to be used during the write, then it's possible that the page has been altered during the write and so what I have on disk is some sort of weird, monkey portrait of the actual page, right? Like not a genuine copy of the page, some sort of muddled thing, right? Which is not what I want. Okay, so I write it out to disk and then the final step is I need to make sure that I update the page table entry with whatever information. This becomes very dependent on how exactly I set up my page file on disk, but I need to update it with some sort of information that allows me to find it later. Okay, so when I think about this operation, removing it and I think about sources of latency, okay? Why would I be particularly concerned about swap out and swap in speed? Particularly for swap out. Why am I worried about optimizing this operation? Yeah, keep in mind, why am I doing this in the first place? I'm doing this because something needed memory. Fork, exec, sbreak, whatever. And I didn't have memory to give it and so now I'm in the process of making memory so that I can allocate some memory to that process and that process that originally requested memory, it's just like sitting there thinking when am I gonna get that memory? All I did was sbreak and I just asked for another page of heap, this is taking forever. So this is something that if it's slow, it's gonna slow down other processes out in the system. Okay, so, but if we look at each component I just gave away the answer. So removing the translation to the TLB, not an expensive operation, something I can do pretty quickly. What about copying the contents of the page to disk? Fast or slow? Slow, right? The disk is slow. If the disk was slow as memory, I'd use the disk as memory and I would call it a day. It's not, right? So this is the slow part and then updating the page table entry pretty fast, right? So, and like I just said, usually when I'm doing this, I'm doing this in order to give a page to a new process. So this is an entirely true, what would be a smarter thing to do? If I don't wanna get stuck in this place where somebody asked me for a page or the user tried to create a new process and suddenly I've got all this IO going on because I'm trying to create all this space in memory, what would be something that there's a couple of ways that the kernel can optimize this process? Yeah, I do that cache. Where is this cache that I'm gonna put it on to? That sounds like memory. I'm gonna use that as memory, right? Okay, any other ideas? There are a couple of things I can do here. So again, what I don't want is I don't wanna get wedged into a corner where I have no free memory. So what can the system be doing sort of constantly? Because here's what happens, right? You sit down at your computer and you suddenly try to do something incredibly moronic, like launch a program, right? Like Photoshop, for example. I mean, this is like Windows worst nightmare, right? Somebody launches Photoshop because it's like, oh gosh. This is a huge program, right? It's not really complex, all these libraries and you just sit down and suddenly you hit, I just wanna launch Photoshop, right? It feels like something I could do. And suddenly the system has to make all this space for it. So to prepare myself for that moment, what could I do? Yeah, so there's a couple of things, right? One is I can clean the system. I can clean pages on a regular basis. What this means is the pages that are in memory, periodically as the system is running, applications are making changes to those pages. If it turns out that when I go to swap out a page, if I can find a page where the contents of the page and memory are identical to the contents of the page on disk, then I can skip this whole process of writing it out. So what I do periodically, particularly when I feel like I have some downtime, like I can tell that you walked off to grab a cup of coffee or something or the server load is a little bit lower than usual, is I go through memory and I find pages that don't match their contents on disk and I write them out to disk. I'm not swapping them out. I'm not removing them from physical memory. I'm cleaning them so that when I need a page, those pages are available. So this is called page clean. And we distinguish between pages that match their spot in the swap vial, which we call clean, and pages that do not, which we call dirty. The other thing that I can do that a lot of systems do is I can just try to keep some completely free memory around all the time. And the way I do that is rather than waiting for this emergency when I run out of memory, I have some target about the amount of available memory that I want to preserve. And I'm always sort of going around to processes and be like, by the way, you don't need that. That piece of code, you haven't used that for a long time. I'm gonna take that from you and I'll put it on disk so it's safe, but now I've created a little bit more memory. So a lot of times, operating systems, particularly on interactive systems, will try to just make sure that they have a little bit of buffer so that when you come and sit down and launch some big application, there's enough room to just launch that right into that free memory without really having to do any work. So in Windows, there's this concept of a working set, which is the pages that the OS thinks the process really needs to have in order to make forward progress. And the algorithms are constantly sort of trimming processes back to try to get them to just their working set and nothing more. But we'll talk about this more in a few weeks. Okay, so now let's say that we've successfully moved the page to disk and, but now I need to bring it back. So what's gonna trigger this? This is important to realize. I've moved the page to disk successfully. What is going to trigger? When do I need to swap in a page? So here's a good question. When's the, ideally, when does the process need, when does the OS need to swap in a page? What's the best time to have to swap in a page of virtual memory? Never, right? That page is never used again. Hallelujah, I will never see it again. Most pages, on the other hand, at some point it's possible that the process is gonna want them. What triggers that? How does the operating system know that a process wants to use a particular page? How do I know when I have to go get something from disk? Yeah. A page fault, right? The process tries to use memory. And again, it was completely okay for the process to try to do that. So when we come back and we talk about how we choose pages to swap, just keep this in mind. Because on some hand, this is even worse than swapping out. With swapping out, I could prepare myself so that I can make the swap out process pretty fast. With swap in, it's a lot harder to do. Because what I would have to be able to predict is that the process is gonna need to use a page of memory. In a lot of cases, that's extremely dependent on you or some other events that the operating system can't really predict. So suddenly, I get this request for a virtual address and keep in mind, the process is just thinking, oh, this is just memory. It doesn't know that I've put it on this really, really slow disk and I have to go get it, right? So when the virtual address is used, we have to swap in. And so now, what do we need to do to swap in a page? What are some of the steps that we're gonna have to take? Okay, so that's like the last thing I want to do is update the TLB, eventually, right? So remember, what triggered this was a TLB fault. There was actually a page fault that turned into a page fault because the page wasn't in physical memory. So you're right, at the end of the day, I still need to update the TLB because this is a TLB fault and the TLB is sitting there waiting for me to tell it what to do. But what else do I need to do? Yeah, yeah, so I might, so, well, okay, so maybe this is obvious. The first thing is that the instruction that's trying to translate the address has to be stopped until I can figure out what to do. So this is what triggers the TLB exception. Here's the other problem, right? So here's another reason that I might want to have some spare memory line around. In order to swap a page in, I need a free page. So in sort of the worst case, bad case situation here, I might actually have to swap a page out in order to swap one in. And then I'm doing, then what a process innocently thought was just reading a byte of memory now involves eight kilobytes of IO to the slowest part of the system. So this is like disaster. I do not want this to happen, okay? So I need to locate the page. Hopefully I took no good notes in the page table entry about where I put it so that I can find it. I need to copy the contents, update the page table entry so that I know where the page is. The next time somebody comes looking for it and reload the TLB. And then at that point, I can restart the instruction that was trying to access memory. When I swapped out, there was no such instruction, right? But when I'm swapping in, the only reason I'm swapping a page in probably, unless I'm doing something clever and predictive is because somebody tried to access it. So there was an instruction that was started that needs the contents of that page. And this is what triggered this whole process. So here's my funny diagram again. So I've got my page on disk. The first thing that happens is the process innocently tries to read from some code page. That code page cannot be located in the TLB. Has to generate a TLB exception because it's not in memory so there's no way for there to be a valid translation in the TLB. Now I look up where it is in my page table. I have to find another page of physical memory. Copy the contents in. This takes a while. Update the page table entry so that I know where the page is. Reload the TLB. And now the instruction can be restart. Any questions about how this works? If you understand swapping and swap out, you understand paging because you need to use all of the system features and the data structures that we've built in order to get this to work. And this is something that you guys will understand much more intimately after in about a month. So let me talk about a couple more things before we wrap up. So one of them is this, and this is another fund system design principle which is that most computer systems try not to do things they don't need to do right away. Unfortunately, the same tendency that causes people to do really poorly on assignments for this class is a technique that operates systems down to employee. Now here's the difference. So with you guys, you know you have to do assignment two. It's not like if you keep waiting, assignment two is gonna go away. With the operating system typically when I'm procrastinating, the reason is I think I can get away with not doing the thing at all. So it's possible if I just wait, the process will sort of forget about that, never use that piece of memory, and I won't actually ever have to do that thing, right? I'm sure you've had cases like this where you have a friend who's been bodying about something and you just wait them out and pretty soon they just forget. And then you go on with your life. So here's an example. The process is starting up during exec and it's got this massive blob of code. And it says, dear Colonel, I would like all this code put in my address space at this starting location, thank you very much. Now what does the Colonel suspect about this code segment? And let's say this is Photoshop. Got like a gigabyte worth of code that I need to plop somewhere in the address space. The Colonel's thinking, I have a hunch about this. What is the hunch that the Colonel might have? So in theory, the Colonel could go get this code from the executable and copy it all into the address space just like the process wanted. But instead, what am I going to do? There's only two options, right? I can either do what the process told me or I can not do it. So the Colonel might say, you know what? I'm gonna write down what you told me to do. I'm gonna take some notes about that thing that you said about the code segment and all that code you wanted. But I'm not gonna do that right now. Same thing with allocations to the heap. So this is another classic example. Malik might say, I want this much more heap. And the Colonel's thinking, no, you don't. You don't really know. Maybe you think you do, but who knows, right? I mean, you may not allocate any of that. You may be about to exit. So I'm not gonna give you this much heap. I will give you permission to use more of your virtual address space as heap, but I'm actually not going to back that additional virtual addresses with anything in physical memory. So the overall, we call these strategies on-demand paging. And on-demand paging consists of allowing the process to manipulate its virtual address space without actually backing those manipulations immediately in physical memory. So going back to the code example, let's say the process told me to do this thing. And you know what, I have to admit, I feel a little guilty, but I didn't load all that code. But when do I actually need to load the code? What's the point where I can't get away with this little charade anymore? I've been called out and I have to do the thing that the process wants. What's the moment at which the jig is up? Yeah. Yeah, when it starts executing parts of the code. So at some point, and of course this is gonna happen right away, right? But at some point, the process is gonna actually start fetching and decoding and executing instructions. And at that point, I need to go and get those instructions from whatever place I noted down they were supposed to come from. So until the process starts executing instructions from a particular page, now this is the big difference. The process might have asked me for a gigabyte worth of code to be put somewhere in its address space. But it's executing code one page at a time. So what's the trade off here? I've gotten away with not copying this huge blob of code. But what is the overhead that's associated? I mean, this is not free. So what results from on demand page in this particular case? Yeah. Right, so as the process starts to execute. So for example, if during exec, I copied all the code in one big blob, then as soon as the process started to run, it would be pretty confident that it wasn't gonna generate any page faults. If I don't copy the code, what happens is as the process begins running, it's generating a lot of page faults because again, it told the kernel to load the code and the kernel didn't. And so every time it gets onto a new code page that it hasn't used before, the kernel has to copy that code page into memory and restart that instruction. So it's almost like swap, swap in. Except in this case, the source is potentially the executable file itself. I know how to parse the alpha executable and get that stuff. So when the process faults on an address in its code space, I just go out to the L file, figure out what's supposed to be there and copy it. Same thing with data pages, heap, stack. Extensions to my stack don't have to be back with physical memory. Immediately same thing with the heap. I give the process permission to use those virtual addresses. I make them valid, but I wait until the process actually uses them to fetch physical memory in order to make them look like them. In the case of the heap, so in the case of an executable, I had to keep track of what content was supposed to be there because there was actually content that was supposed to be there. With the heap, it's even easier. So with the heap, what do I do? The process comes to me and it says, okay, I want four gigabytes, well, not four gigabytes, four megabytes more heap. And I say, okay, fine. I don't do anything about it. When it faults on an address inside that new region, what do I need to do? What is, it's the first time that it's used a heap page. What is inside that heap page? Yeah. I could give it anything. That's true. I could just give it random stuff. It turns out that it's a little safer and potentially an equal amount of work. So one thing I try not to do is I try not to give the process a page that was used by some other process because by doing that, I'm leaking information from one process to another that I don't want to. So I'm going to initialize the page in some way and what am I gonna initialize the page to? Zeros. I'm just gonna, it's called zero fills. So I'm gonna give it a page that I filled with zeros. Now you're right. If I wanted to be funny, I could fill it with random bytes and just see what happens. And probably what you'd find out is there's a lot of user programs that break because they assume that you put null in there. And maybe that's even in the interface description. I don't know, but it would be funny to see what happens. Okay. And the reason for this of course is because if I don't load these pages right away, then I never have to unload them. So I have two strategies. I can let the process fault the pages into memory it's actually going to use, or I can load everything into memory first and then I have to throw things out of memory later as they're not used. Okay. So I did this. I did this. Where are we on time? We are done. I will skip this aside. It's on the lecture notes. So good luck wrapping up assignment two. And we will talk about page replacement algorithms starting two weeks from Friday. So I hope you guys have a great break and good luck finishing up the assignment. Bye.