 Alright, welcome back to a week where it's not negative 20, so that's pretty sweet. So before we get started, any questions about Lab 2? Oh, I saw people looking away from me from Lab 2, that's not good. So if you haven't updated your code for Lab 2, I suggest that you update your code for Lab 2. So I recently just pushed a bunch of new test cases. So you could previously pass half the test cases if you just exited early with a code of zero, so that wasn't great. So now unfortunately you have to implement your print, but all the test cases are updated and much more readable, so you should be able to open the Python file and even look at the line that should run or execute it directly. But for that, today we're starting our journey on virtual memory, one of the greatest topics of operating systems that hopefully today is okay and hopefully review. So if you're not familiar with like hexadecimal and writing binary and all that, I suggest you practice and we'll get some practice today because you'll definitely be using that. So remember what virtualization was, was it was just simply fooling something into thinking it has all the resources. So if we have, now we know that these would be two different processes, so lever office on the left, firefox on the right, they'd both be different processes. They would have access to an independent view of memory. They both think they have all of the memory, but they're sharing some physical memory and really they don't have access to everything. Something is doing some management of the actual physical memory, which we'll get into today. So here's our checklist as we design a virtual memory system. So we want multiple processes to be able to coexist without being able to see each other's memory or start monkeying with their memory, changing it when they don't expect it. You also want to make sure that processes aren't even aware they're sharing physical memory. So your process shouldn't really behave any differently if a thousand processes are running versus like zero. So it should have no idea one way or the other. And that's minus you using that proc file system and finding that out. You also want processes not accessing each other's data unless you explicitly allow them to through some type of inter-process communication, being pipes. You can also, what we'll see later, you can also say a region of memory is shared explicitly and any changes will be reflected in multiple processes, which make things a bit trickier. And we'll see that when it comes to it. And also you want good performance. So you want performance close to using physical memory. So without even knowing the details, there's going to be some translation if, you know, process A accesses memory location a thousand and process B accesses memory location a thousand and they're both different physical addresses. While something has to do that translation and that might be slow, we want to be as fast as possible. And then as our final goal, we want to limit the amount of fragmentation. Fragmentation is just not even a fancy term of saying just wasted memory. So I have gaps in my memory that I can't fill. So you want to minimize that as much as possible. So just as reviewed, so everyone is good with memory. So no matter if I talk about virtual memory, physical memory, no matter what, just remember that memory is just byte addressable by default. That's like the lowest unit I can do. So every address is basically a number. You can think of it as like just memory being like an array of bytes. And each address or number is just, you know, an index. And at each index, there's an independent byte that is one byte that you get back, which is eight bits. And then at minimum, I could read groups of bytes, like four bytes at a time or whatever, but my absolute minimum, I can't do less than one byte at a time. Even if I want to change a single bit, I'd have to read in a byte, change a bit, and then write back out that byte. So each address is like an index of array if you want to think of it that way. So any questions, hopefully everyone kind of knows this about memory. And just keep this in mind as we go through because this does not change. Memory is still byte addressable, whether I talk about physical memory, virtual memory, which we all use now, doesn't matter which. It's all byte addressable. So the memory management unit is like the actual hardware on your CPU that does the mapping of virtual addresses to physical addresses. And it also does some more things than just do the translation. So it will check permissions. Like can you write to that address? Do you even have access to that address? Which as you might be able to tell would be your segfault if you don't have access to that address. So one technique, well, the major idea of this is like, well, if I had to translate every single address, that would waste a lot of memory. So memory is byte addressable, so it's just one byte at a time. Say my address is 64 bits or eight bytes. Well, I don't have to translate each single address individually. That will be really, really bad. And for every one byte of usable data, I need eight bytes to store a translation at minimum, which would be awful. So that just won't work. You have to throw that out the window. So the major idea behind virtual memory and managing that and managing all the translations is I just have a big block of memory that I do translations for. And I only do translations for one big block of memory at a time. And that is what a page is. So a page is just a big block of fixed size memory that the operating system actually tracks the translations for. So typically, this is 4,096 bytes and that's where that magic number is. So that's the lowest unit the operating system cares about your memory. So it will do the translations in terms of these pages. So coincidentally, the 4,096 bytes is actually a fairly convenient number because that would be, if you know your powers of two, that's two to the power of 12, which is some people don't, OK. You should practice your powers of two off the bat. And then you can do the exam way faster. So this is two to the power of 12 while, OK. Everyone here, OK, raise your hands if you've written hex digits before or had to read them before. OK, so everyone's not kind of familiar. So each hex digit, that can represent up to 16 things, which would be like two to the power of four. So each hex digit is exactly four bits that it can encode. So to the 20 is three of those. So three hex characters, you can say where you are within one of these big blocks that we call a page. So this big fixed size block, we might call something different depending on if we're talking about virtual memory or physical memory. So if you're talking about virtual memory, sometimes you call the virtual page. Sometimes you just call the page. And if you're talking about physical memory, sometimes you call the frame. Sometimes you call the physical page. Sometimes you just get lazy and call the page. But it's just that fixed size block of memory that we actually do the translations for. So the first scheme that no one uses anymore that's just there for historical reasons is called segmentation or segments. And that's like a coarse grained way to translate addresses. And even though this isn't used, this is actually where the term segmentation fault comes from. So you see segmentation fault even though it's no longer used. But they had that error code in the 70s. And as we know now, we can't break anything that user see. So it's forever called a segmentation fault now. So the idea for a segment is we divide our address space into different categories of memory. So we have a category for code, which is just some instructions we can execute. Data, things we can read and write, global variables, stuff like that. A stack, and then a heap. And then that looks like an elf file. So you also specify this in an elf file, but it closely relates to this. So the way you do this, each segment is a variable size, and you can dynamically resize it if you want. So it's like, but we'll see that that's kind of a bad idea. That if you actually want to change it, well, that can be really, really costly. It's like if you tried to change what malloc pointed to, and it was a huge array. And it doesn't quite line up, it doesn't start with the same base address. Well, then I need to copy all that data somewhere else, and it's just gonna be dog slow, especially if it's like hundreds and hundreds of megabytes, so that's no longer used anymore. But how it worked is, each of those segments, well, each entry or each segment contains like a base address, a limit that tells you the size of that segment, and then permissions like read, write, etc. So then to actually get a physical address, your virtual memory looks a bit weird, where you have to say what segment you want, and then you say what byte in that segment do I want after that. And then the MMU or the memory management unit that's actually doing this translation will check that your offset address is within that size, so that it's a valid address. Again, think of it as a big array. And then if it is, so if you ask for byte 200 and the size is 256, then it goes okay, great. And then it just adds a base address to that offset you give it, and that's your physical address. And you get a valid physical address if it passes the permission checks, depending on what you're doing as well. So if I'm trying to read from it or write from it, if it doesn't pass the size check or if it doesn't pass the permission check, well, that's the segmentation fault, there's our term. So for example, if this was our weird looking address, so we want segment one and then semicolon and then offset ff, which would be byte 255, well, this would be our segment entry. So it would be like, yeah, segment one, well, its base address is 2000, and its limit is 1ff, so any address within that is valid. So ff is less than 1ff, so that is a valid offset. And then if you want to translate that address, well, I just add the offset you gave me and then add it to the base. So that would translate to physical address like 2000 ff. So nothing terribly surprising here, hopefully. So the way that this is used nowadays, so things don't disappear. So at least if you have an Intel CPU, segments are still a concept, just in case you want to use them. But for Linux, they said screw that, and to get rid of this system, basically you just say the base address is 0 and the limit is the size of your memory. So it doesn't do any translation whatsoever, it completely ignores that part of your CPU, so no longer used anymore. So on our systems now, we have like a 64-bit virtual address, which is huge, right? 2 to the 64 is an astronomical number, so just for reference, like 2 to the 30, that's gigabyte ranges. So no one here probably has over a terabyte of memory, so that would be like 2 to the 40. So you can address up to 2 to the 64 bytes, which is a lot of memory. So typically you don't need all of that. So typically CPUs will let you pick how many virtual addresses you want to map, because it is a bit more costly to map more. So typically you just kind of pay for what you can use, so you just use enough that you can get by and have some room for some future expansion. So for example, on like your Intel CPU, an ARM CPU, or RISC-5 CPU, they typically use 39 bits of virtual address space. And in RISC-5, they call that SV39, just the way, just how to address it. It's just 39 bits of virtual address space. So 2 to the 39, well, if 2 to the 30 is a gigabyte, then it's 2 to the 9 gigabytes, which is 512. So if we have 39 bits of virtual address space, we can address up to 512 gigabytes of memory, which is probably more than anyone has. So that's probably good enough for now. If later, we start having issues with that, we would increase the number, and we'll see what we would increase that number to. So the whole trick to all of this is we just have page tables. And all a page table is, essentially, it's a lookup table. It's a big array. So everything is indexed by virtual page number, and it's just a lookup. So the whole lookup answers the question, well, if I have virtual page x, well, what physical page should I actually look at to get that information? So that's all it has. And this is kind of, and this is what it looks like. So at the top there, we have our virtual address, which is 39 bits. So we don't have to translate within a page. That's our hack around not storing translations for absolutely everything. So the size of a page, we wouldn't translate where on a page the byte is. We would just translate page to page, and it would be at the same location no matter what the page is. So that's what that offset is. So the offset is the size of the page, yep. So segmentation schemes don't really deal with pages. They just do the addition, and that's it. So this is what's actually used now, yep. Okay, so yeah, so the 12 bits of the offset, that has to do with our page size. So if we have 4,096 bytes, well, that's to the 12. So the lower 12 bits, that's where you are in a page. So you don't have to translate that at all. So we leave it alone. And then you use the other 27 bits of the address as the virtual page index. And that will tell you what index I should look up to find whatever physical page that this virtual page represents. So the easiest way to do that is you just take that index and just put it into a big table. So an index into a huge table. So the size of this table would be 2 to the 27. So there's 2 to the 27 entries, because I have to be able to translate possibly every single virtual page number there. So I have to have 2 to the 27 entries in there. And in each entry, all I really care about for now is what physical page number does that entry refer to? So somewhere it's going to say what my physical page number is. That's what PPN is. So I would use the index to go into the table. In the table, it tells me what the physical page number is. And then to figure out the physical address, well, that's what the physical page is. So I just copy that into the address. And I leave the where we are within a page alone. I don't have to translate that. So any questions about this? Yep. So use the index in the virtual address to index into that table. So that table, it's just full of physical page numbers. And all you do is use the virtual page number to find the index or use the virtual page number as an index. And then that's how you find out your physical page number. Yep. Yeah, so the question is, do all the virtual addresses line up? Like are they all contiguous? Like all the pages contiguous? And the answer to that is, well, what you've been told before wasn't a lie. That contiguous memory is faster. So the operating system will try to do that. But it doesn't have to if it divides everything into pages. So as soon as you cross that page boundary, you don't have any control over it. It might be a page way off in Zimbabwe or something like that. So you have no control over it. But the operating system is trying to make everything as fast as possible. We'll try. Yep. Oh, yeah, that's a good question. So the question is, how do I differentiate between different processes with the same virtual address? So that page table, that's a lookup table. That would be independent per process. So each process would have to have its own page table. So if it's accessing or address 1,000, well, then it has an entry for that. And then another process would have its own page table with its own different entry for 1,000 that points to a different physical number. Yep. So the question is, where is the data stored? And the data would just be stored in physical memory somewhere, which is kind of like a little bit of a catch-22. But the kernel, when it boots, it has access to physical memory, so it gets to decide where it goes. Yep. Sorry, the index is only. Yeah. Yeah. Yeah. So in this case, we see that, hey, my physical address, well, it's only 56 bytes, or sorry, 56 bits. So if you actually had 64 bits of address, you wouldn't be able to use it all. So there's some room there as wiggle room for future expansion. So this, yeah. So you'll note that this does translate like a 39-bit virtual address to a 56-bit physical address, which doesn't use all the space. But yeah. Yeah, so we'll get into the flags in a bit later. Just brief one sentence or like read, write, execute. So can I read memory from this page? Can I write memory to this page? Things like that. OK, so we'll go into some example. So here's like a simple page table. So our simplest page table, all it would have is like mappings of virtual page numbers all sequentially. So think of it as an array. So you could just write out the value for each element in the array to physical page numbers. And the physical page numbers could be all over the place. They don't have to be in the same sequence. They don't have to be anything. So in this case, our page table says that, hey, entry 0 goes to, if I see virtual page 0, that's physical page 1. If I see virtual page 1, that is physical page 4, so on and so forth. So let's write it out here, and I will switch to it. So let's see what address is trans. So let's go to it. So if this is our page table, so it's saying that virtual page 1 should be physical page, or virtual page 1 should be physical page 4, for example, which would be this entry. And that's all it says. So if we were to translate addresses, part of all these problems, you'd be given a page size. So if you're not, you would assume the default page size. So that offset within a page that you don't have to translate would be to the 12, because that's how many bytes are in a page, so I don't have to translate essentially the last three hex characters. So if I need to translate this, which is a virtual address, well, I don't translate anything that actually fits on the page, so where on the page I am, I don't have to translate that. So if I have to the 12 bytes in my page, I don't have to translate the last 12 bits, which would be three hex characters. So to translate 0, a, b, 0, well, I don't have to translate a, b, 0, because those are the last three digits, or last three hex characters. So this right here would be my virtual page number. And if I want to translate my virtual page number to a physical page number, I just look it up in my table. So if I see virtual page 0, that's physical page 1. So then you would just replace the 0 with a 1. So this would be my physical address. All right, so any questions about that? Yeah, so why we don't translate the last three numbers? Again, remember the intuition where we pop, when we do these translations, we couldn't possibly translate every single byte, because that would just, for every byte we translate, we need like eight bytes to store the translation. So that would be really bad. So we only translate things in big blocks of memory at a time. So if we translate a big block of like 4,096 bytes, well, if you access byte 20 on that page, you don't care where it is, like where that page actually is, byte 20 is still byte 20. So you don't have to translate it. Yeah, so the page size basically is like the lower index of it. So because we have a 4,096 page size, well, if we just rewrite that in the power of those of two, that means we have two to the 12 bytes, and we need 12 bits to be able to translate that many bytes. So that's how many things we can basically just ignore that aren't part of our translation. All right, anyone else? Okay, so the rest of these should be like really, really simple of just going through. So to translate this virtual address, well, it's the same thing. I take the last three because of my page size. I get FA0, and then this is my virtual page number. So I just look it up in the table. So virtual page one goes to physical page four. So I just write it out. So if you try and access, you know, virtual address one FA0, its actual physical address is four FA0. So any questions about that? It's pretty much the same for all of them. Like for this address, while you do copy the last four hex characters, and then we look up in the table what two is. So it would be 3884. And then similar for the last address, we can copy 2D0 and look up in the table what virtual page number three is supposed to be, and it's supposed to be seven. So this physical address would be seven 2D0. So any questions of that before we get a little bit more complicated? Yep. Oh, okay. So the question is, so we have our virtual page numbers. They're indexes, they're all unique, but do the entries here have to be unique? Is that your question? Yeah, so if I do, oops, that is a big eraser. So if I do something like that? Yeah, so if I do something like that, can I do that? Of course you can. But what did I kind of create by doing that? Which might be a bit confusing to people. So if I do something like that and say I access address, I don't know, zero, three, three, seven, and I access address zero, two, three, three, seven. So those should be two completely, you would expect them to be different addresses, but if you do this translation, well, the first one translates to zero, one, three, three, seven, and this one also translates to one, three, three, seven. So if you use either address, you get the actual same byte and if you change one, it would change the other. So that is something you are allowed to do and you can even do that in your programs. Sometimes this is actually useful, sometimes this is a headache, but you're absolutely allowed to do that. Yeah, the thing about page tables is because of this, they can map to the same page if you want and then hey, suddenly we're sharing memory, right? There's two addresses that are shared. So if you change one and read from it later, you see the change. So you can kind of see how right before we said that each process would have its own page table. Well, if I have the exact same entry in both of their page tables and they point to the same physical memory, well, one change would affect the other. So you can see how we might be able to share memory. So that was a good question. Yep, sorry? Oh, so the AB zero as a whole is just the virtual address. So the virtual address, if we go back, yeah, so the AB zero is just the virtual address. So we broke it up like this. So what I have one AB zero, well, the AB zero is just the part of the offset there. And then the index was just zero and I didn't write like the preceding zeros. I could have done like zero, zero, zero, zero, but no one does that. All right, any questions about that before we get into a more low level example where the page size isn't exactly fit with thing, doesn't fit within a hex character? That makes it more fun. Okay, so I'll leave you with this for a second. So here's just like a typical page translation problem just to make sure we understand things of like a really weird system. So we can assume we have like an eight bit virtual address, a 10 bit physical address and then each page size is 64 bytes, which isn't very big. Then you should be able to answer these questions like how many virtual pages are there? How many physical pages are there at maximum? And then how many entries are there in the page table and then given a page table, translate the address F one. So I will give you a minute and then copy it out and then we can answer it. Okay, so who's any guesses for the first question? The number of virtual pages, yep, four? All right, great, done. Everyone agree with four? Can you please explain, yep. So I was guessing that. So how did he get four? So first, what you always want to do with this is like do powers of two just to make your life a bit easier. So first thing you might want to make a power of two because we want to divide in, or we want to divide off the offset and then the index within our virtual page, like we want to see what the breakdown is. So remember the offset corresponds to how big the page is because we don't have to translate that. So what is 64 in powers of two? Six, yep. So that 64 is to the power of six. One good number to keep in your head of like in memory to do this fast is you don't have to remember all the powers of two, just the important one. So to the power of five is 32. And then from that you should be able to figure out what 64 is, it's just times two. So if you kind of have little benchmarks like that you can at least ballpark what it's supposed to be. So 64 is to the power of six. So if we were to draw that diagram of breaking down our eight bit virtual address, well, now we know that the offset part of that, well, I need six bits to be able to address every byte within that page because the page is 64, so that which is to the six. So I have eight bits for my offset. So everyone okay with that? So if I have eight bits for my offset, or sorry, yep, sorry, I have six bits for my offset, yep, yeah, sorry. So if my virtual address is eight bits, that's where that eight comes from and my offset is six bits, while doing a bit of subtraction, how many bits do I have for my index? Eight minus six, two hopefully. So if I have two bits for my index, well, I can only, everything in powers of two, if I only have two bits for my index, then I can only essentially index two the two things, which is two the two is four. So I could only address, and these are all different pages, so I can only have up to four virtual pages. So that makes sense, so it's your index, yep. So the offset is I just took my page size, which you would be given. So here it's 64 bytes in a page. So because memory is byte addressable, so I need to be able to index 64 things there, so I need two the six different numbers, right? So if I need to index 64 things, I need to be able to index, like, represent zero all the way to 63, right? So if I need to represent zero to 63, how many bits minimum do I need to do that? Six, yep, so that's why I use six. So offset is just where I am within a page, so that's why they call it offset, so you don't have to change it, it's just where I am within a page, and that doesn't change. So it's just typically use the word offset whenever the base can change and you don't care. So in this case, my base address is where the page starts and it's gonna be different depending on, if I talk about a virtual address or I talk about a physical address, it might change, but the offset never changes, so it's where I am within a page, it doesn't matter if it's physical, it doesn't matter if it's virtual, so that's why I give it a different name. So in this case, I can have up to four virtual pages because that's how much room I have. What about four physical pages if I do this? Yep, so you said 12, 10? Yep, so number of physical pages at most we could have is 16 and that's do the same reason. So if we took our 10-bit physical address, which is a different size, well we're still dealing with the same page size no matter what we're dealing with. So the offset part of it that I don't have to translate would again be six bits for the same reason and then if I do 10 minus six, well I have four bits left over here that could represent different physical pages. So two to the four, I could represent up to 16 different physical pages. So you'll notice that this number is a bit, we have a different physical number and different virtual number for the size. You can kind of see a bit of intuition behind here. If this is for different, if there's a page table per process, it makes sense that it would be lower because they have to share physical memory at the end of the day. So if a program was loaded up, well if I have 16 physical pages and each of them we're using like this entire address space of four physical or four virtual pages, well it means I could have like four processes using as most all of its memory that it can and things are still okay and I haven't run out of memory yet, I'm like at my limit. Okay and then next one is how many entries are there in a page table? Well remember our page table is just keeping track of the number or doing the translation for virtual page number to physical page number. So the number of entries that are in the page table is the same as the number, maximum number of virtual pages I could have. So that is also four. Now the fun part, if I translate the address F1 using that page table. So unfortunately for this, the offset is six bits, which does not nicely go into a hex character being four bits. So unfortunately I have to write out that address. So what is F1 in binary? Yep, yep so the address F1 is equal in binary to one, one, one, one, zero, zero, zero, one. And if you get into trouble you don't need to know how to do this fast and you're like oh what the heck is the translations for like A, B and C? Well again like the two to the five being 32, well a good one to know that's fun is A is just one, zero, one, zero. And then from that you can probably figure out the rest of the numbers greater than 10 if you know your ABCs. So sing with me, A, B, C, D, E, F and, thankfully I don't know what's after F and I don't have to, so great. That's why I went into computing because I did not do very well at English. So do I figure out what binary is for all the other ones? Well I can just add one each time. So B is one, zero, one, one. C is one, zero, whoops, sorry. Ah, one, one, zero, zero. D, one, one, zero, one. E is one, one, one, zero. F is one, one, one, one. So you should know F is just all ones too, that's another easy one to remember. So if I'm doing this translation of the address F1, well I don't have to translate any offset bits so the lowest six bits I'm gonna leave alone. So these are my offset and I'm not touching them and this is our virtual address. So if this is an index, what is one, one in decimal? I see some threes, yep, three, so that's three. Remember it's all powers of two so that's the one's column, the two's column so two plus one, three. So if I look up in my page table here, what entry should I be looking at here? Three and that means it is physical page number eight. So how do I write eight in binary? Yep. Yeah, eight is just one and then three zeros so if I do the translation here, it's just a lookup table so I'm replacing three with eight so or I'm replacing one, one by one, zero, zero, zero which would be eight so that is my new index part and then the offset I don't touch so it's still one, one, zero, zero, zero, one. Now if I want to bring this back to hex I'll just start grouping things starting all the way over on the right. So this would be a hex character, this would be a hex character and this would be a hex character and then you can see I actually nicely designed it so you don't actually have to add too many powers of two or do anything that weird. So zero, zero, one, well if I write this back to hex that is one, zero, zero, one, one, well that's just three and then one, zero that's just two so this is my final physical address so using this page table, if you tried to address the virtual address F1 that would correspond to physical address 231. Yep, so yeah, so in this case, if that eight bit is more than what my machine can take I can support, like any program can use all of the memory and it's fine, so that's it. So I'm constrained by this so if I had, so because I have an eight bit virtual address I'm assuming my machine has like 256 bytes of memory which is a kind of crappy machine and if it had more than that I'm kind of, I can't use it all, right? Unless multiple processes use more but then it kind of sucks. No process can use all the memory and typically you want processes to be able to use all the memory if they can. So yeah, so but you can kind of see here as well that if I had a bigger virtual address well then I have to index more things and that takes up way more space. So that's kind of what our trade off is that we're aiming towards. All right, so any quick questions based off that? Okay, so here's what a page table entry actually looks like. So we kind of argued that at each virtual page number what you should look up is a physical page number which is the majority of that entry but you might see that there's a bit of a mismatch here. So for that physical page number I essentially need 44 bits from it and we're still using memory. So you can't store units of 44 bits. So you typically just round up to the next nice powers of two. So these entries would be like 64 bits or eight bytes. And if I only need 44 bits for the physical page number I have a bunch of bits left over and the intuition behind that is hey, since I have a bunch of bits left over let's use them for something useful. So that's where all the permissions get stored. So they get stored in the page table itself and this is the actual like specifications for a risk five CPU, they're all pretty similar. So the ones we care about in this class are the virtual bit, the read bit, the writable bit and the executable bit and maybe the user bit. So that will actually check the permissions when you do the translation and if you read or write it will check the permissions to make sure you can actually do that operation and the valid bit means that hey, that actual translation there is valid or it's not. So in your program, when you're accessing virtual memory you have to tell the operating system that and it would mark it as being valid and do the translation. If that bit is not valid and you're trying to access just a random virtual address then that's why you get the seg fault because that valid bit wouldn't be on and then it would just fail and then the operating system would tell your program hey, seg fault and then kill it. All right, so I'll skip this real quick. So here's our problem. So each process does get its own page table that we saw and when you fork it would have to copy the page table as well. So we're tying it into forking. So we'll get into some tricks you can do and make that efficient. But the problem here is with our actual system if we have a 39 bit virtual address then we have two to the 26 entries in our page table and if our page table entry is eight bytes well if we multiply those together so two to the 27 times two to the three that means it would be two to the 30. So each page table if we did what we just outlined there each single page table for each process would be one gigabyte. Yikes. Imagine if you could, how many processes could you run that would just be page tables if this was like an actual thing? So if you had like eight tabs open Firefox that's eight gigs of memory for all those page tables then you're out of memory. So clearly that isn't a thing and we'll learn what actually happens later where it gets way more complicated. So just remember, phone for you, we're all in this together.