 All right Welcome, so we did quiz two and now we're at the home stretch where today's lecture should be fairly easy So this should be all We can take a deep breath and we can learn what an MMU is today Cool Which will take like two seconds All right, so today we're going to start talking about virtual memory So remember what? virtualization is and virtualization is just the concept of fooling something into thinking it has all the resources So if we have two processes like Libra office and Firefox, they both think they have all the memory and They can have access the same virtual address which corresponds to a different physical address and The kernel is free to do whatever it wants with the mappings that can do something smart like making sure that for example Lib C, there's only one copy of Lib C in physical memory and Wherever Libra office thinks it's loaded and Firefox thinks it's loaded actually point to the same physical Addresses and it just shares by default and you only have to load one Lib C instead of like 200 of them So if we're designing and trying to create a virtual memory system This is kind of our checklist of things we would want We want multiple processes to be able to safely coexist We want processes To not be aware whatsoever if they happen to be sharing virtual memory So everything should be nice and transparent and then processes shouldn't be able to access each other's memory Unless you explicitly allow it so we can do something like explicitly share memory between processes to get some IPC between processes, but by default that should be an opt-in thing and By default everything should be completely independent You also want performance as close as possible to you actually using physical memory otherwise you would probably just suck it up and take all the drawbacks with actually using physical memory Because if it was twice if using virtual memory was twice as slow or ten times as slow Then no one's gonna use it So you want performance to be as close as possible and we'll talk about that in the next lecture and Then the last thing is you want to limit the amount of Fragmentation and that is a new word that will revisit a few times as we would talk about memory allocation and stuff Fragmentation is just the amount of wasted space. So if I allocate, you know an Integer and then something else that's bigger and they have to be aligned that like 16 pages I might have a gap of like 12 bytes that I can't use and that would be an example of fragmentation But don't worry about we'll visit it again As we talk about allocation So now the MMU this is what it is so all it does is map virtual addresses to physical addresses and Also does some basic permission checks like for example if I try to write to some memory I might not be allowed to write to it or I just might not be allowed to read it or I can't execute code from it so the MMU would do all the translation for you and Check all the permission bits and that would be done in hardware so one technique we have is to To actually do this translation if we had to do this translation for every single address like every single byte It would be horrendously slow so what we do is we divide memory up into bigger blocks and they call them pages and Each page is typically 4096 bytes which is a nice, you know power of two and It's been like this since like the 70s So are your Linux system your Mac system your Windows system? They all use this page size still and it still works quite well so we there is You can adjust it a little bit depending on the hardware, but for the most part that's our page size and that's like The unit that your operating system deals with memory in so whenever I create a buffer I made it this size because I know that the Linux kernel likes things to be that size and Typically deals with memory in blocks of that size So that number is going to be an important one to remember. It's 4096 or 12 or 2 to the power of 12 So there's some kind of weird terminology here, so This fixed-size page is Called a page Generally if you're talking about virtual memory It's called a page and if you're talking about that same block of memory as a physical page Sometimes they just call it a frame or a physical page, but they all mean the same thing But by default page generally means a virtual page, but they're all going to be the same size Okay, so now we can talk about Segments, so this is like a very coarse-grain way to do virtual address translation so you divide the virtual address space into kind of different Blocks of memory that might have different permission so you know need to live somewhere different so you might have one segment for code which is anything you need to read and execute and then just Global data that is loaded in with your program that you probably At least need to read from but you might need to write to and then stack in the heap Which is all read write and it kind of looks like an elf file image where there's just sections of code And it says hey load this these are the permissions you can read and execute this So with this you have a limited number of segments in this example There's four and they're each variable size and you can dynamically resize them and this is like an old thing That's no longer used whatsoever, but operating systems like to talk about them for historical reasons So they can be large and really costly to relocate so if you have two sections that are right next to each other and You want to make the first section bigger so it might overrun the next one Well to do that safely you have to take that whole section you have to move it over you have to copy all the memory and then you're allowed to make that other section big and You have to be responsible make sure things don't overlap and it's just a huge mess and it's kind of terrible So it's no longer used in operating systems, but we get a fun vestige of it where The segmentation thing Whenever you access invalid memory and you get a segmentation fault even though there are no Segmentations that that's why it's called that so historically it was called a segmentation fault Even though it doesn't use segments anymore, one of you access invalid memory still called a segmentation fault for Historical reasons, but so that's whenever you access a null pointer or do something you shouldn't have so that's where that comes from So just so you can see what it looks like so each segment generally has an entry that Corresponds to where it is in physical memory so it will have a base address and then a Limit to say how big that segment is and then all the permission bits and then whenever you want to access a physical Address your virtual address looks like this where it's just a segment selector and an offset So it's like I want to access, you know bite 255 of segment three or something like that Then what the MMU is going to do again remember it's responsible for doing the translation It's going to check that your offset is within the limit for that segment So that it is valid and it falls within the side basically like an array bounds check And then to calculate the physical address it will just add the base address to that offset and then do the do the permission checks and If it fills any of these steps That is a true og segmentation fault and not like the reuse term that all of our programs have done at one point or another So for example if you have this as your virtual address where you say I want segment one and I want offset Ff well first it's going and the segment One has a base of two thousand and a limit of one ff. Well, it's going to check to see if the size is within so Ff is less than one ff. So it is within bounds. So to calculate the physical address It's just going to add the base to the offset and then that corresponds to the physical address to zero ff Pretty simple. Yep. Yeah, so under this it's all contiguous Yeah, yeah when we get to the page table, it just deals with pages. It doesn't care. Yep Yeah, so we'll see that so that this is like the old historical stuff Just so we get a taste of what the hell a seg fault is because it makes no sense without this But so in order to disable segments, it's like some old code decides to use segments Well links it looks curl developers are pretty smart and to disable it They set every base to be zero and then the limit to be the size of the memory. So Everything just maps one to one. So it they essentially disable it because your x86 hardware is still going to support this for some ungodly reason because they can't take it out. So They're smart and they essentially just disable it. So all of our systems are pretty much 64 bit machines Which means you can like support up to like 16 exabytes of memory, which no one has So you can actually do something a bit smarter when we do we can put aside some bits We're not going to use all that memory anyways When we do our address translation so CPUs have kind of different levels of virtual addresses how big they support and what it maps to and Conceptually they're all the same. It's just the numbers are going to change slightly so For this example, I'll just assume that we're using a 39 bit virtual address Which is used by risk 5 and some other architectures like arm uses it I think you can optionally use it on x86 But this allows for a good balance where it the longer the address is We'll see later that the longer it takes to translate it. So generally if it's big enough you say, hey It's good enough. So this allows you to address up to 512 gigabytes Of memory which is a lot of memory that's more memory than I have but you know, eventually you'll have to adjust this so how this is implemented is with a page table and it is literally just a lookup table so it's indexed by the virtual page number and You just look up the virtual page number and within the page table entry One of the things in the entry is going to be the physical page number So if you want to translate a virtual address to a physical address You just replace you look up the virtual page number and then replace it with the physical page number And then suddenly you have a physical address so And we'll see an example quick, but this is basically what it looks like so our virtual address if we are using a 39-bit virtual address it's going to look like this and remember our page size is going to be 4096 so that's to the 12 so we're dealing in pages of that size So we're not going to translate the lower 12 bits because that's the size of the page so the offset would be between 0 and 4095 so we don't translate that that's within a page and It would be the same, you know We line up everything by pages so if in our 39 bits we use 12 for the offset it means we just have to translate the 27 bits Yeah, so we would use the 27 bits for our index and that would go in our page table So our page table would have two to the 27 Entries and then each entry in this case each entry is actually Eight bytes so we know how big our page table would be so it'd be two to the 27 entries times two to the three Bites which seems pretty big because it would be two to the 30 Yikes, but we'll get into that Okay, so let's see how you actually do this translation. So we'll just assume that Our page size or sorry our page size is the 4096 so we don't have to touch The lower three bits and we'll just have four entries in our page table So let us go back here and see so this is going to be our page table in the blue box here So we're just going to have three entries in it. So entry zero one two and three and Then they're going to correspond to physical page numbers and They don't necessarily have to be contiguous. They can be all over the place. So if we go ahead. Yeah The segmentation is Yeah page tables are still used not this because this has drawbacks that we'll see but at the base base of everything This is basically what happens So we'll see what really happens next lecture, but if we want to do Translation of these then well if I want to translate, you know zero a b zero well luckily enough each hex character is four bits and My page size is to the 12 so the last three hex digits are all part of the offset So I don't have to touch them whenever I translate So whenever I translate to from virtual To physical I can keep the offset the same because we're not touching that and Then I look up the Here this is the index So the index is Zero so I would just look up in my page table here and then find out a zero zero maps to physical page One so I just replace zero with one So that would be my physical address yep, it would be if your page size is 4096 So in some questions they might be we'll see later. It's like hey the size of the page is now this and it gets more annoying Yep, so the MMU is doing the translation the kernel is managing the page tables So it's doing the translating, but you have to Do the mapping yourself Yeah, so the kernel is managing the page tables and the MMU is just dumbly using the page tables and doing all Translations for you So in this I don't say how big a virtual address is for this Virtual address could be like 14 bits. So it just takes up to So yeah for this I shortened it because like we'd have like 39 a real one, but I don't want to write 39 characters or 39-bit address Yeah, yeah, so in this The amount of memory your process has is how many like valid page table entries it has so in this case if our page table is only four and They were all filled up and all use that means our process because can use four pages Which is what like 16 kilobytes, which isn't that much. Yep Okay, so Anyone want to translate? One f a zero for me it would be four F a zero. All right, everyone agree Because we took the offset Left it the same and then when we looked up zero one It was this page this physical page number and we just We just swap them Okay So what about two eight eight four zero in zero three eight eight Four okay, what about three two D zero and seven two D zero and This is basically how it works So it's just a simple lookup. Yep Yeah, sorry So the MMU just uses the page table. Yeah Yeah, yeah, the curl manages the page table So it would you know the curl would load things into physical address and then if that process says Oh, I want my entry address to be 2000 something like that it would make sure that that virtual address maps to that physical memory when you want to access something it's just as As part of your process each process will be using a page table So somewhere in your hardware Generally, it's like a register that points to the page table and you just swap that with the process And then the hardware just takes care of the rest So it will use that page table whenever that Process accesses memory Yep So the kernel manages the process is page table And it would be protected and all that stuff So that's like one of the core jobs of a kernel that nothing else gets to do Only the kernels allowed to modify page tables user processes can't begin to sniff it Because it you're only allowed to do that stuff in kernel mode So even if you have a small micro kernel the micro kernels going to manage virtual memory and manage all the page tables Okay, any questions about our fancy lookup table. Yep. Yeah. Yeah, it's all all in kernel The page tables are all in kernels magic or kernel space. They can't touch them and it just The kernel would be responsible for setting up that page table to point to the right thing when it switches processes And then hardware is just going to do all the translations and If you do something you're not allowed to the MMU is going to generate a page fault and then you get a Oddly named segmentation fault even though they don't exist when really it's a page fault Yep Yeah, so the question is is there a shared address space between processes so you can opt into shared memory where When before you fork you can set up a region of memory that's you know You indicate to the kernel is shared and that whenever you fork it shouldn't be separate It should actually use the same memory. So you are allowed to do that Yeah, yeah, everything's gonna be going through the MMU all the translations done by the MMU and all the page tables are managed by the kernel So as long as your kernel doesn't have a bug you're good Yeah Mm-hmm. No, it's virtual memory. So your process only uses virtual memory. Yes Yeah, so yeah, that's a good observation that this page table here would be on a per process basis So this could be you know This could be process One and then process twos page table it would have all the same virtual page numbers So it would be you know one two three Four and then here say let's go five six Two and zero so now These are all four Process One and this would be for process two So now if we translated the same virtual address of like zero a b a b zero in process two Well, it would get the physical address Five a b zero So each process would have its own page table and then the kernels responsible for doing all the mapping So it has to keep track of things if it wanted to share memory between the processes well then instead of Mapping to different physical page numbers. I could do something like this So they translate to the same physical address, but to have sharing be safe You typically would make sure that that page is read only Otherwise, it's kind of the same idea as a race condition One would be able to modify the other two and there wouldn't be that clear independence between them But that's basically how it would look Yeah Yeah, yeah Yeah, you're getting getting ahead of us. Yeah. Yeah. Oh the Ford Yeah, yeah the first two bits Yeah, the the three hex digits at the end are just the offset because they fall within the page so Page is four thousand ninety six. That's two the twelve. So three hex digits Which makes it easy. So we'll see when it's not nice, but when it's not nice, we all know our binary, right? No Okay, well, everyone should practice their binary. It's all good stuff So here's going to be our problem where it's not too nice. So this Questions like this will basically give you all the details They'll say how many bits are in your virtual address? How many bits are in your physical address and what's the size of a page? So For this it's really good to know your powers of two off the top of your head Like a good one to know is to the five is 32 and then based off that you can make steps and kind of easily go to other ones so If you have a eight-bit virtual address that means you have two to the eight possible Entries there that's two to the eight bytes you can address and if you have a physical address of ten bits And that's two to the ten and then if each page is 64 bytes What's two? What's 64 and two to the power of something? Six, okay, so knowing that and how to do math with exponents How many virtual pages are there? so What is how many bytes it can we represent with virtual addresses if there are eight bit ones? Two to the eight, right? So Yeah Yeah to the eight divided by two the six so to the eight because that is how many Virtual addresses in total we have and then each page is 64 bytes So we just divide total by the size of a page and that's how many entries we should have Yep, so this is eight bit which can represent to the power six bytes because we're assuming byte addressable systems We've only seen byte addressable systems So to the eight bytes in total divided by to the six bytes for a page Means we can have to the two or four total pages Everyone agree Okay, well then how many physical pages are there? Yep for pages Yeah, how many physical pages there would be? four 16 right So 16 because for a physical address there are two to the ten of them to the ten possible bytes And then it's just divided by the number of pages again so What yeah So that's a that's a good question So if I don't have if I have more physical addresses or physical pages and Virtual how can I map them all anyone want to take a stab at that because that's a really good question? sorry you You each process could use it a but how many processes can we have? We might have lots of processes So all across all the processes they could actually use all the physical memory if it's bigger so Right, so in this case We'd have 16 physical pages so to the ten Possible physical bytes we can address divided by the size of a page So that's to the ten divided by two the six which would be two to the four which is 16 so that means We could have four processes and then four processes could each have four page tables And they could fill up all the memory it would be slightly weirder if you do the other way where you have more virtual addresses and physical addresses because Then you suddenly you still have more virtual addresses you should use but you ran out of physical memory a long time ago Or can't support that much physical memory Yeah So when you context switch the process it would swap the page tables to I mean the night but No, so it's large enough that there's each process would have four virtual pages that can use and There's 16 possible physical pages So then each one would have its own page table and then just do that normal mapping So it'd be like the same Thing as before actually that pretty much exactly lines up So in this example where I wrote out two page tables There's two processes that had full page tables But there is no collision in all the physical addresses so Yeah, so they're using the same virtual address, right? So If I translate zero a b zero For process one it's going to be a different physical address and if I translate it for process two Because it's using a different page table So with an 8-bit virtual address and a 10-bit physical address I could actually support, you know Four full processes and give them all memory Okay, good question. So I go back So kind of jumped the gun But how many entries in the page table it will always be the same as the number of virtual pages because you have to look them up, so there's just four and then The next one that is as hard as it will get today is Given the page table This so this is just a shorter way to write the page table. So it's just index So this is index zero index one index two and index three So those would be the virtual page numbers and then these are the physical page numbers So in this case it asks you to translate the physical address of f one one So if we go to that Yeah, so who knows what it is what okay, so So it is two Zero f1 Yeah, anyone agree sag fault This is mapped it's not a sag fault. We can look it up. Why why isn't this map? Why do you think it's not mapped? Yeah, so this is So these are our virtual page number so It's not mapped f isn't mapped where do you get the two zero from? Yeah, where do you get the two from? Oh, you think this is the same as That So then we just take this Copy it and then zero This is maps to two Okay, that makes sense so If I just copy the last three hex digits How big is the page a to one virtual oh Eight two one. Oh So how'd you get eight two one four? Okay? Okay, and then we added them Okay, so these are this is a really annoying one because the number of bits For the page is six. So it's kind of annoying where it doesn't line up for hex digits. So We have to resort to writing in binary So this will make it easier even though it seems awful. So F1 F1 is if inner eight bits is one one one one So that's f and then one is zero zero one two two one closer So we'll see so here so first We don't touch the offset and now we know our offset is six bits So this is our offset. Oh Okay, so the question is why do we know it's six Why do we know it's six bits? Yeah? Yep, so we know that's the offset because our page size is 64 bytes, which is the same as to the six so Same rules apply. It's just in binary now. So it's kind of crappy, but our offset for our This is our virtual So when we translate to a physical address They're exactly the same so they would be one one zero zero zero one and These are our same offset bits So in that spirit, this would be our index and Then our index that's a three. So one one is three so it would correspond to this entry here, which is eight and eight is Unfortunately one zero zero zero so if we write them all together like that. This is our Yeah, yeah, so this is our 10-bit physical address so it's we can double-check ourselves that it is 10 bits So if we group them back up into fours and write it back into hex Zero zero one that's a one Zero zero one one that is a three and then one zero is a two So this would be our virtual address two three one Everyone on the same page Terrible so in this there is four Yeah, it's the index. It's gonna always be the same as the number of virtual pages And for a single level page table. Yep the first two bits for the physical so we just Looked up the entry the entry was eight. So eight was zero zero They're one zero zero zero. So this is eight So we just put that at the front. So this was our offset here And then we just wrote eight in front of it here So when we combine them like two of the bits were from the eight and two of the bits were from the actual offset So it was a three. Okay So Usually this is actual hardware. This is like in documentation So there's a bit more in our page table So so far we just assumed that The entry in our page table was the physical page number But because there's this offset we don't have to translate essentially we get 12 bits for free we can play with So this is at least for risk five That they reserve 10 bits to be used for permissions and things like that So there's one bit that says whether this page is valid, which is useful when you drew translation so instead of Deallocating it and trying like zero memory or something like that you can just say hey the page is no longer valid So then whenever the mmu translates it it generates a fault and it fails So other Like for the purpose of this course, we only really care about probably the first four bits maybe five Um readable if you can read the memory or not writable if you can write the memory or not executable If you can actually execute it So, you know your cp you can read it and as instructions And then there's going to be a bit that says whether or not this is represents user memory Or if that's not set it's kernel memory So it's just another way as another security check just to make sure everything lines up so These are the entries that the page the mmu actually wants So it's we'll take the physical page number that we saw and then they also ram a bunch of permission bits that the mmu will check for you So we only really care about the first five fights for this course, but this is like actual Hardware, this is what a page table entry looks like so We kind of jumped the gun on this so each process gets its own page table Which just represents its own virtual address space so for Some processes it would look like this where user text and data is at the bottom which is Text is a really dumb name. It's actually code So it's things you can read and execute and then data so that would be global variables and everything that's you know essentially Global data defined in your c program and then there'd be some space for a stack space for a heap and then the trap frame is this is like internal details for a little toy kernel But the trap frame is basically Where the kernel will save all your registers and stuff when it does a context switch So it saves as part of your virtual address space and trampoline is just another thing to Mechanism to switch between user mode and kernel mode that we don't really have to worry about so Each process also gets its own page table. So we know this so now we can figure out what fork has to do so If you fork one of the things we know it's the exact copy of the process So that means if each process has its own virtual or own address space And they each get a new address space when you forked and everything's independent Then it has to copy the page table so If in the naive Way it would copy the page table and then copy all the memory that the page table points to so that everything's completely separate But if you are a smart kernel developer well If you have pages that are only red Essentially, they can share them no problem because you won't see any changes in the other process So if they're readable pages, I don't have to bother copying them I can just ignore them And then there's this trick called copy on write so if I can copy them not copy the physical pages and just turn the right permission off And then whenever one of the processes modifies that memory I get an error and then as the kernel I get to resolve that error So it would generate a page fault and I can be like oh Well, I know I made it generate that because one of them modified some memory So then at that point you can make a copy of that page And then make them point to different pages where one has the modification and one has the original copy And that way you're only copying memory that you actually need to write So it's a nice little easy optimization you can do Yep Yeah, yeah, so that's the secret fork is fast But if you start using the memory and monkeying with it, it's going to have to do the mappings kind of on demand It might be a bit slower, but It's better to just only copying the memory out like an on use basis Yeah Yeah, so that's a good question So what happens with the execs ve so exec ve says you up with a new address space so it starts a new page table from scratch Yeah, like it just replaces the page table So old one gets deleted and it just replaces it So The big problem here is if we use a big single page table Where we have even a 39 bit virtual address, which isn't even that big That means there are two of the 27 entries in the page table each one is eight bytes So if we do our multiplication that means each page table is a gigabyte So obviously that can't happen. So How many processes could you have each process needs its own page table and each one's a gigabyte? That seems like I you could probably run at most like Eight processes and then half of your memory is filled up with page tables So we must be we must have a better technique than doing this so And yeah, then the So that's where we'll leave off for the next lecture and then risk five It would be at least one gigabyte. So we'd have a 39 bit virtual address And in risk five it translates to a 56 bit virtual address Uh and generally they'll all be bigger So it has 10 bits to store the pte and then Reserves some bits that it could use for later expansion And but the page size is still 4096 So you may be thinking that also seems like a lot of work If you're doing fork fault followed by an exec, which is what you just mentioned Why don't you even bother copying the page table at all like i'm just going to delete it immediately So They don't and that's why they invented another system call called v fork So v fork shares all the memory with the parent. It doesn't touch the page table But it also gives you this caveat that if you touch memory in the child process It's undefined behavior and you can screw up the child the parent process So that's pretty cool. So only use this in like very Sensitive applications that really really depend on performance and that immediately exec And this is why they got rid of Adding a whole bunch of other system calls and just essentially made a new system called called clone that lets you control What gets copied? All right, so I will leave you off on this and this is how it's actually used and what we'll be talking about next lecture So instead of having a big single page table The trick is to have multiple levels of page table where they point to each other and each smaller page table Exactly fits on a page. So that's what we'll be talking about next lecture So, uh, let's remember boom for you or all this together