 All right, welcome back to operating systems in this boiling room. Why is it like this in October? I have no idea So everyone's favorite topic is about to come up virtual memory so Before we start that oops Let's ask a question How should we implement it? so all we have to do for virtual memory is Find out a way to map Virtual addresses, which should be independent per process to a physical address Actually residing in memory So I will take a little detour just in case you've discord open if not we can discuss it and That question is simple. What are your ideas for how to map this? So I will paste that so Any bright ideas for how to map actually physically implement the mapping from virtual Virtual address to a physical address. Yeah, so find some chunks of memory give it So, yeah, that could work for actually finding physical memory But what about actually doing the translation from virtual to physical like how would you implement that? Yeah so if it requested a big chunk size just kind of Calculate it figure out it's offset and just like Map it one-to-one somehow so you could give each process just a chunk like a fixed-size chunk and that's it Could do that any other ideas What about? Oh, no crazy look up. We want to look up something about a hash map or something like that Does that sound sane at all? Yeah, how's the hash map different than chunk and what? Oh, how is it better? So it's a bit more flexible right? We could just map it to any arbitrary. We don't need everything all in a row That could be one. Yeah Yeah, you might have to access memory all the place and Remember memory is byte addressable, right? So every address represents a byte So if it was a hash map or something like that addresses are like 64 bits or 8 bytes So my hash map would like need to be keyed by a virtual address So that's 8 bytes and its value would be like a physical address. That's 8 bytes So that's probably a bad idea because for every byte I need 16 bytes to keep track of the mapping even if the rest of it was free, which is not Probably not a good idea got another one divide up memory into small chunks and work work it with their hash map so Hash maps you should have implemented one hash maps are basically just fancy arrays that just do the indexing for you So it essentially just fakes a large index makes it into a smaller one But hash maps are essentially arrays. So if you can just get away with arrays, that's also probably faster So let's go with the first idea. I like the first idea of like having just blocks that represent stuff And then you could just calculate it, right? So in fact You have described Oops, you've described a technique we'll use but first we'll keep this checklist in mind. So Remember with virtual memory needs to be independent for each process So multiple processes have to be able to exist at the same time They shouldn't be aware that they are actually physically sharing memory They shouldn't be able to access each other's data unless they're explicitly allowed Which we'll see how to do that later So type of the commute type of way to communicate between processes but by default everything should be independent for each process Because you wouldn't want another process to read your passwords your private key or whatever We also want good performance So close to using physical memory and to limit the amount of fragmentation Which is just a fancy word for wasted memory. So My hash table idea would obviously fail at number at the last point and it would probably Performance would probably also be fairly terrible So here's just so we remember memory byte addressable So the smallest unit every address represents a single byte You can read or write one byte at a time minimum. You can't just access individual bits. You just get one byte at a time So you can think of like an address as just an index into a byte array So each address Uniquely identifies a single byte memories basically just one big array of bytes if you want to think it that way with the address being the index So your idea was essentially Segmentation and they're fairly coarse-grained. So you could have a virtual address space for like code data stack and heap and It kind of corresponds to an elf file or what your executable file looks like So it's large sections of memory with different purposes and different permissions. So like your code That's something you would execute and read probably not right Data you might just only read it your stack. You would read and write same with your heap So each segment can be a variable size dynamically resized And this is a good idea and it was used back in the day in the 70s But it's no longer used primary reason for that is those segments are really costly to Reallocate because they have to correspond with a big block of contiguous memory So if you want something to grow and it can't quite fit Well, you might have to move all that memory somewhere else copy it to make sure it is in one straight contiguous line and Another thing is it might lead to a bunch of fragmentation because we have variable size things Some allocations just might not fit. We might actually waste a bunch of memory and This was used in the 70s and not used in modern operating systems So how it worked. Oh, sorry. Was there a question? Yeah, of course grained is like I can't really share anything That's not part of a segment So like I can't if I wanted to share code between two processes and like fake a mapping or something like that I couldn't do it in this case because I can only map one big block at a time, right? So I might be able to do it with like two processes, but past that I can't do anything And I just have a segment for each individual thing. So it like kind of works But we'll see as we go on and understand what actually happens. We'll see some more disadvantages to it but It works it will get a bit messy once we get into once you add features on top of it, but At its core each segment. It's a pretty good way first estimation of it So each segment would contain a base physical address So where that segment actually starts in physical memory a limit basically the size of the segment and then permissions read write execute something like that and then You get a physical address by using this as your virtual address So it splits it up into two parts you first ask for a segment which is called a segment selector Basically just a number that says what the segment should be and then an offset So how many bytes from the beginning of that? Segment is the information. I actually want so offset will always just be the number of bytes from the beginning So the MMU or the memory management unit, which we'll get into later Whatever it checks if the if the translation is valid It's going to check if the offset is within the limit of the segment You're currently in and then if it's within the limit it'll check permissions then after that to do the actual translation to get the To get the physical address. It's just a simple addition It's just the base address plus the offset that gives you the physical address Otherwise if it is out of bounds or the permissions don't check you get a segmentation fault Which you have all heard before so whenever you try and dereference the null pointer or whatever It's called a segmentation fault for fun fact Your computers don't use segments anymore But it's still called a segmentation fault because if it's any term that you see well, guess what? We went through that whole library thing where you're never allowed to change anything So that's why it's called a segmentation fault even though once we go through this will figure out that hey They don't use segments anymore So if they did use segments your virtual address would look like this So this means select segment one and I want to do offset ff Which is like 255 bytes into it and then as part of your description of the segment which your kernel would manage it would say hey segment one Starts at physical address 2000 and hex its limit is 1 ff or size and then when it did this translation it would see that oh Ff is less than 1 ff So this is within bounds assume that it has the correct permissions So to calculate the physical address it would just do 2000 and hex plus ff to give you to zero ff. That's your physical address So what links does to get around it because you're like old x86 hardware still has support for this because they cannot take it out They set the base to every segment to zero and then limit to the maximum amount of memory you have on your machine So essentially it turns Segmentation into a no op where it just doesn't translate anything. So the address is just the offset So we also like the other idea. So we're kind of on the right track of dividing memory Into chunks so the easiest thing to do in terms of allocating things is to divide memory into fixed size chunks So everything is the exact same size and it makes allocation a lot easier. So that way We just pick a virtual block with whatever size it is. So these numbers represent offset So like zero bytes into the page one byte in the page two bytes into the or sorry into the block three bytes into the block and Then if we need to translate we just translate the blocks So you wouldn't need to translate where you are within a block You just need to do the translation for a block So our only question now is like how big should this block be so that will be an engineering trade-off and Someone made it found a good trade-off in like the 80s so the MMU or the memory management unit that is the actual hardware on your CPU that is map doing the Translation of a virtual address to a physical address and also does all those permission checks. I was talking about So this dividing up memory into fixed size blocks. Well, we call them something special in operating systems We call them pages. So a page is just a fixed size block Pages are typically 4096 bytes which is that magic number I was using in some of my code examples So the reason I was using that number is because that is how the operating system deals with memory It deals with it in those fixed size blocks, and they are of 4096 bytes So there's going to be some terms as we go through this so a page in virtual memory Generally, you just refer to that as a page or virtual page and then if you Have a page in physical memory Sometimes people call that a frame Sometimes they'll call that a physical page Sometimes they will just call that a page if they if it's obvious that you mean physical memory So another thing we have we all have CPUs that have 64-bit addresses Typically, you don't have that much RAM on your machine, and you wouldn't need to use all 64 bits so CPUs as we will figure out have different number of Like different sizes of virtual address spaces that they support But the implementation details are the same It's just a trade-off with some of the data structures and how slow some things are which we will see So in general right now to this day if you have a 64-bit machine You have a 39-bit virtual address space So that means within a process you can only use at most assuming the kernel doesn't do anything Limited by hardware you can address at most 39 bits of memory So that means you could address up to two to the 39 bytes of memory which would be 512 gigabytes of memory So that is the maximum memory your process can actually use the machine could actually support more memory so This is something called SV 39 in risk five Every system will call it something different and How it's implemented is it there is a page table and is basically a giant array so the page table will be indexed by the virtual page number and All the entries in that page table are essentially going to be physical page numbers so we just do a quick look up in a table and then Substitute the virtual page number for the physical page number keep the offset the same and now we have a virtual or a physical address So in a diagram, this is what it looks like. So our virtual address if it is 39 bits Well, then our offset or where we are within a page depends on how big our page is so if we have a page size of 4096 bytes Well, we need 12 bits to be able to address every single byte within a page So why is it 12 bits? Anyone want to tell me why it's 12 bits? So the offset is like Which byte in the page you are? Yeah, so To the 12 is that page size. So this page size of 4,000 4,096 why is it so hot in here? I wish the AC was not broken So if we want to address Any single byte within a page we have to be able to address 4096 bytes So we would need 12 bits to do that because to the power of 12 is 4096 so that's where that comes from in our offset So the size of the offset is determined by the size of the page so if we have a 39-bit virtual address and we know 12 bits have to be used for saying what byte are we accessing within a page? That means we would have 27 bits left over because 39 minus 12 is 27 so that 27 Bits would be used for our virtual page number, which is basically just an index into this page table So this page table would need two to the 26 entries Because we need to look up a maximum of two to the 26 Virtual page numbers, which would be the indexes and then this page table Essentially would just be a giant array of physical page numbers So if we were to translate the virtual address We would get the index from these 27 bits use it as an index into this page table Which has the physical page number and then we would just substitute that in for our physical address and That would be our physical address We don't we keep the offset the same where you are within a page Because it's going to be the same we want to make this as fast as possible So they would all be aligned so there's also a nice reason why this is 12 bits Because typically we write numbers in hex So can anyone tell me how many bits a single hex digit can represent? for right so the nice thing about this number is well if Each hex digit is four bits and an offset is 12 bits How many hex digits would represent an offset? three right so 12 divided by 4 3 so Three hex digits represent an offset. I don't have to translate them makes it really really fast I don't have to do any math So The entries in that page table like I said The most important thing is this physical page number, which in this case is 64 bits But this is the entire what the Page table entry looks like in its entirety so Because it doesn't have to translate the offset The important part for translation is this physical page number So some of the other information in this is used for permissions for the entry so the most important ones for us is this Valid bit so if it's one it means the page table entry is valid and I can use it for translation The other ones that are important for this course are is this memory readable and then is this memory writable? That's it for this course There's other ones you could imagine which would be useful is this memory executable The user just means should this only be valid in user mode Global means is this mapping the same for every single process then there's like access bits and then dirty which Get your mind out of the gutter that just means that There was a right to the page and it's been modified. It doesn't mean what you probably think it means Then there's like two bits that are reserved for supervisor use or the kernel use Which you will find useful when you do this yourself in lab three and then they reserve some 10 bits Just for future use they could extend the physical page number if they really wanted to but for now That's like they have 56 bits for physical addresses So that's enough for this foreseeable future, but if they wanted to they could expand it if they wanted to so The kernel is what keeps track of all the page tables And if we have a simplified version of our page table where we'll just list like the virtual page number Which is the index and then which physical page number that maps to? This would mean that virtual page number zero maps to physical page number one Virtual page number one maps to physical page number four So on and so forth so if we have to do the translations where on the left is a virtual address And we want to find the physical address Well to do that translation First you find the offset the offset is three hex characters So we take AB zero and just write them for the physical address. We don't have to translate them So the last three hex digits are going to be the same So they would be the same in the physical and the virtual address So that means if it is not the part of the offset It means that this is the virtual page number. So all you have to do is look up virtual page number One or sorry zero in this table figure out that it corresponds to Physical page number one and then you just do the substitution. So our physical address would be one AB zero Not too bad. Hey Same thing for this address. So you don't have to translate the last three hex digits. So it's f a zero same thing in The physical address and then to translate this virtual page number. It is virtual page number one We'd look it up in the table get physical page four Just substitute it and this would be our physical address for f a zero. Yep sorry Yeah, so All your processes they only use virtual addresses. So every address you see is a virtual address it you will only ever see physical addresses if you are the kernel and Most kernels actually use virtual memory as well Yep, sorry Yeah, so the MMU is like the actual hardware that does the translation and the kernel manages all the page tables that the MMU uses So like The the kernel would be responsible for these tables and then the MMU would be the hardware that uses the page tables to do This for you. So it automatically does all these using the page tables So the MMU is like the physical hardware. So if the MMU Would just so this would be part of the definition of the MMU It would define a page table entry and how you're supposed to use them So yeah, you would just have to follow whatever that CPU says So it would have to be formatted exactly like this And we have a one The page table entry translates the virtual to physical But how come in the PT example doesn't have a virtual page number So the virtual page number is essentially just an index into this page table So it doesn't exist in the page table entry. It is how you find the page table entry So this virtual page number is an index. I use it to get the index into my page table It gives me the page table entry and the important part of the page table entry is this physical page number Which is why we did these translations So any questions about these translations the last one makes sense So there's any questions about those translations asked now because it's So remember how processes started off okay, and they got real weird real fast This will get weird fast All right, same same page. We're all good All right, then let's try a question to see if we remember this So I can describe any system and you should be able to figure out how to do some Translation so in this system, let's assume I have an 8-bit virtual address and a 10-bit physical address and then each Page is 64 bytes. So we have to Essentially do what we did before except the numbers are a bit weird now So you will probably get asked questions like hey, how many virtual pages are there? How many physical pages are there? How many entries would there have to be in this page table and then given a page table actually do the translation? So I will give you a second or a minute to think about this and then we'll go through it So think about how you would get each of these answers and what this system would look like alright Do you have any ideas how to start yet? Yeah, all right everyone agree with that everyone heard that So first part of this is given the page size the page size influences how many offsets I Offset bits I need to say where in the page. Am I so if I have a 64 byte page and memory is byte addressable that means I need to Be able to access up to 64 different locations within the page So I have to essentially figure out two to the what Equals 64 and that X is how many number of bits. I need to say where I am within a page for this example, so To not get your calculator out because you know you solve this log 2 is blah blah blah that is slow as hell So I will show you You should only have to like memorize like two powers of two and then Like you could probably memorize less. I'm a bit slow I can only multiply by two like two or three times, but if you remember two numbers, that's all you need so if you remember that to the five to the five is 32 just remember that and then that to the 10 is 1024 If you know those two numbers you can figure out the rest by just multiplying by two a few times every time you multiply by two The exponent just goes up by one So if I know to the five is 32 then to the six has to be 64. I Whoops, I Don't have to do any complicated math The the only level of math I can do is multiply by two a few times and add that's about all I can do But for this course, that's like all the math you need to do things need to be real fast. So That's also why you know, you can bring your calculator to exam But if you're using your calculator, you're going to be like five minutes slower than everyone else That's doing something like this so in This case for my virtual address well We figured out how many bits we need for our offset That was whoops six bits So how many bits do I have for my virtual page number? To I hope right? Like I said, I can only do a bit of math at a time subtraction One thing I can do so eight minus six. That means two Why is it eight because it comes from the size of the virtual address? so If that's the case, how many virtual pages do we have? four right right because The number we have Corresponds to how many number virtual pages we can actually index so we have two bits so we could actually Index two to the power of two pages. So to the power of two It's only multiplying by two once or yeah two times two. I can do that. So that's four any questions about that All right, then what's the next one how many physical pages can I support in this system? But yeah, and what's two the four? 16 right so Same thing for the physical address. It looks like this and The offset is going to be the same which is six bits So what's left over for the physical page number? Well ten minus six I can do that too that is four bits So the number of physical pages we can address is Two to the four which is just two the five divided by two So that's 16 All right next question then how many entries would there be in the page table for right? So This is a trick question kind of there's just one entry in the page table for every single Virtual page number. It's just an index. It's just how you look up How you know what to access in the array? So it will always be the same as the number of virtual pages in this kind of a system and Also, there's a hint that it gave you a page table. It had four things in it So you could probably figure it out from that so This page table is a short form of a page table where for this I just it's a page table full of Physical page numbers and it's written like an array. So each of these is a physical page number and Then the index of each one of them. So it'd be like zero one two three Well, that's the virtual page number That is how we look it up in the array and know which element we actually need to use for a translation So then the question said well Oops, if you have fit virtual address F1 What does that translate to? What physical address does that translate to using this page table? So This is where it gets a bit more of a pain because our offset that we don't have to translate is six bits Not a multiple of a hex digit. So I can't just kind of chop it off This gets annoying. So this is why you were drilled on like binary and hex and all that stuff to prepare you for this I guess so the easiest thing to do believe it or not is to just write this number out in in binary so Let's see if we remember stuff. So what is f in binary? Yeah Yeah, f is one one one one well about one So it's hex. So there has to be four bits. So if it's one, hopefully it's just zero zero zero one right So another little trick for remembering how to translate from hex to To binary You only really have to remember one really important one and then from that you can figure it out So just remember that a is one zero one zero What's a good way to remember that? Well a is supposed to represent a Decimal ten so just write ten out twice a is ten two times and then pass that you just have to add a bit So like B would just be adding a bit in the least significant spot So B would be one zero one one Just write this down. You can you can work out the rest from knowing this one so if this is our virtual address in binary and We don't have to translate the offset part. Which numbers do I? Which numbers are part of the offset that I don't have to translate so like hey don't have to translate these six No other way, right? So the offset is at the least significant bits So this represents my offset. So when I translate that to a physical address Means I don't have to touch them. So it would be one one zero zero zero one So that is the offset part of my physical address. So that would mean that this is my Virtual page number. So what is one one in? Normal decimal Three right one one is this three. So I would go to my page table Look up what I need to translate this virtual page number two. So it is eight, right? So instead of the virtual page number, I just write the physical page number as indicated by this page table So what is eight in binary? one zero zero zero right three zeros so We just write out eight one zero zero and now if we want to translate this back to Back to hex we just start grouping in groups of four from the back So that's one group. That's another group So the physical address we end up with would be what is This in hex One what is this in hex? Three what is this in hex? Two so given this page table if a process tried to access virtual address F1 it means it would be translated to physical address two three one Everyone see how we got that? Fueler fueler. We're good. All right. So this Okay, usually people are already lost by now. So this is good. We're much better than previous years So some other fun things so each process like I said before gets its own page table So what happens when you fork it will copy the page table from the parent So part of being exact clone of each other that means it will copy the page table and all their virtual memory will be the same so what the kernel actually does as a trick to make fork really really fast the page it copies the page table and Both processes have the same translation if the same virtual address points to the exact same physical memory and If you only read information in both processes you can actually share it and it's really really fast So you don't have to allocate double the amount of memory They both just share it and then one of the things the kernel does is turn off the right bit To that memory so it can tell if one of the processes tries to modify memory Then if one does the kernel will then create a copy of that page and then separate them out so it changes the mapping in both processes and that is that That optimization is called copy on right and you get to do that for lab three So that will be fun so There's also a big problem here So we might have noticed that Hey, this page table has two to the 23 entries If you put that in your calculator, that's a lot of entries So there are two of the 23 entries and each entry is This big which is 64 bits or eight bytes so eight bytes is like two to the three bytes, so that means this page table is Two to the 27 times two to the three. So that's to the 30 Bites this page table is to the 30 bytes Which is one gigabyte and like real gigabyte which sounds really bad, right? Yeah, that poses a problem. So you can also see why This index the virtual address is small because it essentially corresponds to the size of this page table So if we just made a 40-bit virtual address and just bump this up to 28 bits Well, suddenly your page table just gets twice as big So now it would be two gigs if we did something like this So you can see why we would restrict the size or the amount of number of bits we use for the virtual address because Corresponds to the size of the page table With wall just this physical page number corresponds to the size of the entry which It's just always going to be eight bytes in general so We have that issue Yeah, so we have that big issue that we will tackle later You also might be thinking this seems like a lot of work so like in the sub process lecture and also in your crate you do like a or in your lab to Minus a few things You basically always do a fork fall by an exec so Assume we're still in a world with a gig page table Well, we wouldn't need to copy a huge page table if we exact directly afterwards we are executing a new Program and it would reset the page table that process would you know It would reset the page table for that process for the mappings for that program so it's a bit of a waste to copy the page table in that case and There is a special system call that will actually not copy the page tables so there's a system called called v fork and that makes the page tables the same in both processes Which also means that if you modify memory in one process the other process can immediately see it So they only have the system call with the intent that you immediately call exec and then if you try and modify anything In either process while they're both active that's undefined behavior and the kernels allowed to do whatever it wants So this is only for like very performant sensitive application. So you get some control over what fork does so What we saw today we use pages for memory translation divide memory into fixed size blocks Typically of that 4096 page. Oh wait also before a little aside So you might have noticed I wrote GI be and not gigabyte so That is very intentional. So in Computing generally we like powers of two so two to the power of ten It's generally like a kilobyte, and I'm gonna write an I here To the power of 20 is like a megabyte and to the power 30 is a gigabyte looks like a bit weird I'm writing that little case I because a Gigabyte is actually not well-defined neither is a kilobyte neither is a megabyte. So for some people They believe in powers of ten. So some people will call a kilobyte Ten to the power of three which is a thousand and that is also a valid definition of kilobyte So whenever you sit see kilobyte It could mean ten to the power of three or it could mean two to the power of ten And you might notice that to the power ten is slightly bigger. That's that one thousand twenty four So it would go up like this So this is also why if you have bought like a 500 gig hard drive and tried to use it and it says it is only 400 and something gigs because This number is what they sell you that is the marketing number if you look at the fine print on the hard drives you buy they'll say one gigabyte equals one billion bytes While your computer thinks a gigabyte is this So it reports that that number is way bigger than what they sell you So it's going to report a much lower size than what you actually bought So that's the differentiation this little I in that it always means powers of two And if you just see gigabyte you have no idea whether it's powers of ten or powers of two so That is your fun aside for the day Yeah, so the kernel is the only thing that keeps track of the page table So the curl is the only thing that's managing it. So it would know so like for example You could have you know process a process B and Say this is their same virtual page for whatever reason well the kernel could map both of those to the same physical memory and If they both read it say like it just says x equals one or something like that They could both read it if at the time of the fork x was one. They're both happy, right? so the only thing that happens is whenever one of the processes Modifies x. Let's say process B modifies x then the kernel is going to grab a new physical page and Do the update to x there and then change this mapping in process B So now process B will point to this physical page, which is different than the one for process a so and This don't worry about this will get into this in the next lectures too, but this is generally how it works So whoops? So today we use pages for memory translation divide memory into fixed size blocks only have to translate once per block Don't have to translate the offset pretty fast Because page tables are basically just an array The array is indexed by the virtual page number and it's a ray of page table entries the only thing we care about really that's in a page table entry is the physical page number and three flags valid read write and We have a new problem on our hands we get to solve in the next lecture that this page table is huge and Our program is probably not going to use 512 gigabytes. So we should probably do something about it So that is where I will leave you off for the next lecture. So just remember pulling for you