 All right, good afternoon. So following the success of lab five help in order to get you out and finish the lab, Wednesday we'll do lab six help. So read it, all the test cases are there. It's fairly straight forward-ish. Lab five will actually help with it. So it's just file systems instead of page tables. But fundamentally it's the same thing. We're just pointing at pages of memory. But this time we're using iNodes instead of just nothing, instead of multi-level page tables. So read it, we're doing a file system from scratch. So you're actually implementing your own file system. You're creating a regular file, a directory and a sim link. And I already give you a directory to follow and we'll start, we'll open the code, we'll start experimenting with it. And hopefully that gets you doing lab six and finishing lab six, well before likely your hell week in the last week. So hopefully you get a jumpstart on that. So where we left off on page replacement is we looked at a bunch of different page replacement policies where if we're running out of memory we can use disk kind of as, or use memory as a cache for a disk and pretend that we have a lot more memory when really we're just using a disk for any spillover. So we saw some page replacement strategies. We saw the optimal one. We saw FIFO, our friend and least recently used. And we argued that least recently used you can't really implement and it's not realistic. So this is the first actual realistic one we will see. So it is called the clock page replacement algorithm and we will quickly see why it is called the clock because I had to draw a little clock for you. So that did not follow what? Come on. Hello. All right, switch my slides, but this did not switch. That's cool. Okay, close open. There we go. Okay, sorry about that. So here is the entire algorithm. So it has a few data structures and then the algorithm is actually surprisingly simple. So the algorithm is it keeps a circular list of pages that are currently in memory. So it's just an array that when you reach the end it just goes back to the beginning. Next thing it has is a reference bit for each page that's currently in memory. So instead of keeping track of a queue of the least recently used pages, instead of keeping an ordered queue it just keeps track whether or not a page has been referenced relatively recently. So it's just a single bit and on the slides in the next few slides it will be in the light gray. So the only other thing it has, it has a bit which just is supposed to say whether or not memory a page has been referenced. And then the only other thing it has is an iterator or a hand or if you don't know what iterator is it's just a fancy word for pointer. And it points to the last element or page that has been examined and this is the entire algorithm. So to insert a new page if we have to bring a page in from memory and we have to select one to evict. First the clock is going to check its reference bit whatever it's pointing to. If it's a zero that should indicate that it hasn't been referenced in a somewhat long time and that is the page we are going to replace. So you just replace it and then advance the hand to point to the next available page. And then the other part of the algorithm well is if it's a bit we have two options. So if it's zero we choose to evict that page. If it's one we just set that bit from a one to a zero and then we advance the hand to the next potential victim page and then we just repeat that until we actually find one to evict. And then now for page accesses in order to approximate least recently used on whenever there's a page access we just set the reference bit to that page as one. So that's about the least we can do. The least we can do to actually keep track of what is actually least recently used. So this is an approximation. So the quickest thing we can do whenever there's a page access is just set a bit to one. So whenever we have a cache hit it will just set that reference bit to one and if this isn't apparent now why this is good we'll figure it out very shortly. So again, same type of question. So physical memory only holds four pages and then we have the following accesses in this order and then you assume all the pages are initially on disk. So initially there is absolutely nothing in memory and we have to bring it in and then this is our clock that I've nicely drawn out for you. So around the circle is pointing from one element to the next to the next to the next and initially it's all empty everything is zero. There are no pages in memory and then the little pointer in the middle is supposed to represent the hand that is the next element to inspect for our algorithm. So if we access page one and we follow this algorithm while this bot we're currently pointing to has a reference bit of zero so we should replace the nothing page with page one. So we replace the nothing page with page one set the its reference bit to one and then just move the hand to the next element and then now we can do the next access. So everything straightforward so far fun little hand, fun little clock. Okay, so if we do the first few accesses they're gonna be really boring. So if we access page two, well, same thing my clock is pointing to a position where the reference bit is zero therefore I can evict that nothing and I would replace that nothing with two. Set its reference bit to one, advance the hand. Now I'm going to access page three. Same thing is going to happen. I insert a three and a one, advance the hand then I access a four. So my next access is a four. Do the same thing, oops. Do the same thing, insert the four, advance the hand and now my clock looks like this. So I have one, two, three, four all in physical memory and now my next access is a five. You're going to find this is really annoying with clock because I'm essentially going to have to go around the whole clock initially to actually find something to evict. So if my next access is five and I have to follow the algorithm while what I'm currently pointing to the reference bit is a one so I can't evict it. So according to the algorithm I have to set its bit from a one to a zero and then just advance the hand. So I will not evict page one. Now I'll just move it one position. So everyone okay with that? Because we're going to do this a lot more time. So we're still pointing at a one. It's reference, we're still pointing at a page, page two, its reference bit is one. So we can't replace page two. So we would set its reference bit from a one to a zero then advance the hand. Same thing with three. So three, its reference bit is one. We would advance the hand. Same thing with four. So we're doing the whole world tour here and then four, its reference bit is one. We'd change it to zero and then advance the hand. So now eventually our clock looks like this where we're finally pointing at something where the reference bit is a zero. So we choose to evict page one from memory and put it back on disk. So we would replace page one with page five, set its reference bit to one and then advance the hand. So now right at the beginning of our next access, this is what the clock looks like. So so far it did the same thing as what FIFO would do. So now we will have an access to page two. So unlike FIFO, well this actually does something. So we have a hit on two, it is currently in memory but its reference bit is currently a zero but because we just access it and it was a hit, we actually just changed that reference bit from a zero to a one to indicate, hey, we used it and now the intuition behind that is if we need to replace something, well we would have replaced page two if we just did something like FIFO but now because we actually used two right after and we set its reference bit to one, now we wouldn't actually evict it. It's not the next one being evicted because it would go on and try the next one. So any questions about that? So we were just after the access to two. Okay, so same idea is gonna happen whenever we access page three. Its reference bit is currently zero. We change it to one and that's it. So nothing really changes except we change the reference bit. So now the only change between this and FIFO is now we can see it coming in whenever we access page one. So now we need to pick something to evict. If we had, if we just had FIFO we would have evicted page two but and if we did least recently used we also would not evict page. We wouldn't evict page two but FIFO would. So hopefully this does the same thing as least recently used or at least does the same thing most of the time. So now if we try and access page one and we follow the algorithm while we won't evict page two because its reference bit is currently a one. So we just change its reference bit from a one to a zero and advance the hand. Same thing, we can't evict page three. Its reference bit is one. We change it from a one to a zero. Advance the hand. And now we are currently looking at page four. Its reference bit is a zero. So we get to evict that. So now we evict four, replace it with one. So that's reference bit to one and advance the hand. So now our access would look like this after page one. So we chose to evict page was at page four. So now it is gone. Now we won't fall into that stupid thing of FIFO replacing something that we just had up. Oh, and then there's a question. Why did the reference bit for three change from zero to one? So it changed from zero to one because of the access right before. So we had an access to three right before so it would just change the reference bit to one. Okay. So now we have one, we've placed one and now the next access we have is to page two. It's currently in memory. So we change this reference bit to one and then carry on with life. Same thing for three, we changed this reference bit to one. So now everything currently in memory has been referenced relatively recently. So we would have to go around if we actually have a page replacement. All right. So any questions about that? It kind of looks like least recently used except instead of maintaining a big queue. Yeah, good. Yep. Yeah, so the hand only moves when you're actually doing the eviction and replacement. So the idea behind that too is for memory accesses you want the fastest thing you can do. So if I have to play with the hand, every single memory access that's gonna be really bad. So the idea here is to minimize what happens on memory accesses. So on memory accesses the most I do is I set a bit from zero to one. And then other than that I don't do anything else. All the actual meat of the algorithm happens whenever you are picking a page to evict. Yep. Yeah, a bit only changes from one to zero whenever you're doing the replacement and it survives around. So you can think of, you can also think of the bit as it giving, getting like an extra chance at life of staying in memory. So every time it referenced it renews that life but it only has one extra life. So if something hasn't been used in a while it wouldn't have an extra life and it'd be gone. So this isn't exactly least recently used. It just kind of approximates it. So they're not in order or anything. It's just like, yeah, it was kind of used recently. I don't really know the order in but it's actually fast to implement. All right, any other questions about this? Hopefully kind of boring because this is fairly, just a simple little algorithm. Okay, so if we drew it with all the page faults and everything, so same order. So this is just so you have it on your record of all the page replacements we did and that was just what was outlined in the last slides. So for those memory accesses if we do the clock algorithm and we want to answer how many page faults we have while you just answer how many reds there are or how many times we had to evict something. So in this case we have what six page faults. So if we want to compare this to optimal or well, if we want to compare this generally we compare it to optimal and if we want to compare it to optimal well hopefully we know how to do that so we can actually do that really quick or do you want me to just skip it? So more practice with optimal? Sure, okay. So real quick optimal is you replace whatever will be used furthest in the future in order to maximize your cash hits. Optimal is the one where you actually look into the future and you don't actually, it's not actually feasible to implement, you kind of just evaluate it after the fact. So I will cheat a little bit for my first four memory accesses because they will be boring. So I will draw how I've been drawing and I know I have to fill up my memory on the first four memory accesses because they are all unique. So one would be a page fault, two would be a page fault, three would be a page fault and four would be a page fault until I fill memory. So my first four accesses are all page faults and it would look like that. So any questions about that? Hopefully I can take that shortcut. All right, cool. So now we are at five. So if we need to access page five well we need to choose one of these to evict one, two, three or four. So if we look into the future and we want to evict whatever one hasn't been, is not going to be used in the longest. Well, we can't evict two because it is used next. We can't evict three because it's used after that and then we can't evict one. So then we only have one choice left that's in memory and that's four. Whoa, that's a big eraser. So I have to evict four. So I would evict four and replace it with five. So now in memory is one, two, three and five. So any questions about that? Cool, and then questions. Last two slides from the clock example. Why does the reference bit for three change even though I think, yeah. So anytime you do a memory access and it's in memory we flip the bit from a zero to a one or just keep it as one if it's not. Okay, so for the rest of the memory accesses, page two already in memory, so I don't have to do anything. And in fact for all of the other ones I can actually take a shortcut because all the other pages are already in memory so all of these memory accesses are going to be cache hits. So in total I don't have to draw them all so in total I will have five page faults. So five page faults, I got six with the clock, optimal is five, so that's pretty good. Okay, so to practice we can do another clock if everyone wants to do another clock. Sure, so let's practice the clock a little again if you get really bored. I mean we don't really have any other plans today so you can just yell if you really wanna go home and we can wrap it up because there's a few words at the end. Okay, so for clock first the question will say oh, how many pages will just assume memory can only hold four so we can draw our nice little clock here. So again I put what page is at the top and then the reference bit at the bottom and I'll draw arrows to indicate where the clock is and then I'll set the hand initially pointing to wherever I like it just pointing at the top but it doesn't matter if it's empty you can point it wherever you want. Whatever makes you feel good. So this is my initial clock, it currently has nothing in it and then I do all my memory accesses so if I access page one well I just follow the algorithm so currently I'm pointing at something that has a reference bit zero so I would replace that and it currently doesn't even have any entry so I replace it with one, set the reference bit to one and then advance the hand. So if I also draw the memory accesses whenever I access page one that would be a fault for page one. So everything is good, hopefully any questions about that. So skip a few steps because this is gonna be really boring when we populate it so next access is two it's gonna do the same thing. It's going to put a two in and then advance the hand. Now we have an access to three it's gonna do the same thing, pop in a three, advance the hand then we access page four, same deal it would put in a four with a reference bit of one and then advance the hand. So that is our first four memory accesses if we are cheating a little bit and going somewhat fast because that part is very, very boring. So we are at this point, any questions? All right, so now the fun one well not even that fun so now we are here here I'll point to it in blue. So now we're accessing page one it's already in memory so we don't need to do anything and its reference bit is already one so you can think of it if we change one to one if that makes you feel better or you can just leave it alone as a one up to you whatever makes you feel good. So that's a page hit nothing gets replaced and now we access page two. Same thing's gonna happen it's already in memory. So it's already in memory we got one, two, three, four. Now something more interesting happens where we access page five and now we have to figure out something to evict. So this is where you actually move the hand and change bits from a zero or sorry from a one to a zero. So we're gonna go around the world as I like to call it where we look at this entry its reference bit is a one so we can't evict it we have to set its reference bit to a zero and then advance the hand one position. So same thing's going to happen for this page we set the reference bit from one to zero because we can't evict it and we advance the hand and we're going around the world again. So same thing the reference bit is a one so we can't evict page three so we would set it from one to zero and then advance the hand same thing with four one to zero and then advance the hand. So now we're pointing at page one its reference bit is zero so we can actually evict page one so I would evict page one put in page five with a reference bit of one and then advance the hand. Whoops not one we just evicted one. Okay well here's a question for you so why if I put in page five why do I just put in page five and then also advance the hand can I just skip doing that step or is that a bad idea? So what about if instead of putting in five and advancing the hand I just put in five and left it there. Yeah next time I go through well it was used recently because I just put it in there and it would immediately just change that bit from a one to zero and it'd be like it's not used anytime and then it's up for eviction which you probably don't want so that's why we move the hand afterwards so otherwise it wouldn't really make sense because we would just try and evict it immediately after putting it in on the next miss so that's the intuition why we move the hand after we insert something. All right so good cool. All right so now we have an access to page two so this is where also from question from the discord this is where we set reference bits so we set references whenever something is accessed and it's already in memory so page two right now its reference bit is one or sorry its reference bit is zero and we just tried to use it so instead of updating the iterator doing anything all we're going to do is update this reference bit of zero to be a one oops I should probably draw it and then that's it so we had essentially it was already in memory it was a cache hit so and we used it recently in fact we just used it right now so we update it but it would look the same if we're just keeping track of the number of pages in memory. All right any questions about that? All right same things are going to happen we access page three so same thing happens its reference bit goes from a zero to a one because we just used it so that's good we didn't actually have to do any replacement everything is still in memory now we try and access page five page five its reference bit is already one so we could just leave it as it is or if it makes you feel good you can erase a one and then write another one totally up to you but same thing at the end of the day so five already in memory so this also looks like it kind of looks like least recently used because now we can see by just looking at the state of the clock that we're going to evict page four next because its reference bit isn't set and it's also the one we haven't used in the longest so same thing so next access is to page two same thing we already referenced it so again if you want erase the one put a one or just leave the one completely up to you so now we go on to the next memory access to page one and we actually have to evict something so we can follow the algorithm and actually pick one to evict so it's currently pointing at two its reference bit is one so we're not going to evict that we'll just set its reference bit from a one to a zero advance the hand same thing for three its reference bit is one we change it to zero advance the hand now four its reference bit is zero so it's out of here so we would replace four with one set its reference bit to one and then advance the hand because we just used it and we don't want to try and replace it immediately so this was a bad decision as we will see but you know we tried but it would do the same thing as least recently used so we replaced page four with page one and that was our page fault alright any questions about that while we go into the home stretch pretty straight forward hopefully so now we have to we're accessing page four it's not in memory we got to bring it in we have to pick something to evict well we're going to save five because it's we're just following the algorithm five was used somewhat recently so its reference bit was a one so we will change the one to a zero whoops and then advance the hand now we're pointing at something the reference bit is zero so it is out of here so we would replace two with four make its reference bit one and advance the hand and now we replace page two with four so now we have five four three two in memory that was our page fault and that is our final memory accesses so if we counted all the page faults well we have one here one here one here one here one here so we have the initial four five and then six seven so we have seven page faults alright any questions about that that was a longer one same thing so this is something that you know hopefully this is like pretty much a gimme on the final exam because this will probably be on that so go ahead you can write that down now if you want so this should will probably be on the final so you know go ahead practice if you need to or just you know hopefully it's fairly straightforward okay so a few other notes so this is kind of odd and for most systems now you'll find out that no one really uses this um because for performance you may not want to because this is kind of done behind the scenes where I'm kind of running out of memory and it's going to start swapping the disk which is going to be much much slower and then you can run into situations where the algorithm doesn't quite agree with your what you're doing and it's constantly swapping information in and out as you're trying to use it and it's just going to be really really really slow and nowadays memory is pretty cheap you can just run to the store and just buy more ram or download more ram I guess right so you can go ahead it's cheap what Google will do they'll just disable swap completely where they're just like yeah I don't want things to run slow just run out of memory and tell me and then if I see an memory message I'll just run to the store and buy more memory or just or just pay Amazon to use a system that has more memory on it or whatever so this is an option you can do in your kernel so you can just choose to disable swapping if you really really want to if you start adminning systems or doing that and then what the kernel will do is whatever you actually physically run out of memory and there is no swap Linux will or any good kernel will try and save you and free up some memory so it runs what is you know nicely called an out of memory killer and all that does is basically select a process that is using the most amount of ram and then send sig kill our nice you know dash or our signal that is like dash nine that if you send a process it's immediately dead it doesn't get to respond it just kind of terminates and then disappears from the world so that's what the kernel would do it would whenever it detects you've run out of memory it would be like okay hey chrome you're dead okay there you go suddenly 30 gigs of ram is free right so that so that's what it would do and that's always an option where you don't actually have to do this and then there's also different different paging strategies so even with that risk five that three level page table you saw what you're what you did in lab five or are doing in lab five currently depending on how on the ball you are so that's not the full page table there are other options for page tables so by default your page size is four kilobytes which is pretty lame especially for now systems memories kind of cheap so there are actually larger page sizes you can use even with that architecture and essentially they just chop off a level of page tables and use that to make a bigger page so before we were doing what 30 sorry we've had a 48-bit virtual address or 39 I think you did 39 right yeah 39 so when you did 39 that was three levels of page tables and then we had a four kilobyte page size well you could also choose without making too much disturbance is to actually just use a two megabyte page size so instead of being two the 12 it's essentially 12 plus nine so it'll be to the 21 so my page size now is to the 21 and then if you still use four kilobyte page tables you only need two levels instead and then your page is just much much bigger so on that architecture you can actually have variable numbers so in the l1 you could just choose to skip the l0 page table and say hey no this page actually points to a two megabyte page and then the next level up from that is you can actually have gigabyte pages which are might unsurprisingly be called gigapages and the two megabyte pages if you look it up I think macOS supports it too and Linux and everything they're called huge pages so huge pages are two megabytes and they just skip a level of page table so we only need two instead of three and there's other benefits to this because for the same number of entries in our TLB remember that the TLB is just a cache from virtual page number to physical page number well if my pages are suddenly much much bigger and my cache just holds you know x number of entries well if all my pages are bigger I have a bigger coverage so if I only have I don't know 10 entries well that covers 20 megabytes of space as opposed to if I only had 10 entries and I had four kilobyte pages which is only like 40 kilobytes of space so the drawback is there might be more fragmentation if I only give out you know a page to a process and it only uses 12 bytes in a page well now instead of wasting you know essentially a four kilobyte page I'm wasting a four or a two megabyte page so you're essentially trading fragmentation for more TLB coverage and slightly faster lookups as well all right so any questions about that cool so again as a recap clock algorithm that was an approximation of the L or least recently used page replacement strategy that we can actually implement and the reason that we can actually implement is because on the normal accesses it doesn't actually do that much all it does is set one bit which is quite fast but of course nothing's in order but it's just an approximation so yep yeah so worst case so on accesses it only has to set a bit but yeah you're right for actual eviction and replacement worst case it's order n because it would swap through it might have to search through everything once yeah so on hits it just it just sets a bit that's all it does on a hit so our order n only happens if we actually are evicting something and we actually have a miss which is okay because hopefully our misses are rare anyways so it's okay and we can't do that much better than it so data structures has to keep a circular list of all the pages in memory keep track of all the reference bits and then it would use a reference bit for each page in memory which again was like grain the slides then the only other thing it has is an iterator that points to the last element examined which is basically which page was it and what is its current reference bit and then the algorithm only had two steps because essentially only looks at the reference bit so if it's a bit it's either a one or a zero so if the reference bits currently pointing to and again this only happens on a miss if we need to find a page to evict so if we need to find a page to evict that's not in memory or and we need to bring it into memory well then we check what the clock is currently pointing to check its reference bit if it's zero we place it there then advance the hand after we place it otherwise if its reference bit is one we set it to zero and then advance the hand and then we just repeat until we find a one then have a question why do we not advance the hand when setting the reference bit to one wouldn't that be an issue where we need the page but but then set its bit to zero immediately in the next page so yeah so we don't monkey with the hand whenever we do any accesses you're right that it may lead the situations where we get you know it's not as good as least recently used but again for accesses anything we do have an access is like literally any memory access which would just make everything way too slow so setting a bit is about the most we can do so that's why we don't monkey with that's why we don't monkey with the hand or do anything else if it is already actually in memory so that's the goal here for memory accesses like the last point in the slide all we do set the reference bit to one that's it don't change the hand do not pass go do not collect 200 dollars so on and so forth all right any other questions whatsoever all right so we have some more time so you can ask about lab six or bring it up or so lab six is somewhat well it's pretty fun so you are going to make a file system you're and you're going to start it from scratch so it's going to be a you're essentially given a one megabyte file full of zeros and you are making a natural file system where if you try and use it you will see a file called hello you'll find a sim link and you'll find a root directory so you can actually actually see what's in it so there will be no secrets after you're done lab six and it basically yeah it has everything there covers allocation the uh i-node structure and what it points to so unlike multi-level page tables where we have to hop that you've already done that so this is real easy remember in the i-node structure some of the uh fields actually point directly to a block so you're just pointing directly to blocks because all the files are going to be really really small so they're only going to need a block of space so you don't have to worry about hopping loops or anything like that you're just pointing to blocks so hopefully it's not too bad all right anything else all right well just remember pulling for you we're all in this together