 All right, good morning everyone. So now we get to actually talk about virtual memory now. So hopefully you kind of know I guess what's going on, but this is actually where we'll talk about the translation and actually know what the MMU does, which we won't learn today. So today we'll be talking about single level page tables are what does virtual address to physical address translation. So remember virtualization is like just the concept of fooling something into thinking it has all the resources. So both, you know, if we had LibreOffice and Firefox, if they both use virtual addresses, they would think they have access to everything. And then the kernel is free to map it to physical memory and give or not give physical memory to each process as it sees fit. So if we want to implement or use virtual memory ourselves, we'll probably have this checklist in mind where if we have virtual memory, we want multiple processes to be able to coexist and they should not be aware that they're actually sharing physical memory if the kernel decides that's an optimization it would want to do. So one optimization would be that it would only load, for example, libc into memory once and that every single program that uses libc could actually just read, have access to the same physical memory where it would just execute code from and each process should be done the wiser. And if it needs to be independent, the kernel has to make sure that it does actually have an independent view of memory. So we also want processes to not be able to access each other's data unless you explicitly allow it. So there is a mechanism where we can share memory between processes in order to do some communication but you should have to opt into that by default everything should be completely independent. And then of course you want performance as close to possible as using physical memory with virtual memory. If it is twice as slow, you probably don't want to use it and would rather have the headache of using physical memory if it's gonna be something like twice as fast or 10 times as fast. So we want performance as close as possible. And then we want to limit the amount of fragmentation which is a new keyword that we'll be going into especially when we talk about memory allocation and fragmentation is basically just wasted space. So memory that should be usable but for some reason is not usable. Oh, we do talk about that. So we'll actually, I thought we were gonna talk about it later. All right, so now we get to be introduced to the MMU or the memory management unit. And all it does is map virtual addresses to physical addresses and also does some permission checking. For example, maybe there are some addresses that you should not be allowed to write to. So it would have permissions for that. So the prevailing technique that is actually still used today, it's been used since the 70s is to divide memory up into fixed size pages. So typically these are 4,096 bytes and that is the page size of your actual machine now in all likelihood. So the reason of doing this is because they don't want to translate every single address byte for byte, you would want to group them somehow. So they just pick this as a big group size in the 70s and for some reason it still works pretty well today. So when we divide these memory up into fixed size pages, the terminology is a bit different whether you're talking about physical memory or virtual memory. So a page in virtual memory is just called a page, nice and simple. In physical memory, sometimes it's called a frame, sometimes it's called a physical page and all sorts of kind of different terminology but usually when you say page that means virtual, a fixed size page in virtual memory and sometimes you might refer to physical memory, one's is also a page but technically most people refer to it as a frame. So one really old technique is to divide memory up into segments or yeah, into segments. So you divide the virtual memory space into segments for memory with different permissions. So you would organize it into code which would be memory you're allowed to read and execute, data which is memory you maybe just be allowed to read, maybe allowed to write and then stack which would be read write and then the heap which would be rewrite but located somewhere else in memory. So this kind of looks like an L file where there's large sections of memories with permissions and then each segment is a variable size and you can resize them dynamically but this is like a really old legacy thing that's not really used anymore and segments are really costly to relocate to and can lead to a lot of fragmentation if you run out of memory or like your code and data are right next to each other. So if you want more memory for the code, well you have to move all the data, copy it all, shift it over and then you're allowed to grow your code. It's kind of bad in that way, it's not no longer used but the terminology still exists because this is where the term segmentation fault comes from. So even though we don't use segments anymore, we still get seg faults because that was a name they were using before. So a little bit more details even though it's mostly irrelevant. Sometimes it's still covered in courses so each segment is pretty simple. It contains a base address, a limit or a size and then permissions. So how you get a physical address is you use a segment selector because there's only typically like four of them and then an offset where in that segment you want to be. So the MMU is gonna check that the address you're accessing is valid so it's within the size limit and then to calculate the virtual address it would just look up that segment, add the base address of that segment to that offset and do the permission checks and then otherwise if it doesn't line up or if you have the wrong permission, segmentation fault. So for example, if you use something like this as an address, zero one and then segment selector you wanna use FF. So somewhere there would be segment one and for example, it would have the base of address 2000, have a limit of one FF. So to do the translation it would be, it would be FF and then the limit is one FF so it's within the limit so that's fine and then it would just add the base address to that offset so the physical address for segment one FF would be 20 FF. So it just adds them together kind of like we've dealt with pointers all the time so this should be pretty much similar. So on x86 these segment selectors do exist and are still used by the hardware because they have to be to be backwards compatible but Linux just cheats and says the base of every one of them is zero and the limit is the maximum amount of your memory. So if you use a segment selector or do anything weird you'll just get the exact virtual or you'll get the exact physical address as you're looking for. So it pretty much negates the whole system so if you're on x86 that's what will happen. Okay so nowadays we have 64-bit machines. Typically you don't need to use all of them because 64 bits if they're all can each point to a byte is a lot of bytes. It's like what like 16 exabytes or something like that. So you actually don't need to use all that memory yet. Maybe we'll get there in 30 years. Probably not even that but we're not gonna use them all so we can use them all for some different purposes. So CPUs have different levels of virtual addresses you can use the implementation ideas are all the same. So for now we'll assume only 39-bit virtual memory addresses and that's used by risk five it's used by arm it's used by I think you can do it on x86 as well. So we'll just assume that it is like a pretty realistic one that's also not overly complicated. So if we have a 39-bit address that means we have 512 gigabytes of accessible memory and this scheme of having 32 or 39-bit virtual addresses is sometimes called SV39 and at its most basic it's just a lookup table. So it's implemented with a page table it's indexed by the virtual page number which is the virtual addresses that the application would use and then it would look up the physical page number so let's look at an example and this is exactly what it would look like. So at the top there in the blue is a virtual address so if we have a page size of 4096 that means we have 12 bits to describe our page because to the power of 12 is 4096 so that's our offset into the page and we're doing translation about the page. So this offset into the page we don't have to touch when we do any translation. So it's where in the page you are you don't have to translate that part it's exactly the same in the physical address and then all we're doing is translating the virtual page number to a physical page number. So if we have 39 bits of virtual address space and our page is like 496 we would have 39 minus 12 bits left for our virtual addresses so that's why it's 27 and then since we have 64 bits we have 25 leftover bits that can be used for future extensions. So using this index so it would use this virtual page number look it up in a page table and this would be used as an index so this page table would have two to the 27 entries and each entry would just store a page number and because we don't have to translate the offset we essentially have 12 bits we can use for storing other information about pages so they set aside 10 of those bits to use for flags like permission checks and all that that we'll see shortly. So that's actually what's in a page table so any questions about this? Okay so it's just a lookup table not too hard yet so let's look at how the translation would actually happen so if we consider the page table up here so it is virtual page numbers in the first column so zero, one, two, three and then the physical page numbers which would be like one, four, three, seven then we would get these following translations and we'll go through them quick. Yeah so there's a question so does the index in the virtual address the 27 bits which point to the index of the virtual table which contains information of the physical page number used in the physical address while the offset is exactly the same? Yeah so we don't translate the offset because it's within a page so we're just translating pages so a virtual page to a physical page so we can actually look it up in memory. Okay, whoops and yeah the flags would be something for we'll see what the flags are for later but if this is our page table up here then we can do the following translation so we'll assume our page table size is still the 4,096 bytes so that means each hex represents four bits so we don't have to translate the last three numbers because they're the offset so this would be the offset into our page would be AB zero and then so this is our offset and then this would be our index into the virtual this would be the index of our virtual page that we need to look up to get a physical page so zero is just if we look it up it translates to zero X or sorry yeah zero one sorry so that would be our physical address and then what would this address translate to one FA zero yeah zero X four FA zero yep so these are questions you would have that's just really basic translation so this address 2884 well two is our index so if we look up two it corresponds to the physical page number three so it would be three, eight, eight, four oops my writing is terrible this should be a three so if we have three to D zero then that would translate to zero X seven to D zero any questions about that so our page tables are pretty much just a simple lookup table and that's the trick okay well let's go into another type of question we might encounter so assume that you have a eight bit virtual address so we'll just make it really really small just so the numbers are a bit more easier to work with so you have an eight bit virtual address you have a 10 bit physical address and then each page now is 64 bytes so these are questions you should be able to answer it's like how many virtual pages are there how many physical pages are there how many entries are there in the page table and then given a page table do the translation so anyone for the first one so how many virtual pages might there be two to the eight so two to the eight is the number of that's like the bytes range of our entire virtual address yeah so it is two to the eight two to the eight divided by two to the six which is the 64 so that's two to the two which is four so there are gonna be four virtual pages well about physical pages 64, 16 16 seems reasonable yeah everyone agree with 16 so the number of the total range of our physical address space is two to the 10 and we have 64 byte pages so divided by two to the six we know our little tricks right with exponents so it's just 10 minus four so it's two to the four which everyone should know because we're computer engineers so that's 16 so if you just divide them 16 so there's more physical pages and virtual pages which makes sense because there's 10 bits for physical addresses and there's only eight bits for virtual addresses then how many entries are in the page table well it's just however many virtual pages there are so it's four and then and also that there's a page table right below it so you can count four so in this page table it's just in order of indexes just as an easier way to write this so virtual index zero would be this would be zero two virtual index one would be zero five virtual index two would be zero one and index three or sorry index two zero three would be zero eight so what is the physical address of F one well we can just write out the binary and just do that instead so remember this is our virtual address so our virtual address would be if we write that out F is one one one one and one is just zero zero zero one so remember we said that each page is 64 bytes so our offset is going to be the lowest six bits so this is our offset so that would stay the same so we'll just write in binary because this is a bit easier and then one one is our index what is one one three so it would correspond to this entry here it would be the last entry in the page table so if we just translate that and eight is zero zero zero one so if we convert this whole thing back to a well back to hex it would be one then this is three and this is two so our address at the end of it would be two three one all right any questions about that because yeah we're going to have a fairly easy day today then so no questions about that yeah oh yeah so the question how'd I get the offset to be six so I sit in the question it'll say what the size of the page what the size of the pages so this is a 64 byte page and 64 is just two to the six so the lower six bits is going to be the offset there so it's within the page yeah yeah so which would range between you know zero to 63 to get get you there so that's why if we have a 4,096 page size that it's 12 bits okay so there's all our answers we came up with no problem so far so this is actually what page table entries look like so if you want to explore the flags a little bit more so in this case in this case there's the physical page number which is 43 43 bytes so it uses 50 it's uses 56 bit physical addresses and these are all the flags so there's one flag that just says whether or not this translation is valid because that's something we might need if you're the kernel later if you want to invalidate ones and then there's a read read flag write flag execute flag and then other flags we don't have to worry about like is this user memory is it global memory has this been accessed before and then a dirty bit which we'll see later when we talk about what to do when we actually run out of physical memory so one thing you can do is just swap it all to the disk and fake that you have lots of memory when you actually don't which is another benefit of having virtual memory but we're not gonna talk about that until probably like next week so the MMU is the hardware that does the translation and it also checks these flags so for the purposes of this course we'll just focus on the first like five flags so whether it's valid readable executable and a user flag so the kernel can figure out hey is this physical page used for a user process or can I monkey around with it oh there's a question about got confused by the bit conversions why is dividing them giving us a number of pages so if we go here so the number of virtual pages so because we have an eight bit virtual address that means we can address two to the eight bytes so that's where to the eight number comes from and then if each page is 64 bytes you would divide it up to get the number of pages so the total number or the total memory you could address divided by the number of pages is the number of or sorry by the page size is the number of pages you'd have to have to represent all of memory so that's why we do the entire address range which is to the eight divided by the page size which is to the six yep sorry this oh the four this yeah so the two in the file answer so that just came from translating it so this was our virtual address when I wrote out this was F and then this was one so here I'll translate it again so for the last six bits I don't touch them because they're the offset so the last six bits when I translate I don't touch them because they are the offset so it would look like this and then for translating the physical or sorry the virtual page to the physical page it would be index three here because it's three so if I look it up it translates to eight if I write out eight in just binary it would be one zero zero zero so I have to put that at the front of this so if I do if I do that it translates to this which is now a 10 bit number instead of eight bit so if I want to bring it back to hex I have to group them into fours so I group them into fours and then this would be a one this would be a three and this would be a two yeah and this is just uber annoying because it doesn't the page size doesn't nicely line up to a hex size so usually you do just to make it easier to write and just for convenience they're usually dividable by two to the four just to make your life easier but this is like exceptionally kind of mean and tedious but that's as hard as it would yeah but that's as hard as it gets yeah so we'll just focus on the first five flags we're actually not going to be implementing them or doing anything with that so we but we should kind of understand what the flags are and how the kernel could use them to its advantage so this is the address space for each process and each process we know gets its own address space so this is kind of what it looks like if you look at like very simple operating systems like there's xv6 which is like MIT's small Unix kernel it looks like this so that address zero it puts all the user text and data text is just a weird word it actually means code so it's actually information you can execute so it just throws it all there at the bottom and that's what would be loaded in from the elf file so that's all your addresses all your global variables all of that would just be mapped to some lower region of virtual memory and then above that would be the user stack which would be like on pthreads it's like eight megabytes and then above that would be the heap that typically starts at the top and then grows down and then for this really simple operating system there's a trap frame and a trampoline we don't have to know that but that's basically mechanisms that it uses to switch between kernel mode and user mode so the trap frame would store all the registers and everything whenever you initiate the switch from user mode to kernel mode and the trampoline is just some way of actually changing modes where if the kernel is changing modes it has to change to its address space and it might need to access something from the user process which it can only get by being in the user address space so that's all stuff beyond the scope of this course that's mostly for monkeying with that transition so each process gets its own page table and that's how we make address spaces just each process gets its own page table that's it so whenever you fork a process now we understand a little bit why it could be much slower is when we said it was an exact copy we didn't lie so it would copy the page table from the parent so that's why everything maps up to be exactly the same but separate so if you didn't have any optimizations or anything whatever you copy the page table you would also have to copy the pages into new physical memory if you wanted to make a complete copy of it but that would be pretty slow so one optimization the kernel has that's really important is something called copy on right so what that will do is whenever you fork it will copy the page table and then turn off the right bit on all the addresses so you can both of them can read to them and they'll read to the same physical address but as soon as one starts to write to it it will generate a page fault in the kernel which means that you're accessing it in a way you shouldn't and then only at that point the kernel can be smart and actually physically copy that page to another uh... another physical address then do the modification and then make it writable in both so you have really fast forking and it still has that illusion of everything being separate finally we've talked about it now so there is a very big problem with these page tables so so the page table we were talking about before if it is a thirty nine bit virtual addresses with four thousand ninety six size pages that means that if we have our page table it is two to the twenty seven entries long each of the uh... entries in the page table is going to be eight bytes so if you multiply those two numbers together it means each page table is one gigabyte so if one page table was one gigabyte in your machine how many processes could you have like eight and then half of the memory is probably is in page tables so something going on here because that doesn't quite make sense but we have processes we have lots of processes in our machine and they can access up to eight gigs of memory without this whole thing when you start off some little you know console application that barely uses any memory it doesn't use a gigabyte of memory so something's going on here so again talking about the risk five translation it is a thirty nine bit virtual address to a fifty six bit physical address so ten bits to spare in the physical uh... in the page table entry that's what pte is and they could expand it it's all future use for the purposes of this course and for the purposes of pretty much any hardware you'll ever see that's the page table size four thousand ninety six that's why i kept allocating buffers in that size because the colonel only cares about pages and that's kind of the default num the default uh... unit of memory it deals with his pages can you yes a question is can you manually change the page size because you know this was in the seventies what both we just increased their page size to you know the next factor four up or something like that so there is something you can do for that uh... they're called huge pages so there's actually a push now that with really really memory intensive applications some hardware will let you use two megabyte pages some and then some will go up to i believe their gigabyte pages so that makes translating really easy if it's just a gigabyte page there's not much you have to do going to be some trade-off so if our page size then our application has to use at least one page and then every application uses at least a gigabyte of memory so two megabyte though is probably actually more realistic nowadays adoption you can do on newer hardware but the default is still going to be from the seventies it's still going to be that four thousand ninety six oh whoops i didn't fix this so you may be thinking this seems like a lot of work so this is actually in lab four you'll be doing a fork followed by an exec i did not update this my bad uh... so if you do a fork followed by an exec one of the questions you might have so exec will also create a new address space for the new process so it seems kind of wasteful that we would copy all the page tables and possibly copying memory when we're just going to exec anyways and blow it up so there's actually the developers actually thought of that and there is another version of fork so there's a system called called v fork which by default shares all the memory with the parent and it is then undefined behavior to actually modify any memory in the child so the only purpose of it is to immediately exec and you're not allowed to do anything else was only really used for really performant sensitive applications so any questions for anything with today because i'll leave it off on the super hard note okay single level page tables really bad because they would be at least with this scheme it would be gigabyte size page tables so obviously they are not a thing so what you actually do is do something called a multi-level page table and that saves space for applications that just don't have very many application or don't have very memory that don't use very much memory so the idea here is to divide up our page tables into a series of smaller page tables that kind of cascade with each other so if we take our virtual or sorry if our page table size is two to the twelve and each of the entries are eight bites which would be to the three the idea here is to make each of the smaller tables exactly fit on a page so if they're each eight bites that would mean i can fit to the nine of them so in here all of our pages or all of our page tables would fit on a page so we would have to the nine entries in them everyone follow hopefully so then to do multi-level page tables i would divide up that twenty seven bites that i originally used as an index into three of them that each represent a different level so the highest level page table here the level two page table well when it goes and looks up into it it would look up the index so it would be you know zero two to the nine minus one and for the physical page number there instead of doing that in the translation that would actually correspond to the level one page table so we would follow that to the level one page table and then use the index from the virtual address here and then that would correspond to another page table so that would correspond to the level zero page table and then in the level zero page table the lowest level page table is going to act exactly like our single level page table where we just find the physical page number and use that in the translation so we go through multiple levels here and then the last level we just do the same translation we always do the offset stays the same and then the physical page number is whatever we looked up in the lowest level and that's okay if it doesn't make sense because this is just a preview for next lecture so any questions about single level page tables now though they're big wasteful but they're just big lookup tables they're just a big table alright cool we get a bit of a break and we get to an early so pulling for you we're on this together