 Okay, welcome to November. We're one lecture away and then we have a reading week. So that's pretty exciting. And hopefully reading week won't be too terrible. And yeah, I was gonna try and bring leftover Halloween candy, but I got cleaned out last night, so that's unfortunate. So I actually got cleaned out by like 7.30. But today we're going to be talking about more page replacement and the clock algorithm, which was kind of that approximation of least recently used, except we're actually going to be able to implement it. So here's the algorithm in all its glory and the rest of the class, we're just going to go over examples again and again. So the data structures it has, it just has a circular buffer of pages. So it keeps a circular list of pages in memory or buffer and it just uses one bit to keep track of whether or not it is referenced. So every page just has one bit associated with it, whether or not it has been referenced somewhat recently. And whenever I show the algorithm, I'm going to put the reference bit in the light gray box. So the light gray box will either be zero or one. So then the other thing it has, it has a circular buffer and each page has a reference bit and then the final part of it is there's an iterator, which you can think of just as the clock hand. So it just essentially just has an iterator pointing to the last element examined, which would be a page. So then the algorithm works like this. So if you need to insert a new page, if I need to swap a new page into memory, then I check the hands reference bit. If that page is currently a zero, then I delete it and I swap in that page. If it is not a zero, which means it's a one, then I just change its reference bit from a one to a zero and then advance the iterator one position. And then I just keep going on like this until eventually I find a place for it. So that's all it does. And whenever you reference a page, whenever you access it, it's just going to set the reference bit to one, which keeps track whether or not it's used. So it's better than actually doing all those pointer updates. All we have to do is write a single bit every time we do a page access, which is more realistic to do. So that's it. And whenever you insert a new page, you would advance the hand after you place it, essentially to keep that page, make sure it doesn't get its reference bit set to zero whenever the next thing comes in. So you want to advance the hand after you swap in that page. So this is what it's gonna look like. So this is our question. So we're going to assume, again, physical memory can only hold four pages and then we have the following accesses, one, two, three, four, five, two, three, one, two, three. Again, we're always going to assume that all pages are initially on disk. So in this example, this is what our clock would look like. So we have four positions and it's a circular buffer. So each one is going to point around the clock and you can draw it in a nice little circle. So it actually looks like a clock and the whole thing makes sense. And then all the reference bits are zero and there are no pages loaded in. So we'll just say in the light box that, there's no page there, so we'll just give it a zero and then all the reference bits are zero because they're in the light great box. So now we just follow the algorithm from the last slide. So if we access page one, we have to swap it in. Our current hand on the clock points to a location where the reference bit is zero. So we get to replace it. So we would swap in one, set its reference bit to one and advance the iterator of the clock. So this is what the clock would look like right after we access the first page. So now it is in memory. Everyone good so far? So same thing is going to happen when we access page two. It's pointing at a spot where the reference bit is zero. So we're going to insert it, put the reference bit as one and advance the iterator one spot. So this is now our clock. After the second access, it is now in memory. So in memory, we now have pages one, two, and then both the reference bits are one. So this is going to look a little boring. So page three comes in. We're pointing at something with a reference bit of zero. So we would insert that. And now we have page four. So same thing happens. It would have a reference bit set as one and we advance the clock. So now when we access page five, then a lot of really boring stuff is going to happen. So now we have to follow the algorithm like it said before. So if it's pointing at something where the reference bit is one, we can't get rid of it because we're trying to approximate least recently used. So we're going to essentially give it another shot at life. So when we first access page five, we're pointing at something where the reference bit is one. So we're going to change it from one to zero and then advance the iterator. So we would do that. Now we're pointing at something else where the reference bit is one. So we would advance it again, set it to zero. Now again, we're pointing at another page where the reference bit is one, set it to zero, advance it again. And then we're going to do it again. So now we're pointing at another location where the reference bit is one. We'd set it to zero and we'd advance the hand. Now at this point, it's pointing at a location where the reference bit is zero. So we can replace it. So we would replace page one, we would swap it out with page five, set its reference bit at zero and then advance the hand. So our clock would look like this right after our access to page five. So now we would access page two. So right now its reference bit is zero, but we just used it. So we should update this reference bit to indicate, hey, we're actually using this recently. So from the access in two, instead of like FIFO where it did nothing, we would update the reference bit to a one. And then our next access is to page three. So again, with page three, currently its reference bit is one and we just, or sorry, its reference bit is zero and we just referenced it. So we should change its reference bit to one. So now after the access to three, it would look like this. And again, on references, we're just trying to do the minimum possible. So that's why we don't touch the hand at all or do anything with that. We just set a bit if it's not already set. So now when we access page one, now something more interesting is going to happen. So if we access page one, well currently the iterators pointing at page two, its reference bit is one, so we can't kick it out, but we can change the reference bit from a one to a zero, advance the hand. And similarly for page three, we're also not going to kick it out because we just referenced it. So this is good that we're keeping track of it. So now we would change its reference bit from a one to a zero, advance the hand. And now we have page four. So whenever we have page four, we go ahead, its reference bit is zero, so we can replace that with page one. Yep. Sorry, when we check the three. On references that are already in memory, you just update the reference bit, you don't touch the hand at all. Yeah, you only check like what the hand's pointing at whenever you know you have to swap in. So whenever it's not in memory for which one, sorry. Yeah, so like there'd just be a reference bit associated with a page. So here when we access page three, all it would do is change the reference bit for page three from a zero to a one, and that's it. It's not gonna monkey around with the hand or do anything because we're just keeping track, we're trying to approximate least recently used. So we're just keeping track whether or not it was used or not. So we just set one bit. Yep, sorry. So this is what it looks like after we swapped in five. So here right before, this is like, we're looking at page two, its reference bit is zero. We know this one's going to get swapped out. So when we swap it out, we swap in page five, its reference bit is one, and as part of putting it into memory, we update, we just move the iterator one. Because so right here, after we insert page five, the iterator points to the element after the five. If it pointed to the five, that would be kind of silly because next time we try to swap in something, it would try and replace the thing we just put in. So we wouldn't want it to do that. That's why after we update it, after we put it in, we advance the hand one, and then this, whenever we access page two, it's already in memory. So we don't want monkey around with the hand, that would just be too slow. All we want to do is swap, or yeah, is change one bit. So we'd only change one bit, so for the access for two, it just changes its reference bit from a zero to a one. When you find it, if it's in memory, all you do is set the reference bit to one. Yeah, when we access two, we didn't. So right here, this is what it looks like right after we inserted five. This is before we know about two. So we didn't, access to two didn't change the hand at all. So whenever we insert, we put the iterator one past whatever we just inserted. So access to two didn't touch the hand at all. So the only thing the access to two did, so this is what it looks like after the access to five, and then as soon as we touch two, all we do is change the reference bit. So the reference bit goes from a zero to a one. Yep, and then for the access to three, again, same thing. So three is already in memory. We don't want a monkey around with the hand at all. Do any slow operations on every access. We want to make sure that whatever we do for each access is gonna be really, really fast. Flipping a bit is pretty fast. So after we access three, its reference bit is updated from a zero to a one, and now we access page one, so now we have to swap something out. So this is where we would start monkeying with the hand. So the hand's currently pointing to something with the reference bit as one, so we would just change one to a zero and advance the hand. Now it's pointing at page three, reference bit as one, so we would change the one to a zero, advance the hand, and now we're pointing at something that we can replace. Well, whenever you need to replace something, the first element you'd look at is whatever the hand's currently pointing to. The whole monkeying with the iterator and using the hand only comes in when it's not in memory and you need to swap it in. This only runs whenever it's not in memory, so that would be just part of the kernel that keeps track of, like it has to do the swapping so it knows what pages are where. So this would all be handled in the kernel. Okay, so now for access to one, now we're pointing at page four here, its reference bit is a one, or sorry, its reference bit is zero, so it is now gone. So we're going to swap in four with one and its reference bit would be one and we would update the hand to point to the next thing. So after we resolve the access to one, this is what our buffer, or sorry, this is what our clock would look like. So we'd be pointing at five and the reference bit would be one for page five, one for page one, and then page two and three have zero for reference bits. So now the other accesses we're gonna have, we're gonna have an access to page two. It's already in memory. All we do is update the reference bit from a zero to a one and now for page three, same thing, it's already in memory. We just update the reference bit from a zero to a one. Yeah, well so the arrows around the clock are just to show that it's a circular buffer but it could just be an array or something like that. Yeah, this is mostly to illustrate so it looks like a clock and it's all cool and stuff. Yeah, so all the pages would actually be used for something so you couldn't just reserve a bit on a page. Yeah, they'd be stored somewhere else. So they'd be, as part of the bookkeeping, they'd be stored somewhere else. So every page you have would have a bit associated with them which is like how many bits are in a page a lot. So just one bit per page which isn't that bad so that doesn't add to storage as well and make the bookkeeping just untenable. We'll see kind of the bit counting you can do when we get to file systems. So whatever we learn with file systems and keeping track of essentially file systems also keep track of blocks which look a lot like pages. So we'll see some of that when we talk about file systems. So any questions about this? We'll see another example as well but any questions so far? Okay, so, yeah, the kernel if it used this algorithm would have just like a reference table that it would keep track of all the bits. Okay, so if we write out all the page faults here for it, well for the first access we swapped in one, then second one we swapped in two, then three, then four. Then for page five we replaced it with one and again this is if you were actually doing this you would write it while you had the clock so it would make more sense. So for the access to five we replaced page one and then remember when we did access to page two and three since they're in memory it would also update the reference bit and then when we accessed page one that's when we had our miss and we replaced it with page, we replaced page four with page one. So, and then the next two accesses we had in memory. So if we count this we had six page faults. Okay, so I guess for practice, so if it was six we can go ahead and check whatever the optimal was. Whoops. So if we had the same accesses what would happen if we did the optimal strategy? So for the access to page one what are we gonna do? Yeah, so we're assuming that there is nothing in memory so we would swap it in. So we would just swap in page one and then it's gonna look the same thing. So for page two we would swap it in. I'm going to skip some steps. One, one, two, two, three. So for our first four accesses this is what it's going to look like. And so always going to be pretty much the same and now we have access to page five. So it is not in memory. So if we are doing the optimal strategy what should we kick out? I see a four, everyone agree with four? So let's see, would it be a four? So we're currently accessing page five while the optimal algorithm just looks in the future and replaces whatever page is not going to be used in the longest time. So between one, two, three and four it's not going to be two, it's not going to be three and it's not going to be one, therefore it's going to be four. So we would swap out four with a five and that is another page, like that's another page fault. Now, since we use the optimal, thankfully whatever we access page two everything's already in memory. So we have a hit and then when we access page three also going to be a hit because again we use the optimal thing and then when we access page one again then it is also going to be a hit and then again we access page two it's going to be a hit again and then page three is also going to be a hit as well. So in this case our optimal, we had a total of five page faults, so pretty good. So with clock we did six and with optimal it was five. So let's go ahead and look at a bigger example and see how well we understood the clock. So here's our accesses and we'll do this using the clock. So I will draw one over here and again we'll just assume that there's only four pages in memory. So if I draw my little clock it would look like zero, zero. So this is what it would look like initially. So now if I access page one what's going to happen? Yeah so I put one in so it is now in. So page one is in memory now and I change its reference bit to one and what else should I do? Move the hand by one. So I move the hand by one so that's what it looks like right after the access to the first page. So I just swap that into memory so I will record that. So now we have a access to page two. What's going to happen is put it in again and advance the hand, same story. So we put it in, its reference bit is a one. We advance the hand and now after that we have swapped it in. Now for the access to page three well it's going to be the same thing again so we swap it in, set its reference bit to one and then advance the hand. So now we've swapped it in. It's going to look boring because for four it's going to do the exact same thing. So two, three. So the current hand's pointing at something with a zero we're just going to put it in. We swap in page four so that's reference bit to one and then go along advance the hand. So now we swapped in page four. So now what's going to happen when we access page one? So it's in memory, do we have to do anything? Would we have to do anything if its reference bit was a zero? Yeah you'd have to update to one but in this case it's already a one so I'm good I can just enjoy this page hit. Now I'm going to access element two. Element two is also in there. Which reference bit is already a one? I don't have to bother to do anything again. So we have another hit. So now when we try to access page five it is not there, what should I do? Yeah so I'm going to be looking just advancing the iterator until I'm pointing at something that is a zero. If it's currently pointing to something with a reference bit as a one I change the one to a zero. So this is like when we go around the entire clock so page one that is currently pointing at its reference bit was a one we would change it to a zero and then we would advance the hand. Now same story we're going to change the one to a zero and then advance the hand. Again one to a zero advance the hand one to a zero advance the hand. And now we're finally pointing at something a page that has zero for its reference bit. So in which case we would choose to swap it out. So we've chosen to swap it out so we put in page five, set its reference bit to one and then we advance the hand when we're done. So now we swapped out page one with page five. So now our memory or yeah the pages in memory are five, two, three and four. So now we will have an access to page two. So that is in memory is there anything else I need to do? Yeah changes reference bit from a zero to a one. So I did that. So now we're looking at the access to three is also in memory. So I would have five, two, three, four. What do I need to do? Yeah change the reference bit again. And I don't monkey with the hand or do anything slow. So now we have a access to page five. It is in memory and its reference bit is already a one. So we don't have to do anything. Similarly for an access to page two its reference bits already one. We don't have to do anything. Now we would have an access to page four or sorry, page one. So we have, oops, that looks ugly. So we have to pick something to get rid of. So currently the hand is pointing to something with a reference bit a one. So we would change it to a zero and advance the hand. Now page three reference bit is also still one. So we would change it from a one to a zero and then advance the hand. Now we're pointing at something where the reference bit is zero. So we get rid of it. So we replace four with one and then sets reference bit to one and advance the hand. So now we replaced four with one, two, which was kind of unlucky because we were actually using page. We just.