 Alrighty, welcome back to operating systems. So we get to talk about more page replacement today where we left off last time. We were talking about least recently used. We argued that we couldn't implement it because it would just be too slow, take too much time. So we have to implement a approximation of least recently used and it's going to be something called the clock algorithm. So today's lecture pretty chill because this is pretty much all we're doing today. So clock algorithm, data stretchers. All it does is keep a circular list of pages that are currently in memory for each page. It's really quick. Instead of having a whole timestamp or something like that just keeps track of one single bit. So they have a bit on them, whether or not they have been referenced. And when I write it out on the slides, it's the box in light gray. The only other thing the clock algorithm has is a hand, which is just an iterator. So it's pointing to one of the elements in the list. So the entire algorithm is this. You check the hands curt reference bit because it's an iterator to an element of the list. If that reference is one, the idea behind that is it might not be the least recently used thing, but it was least at least used somewhat recently at some time in the past. I just don't quite know when that was. So you check that bit, if it's zero, then you kick that page out of memory, swap it to disk. You place the new page there that you swap in, set its reference bit to one because while you just had to bring it to memory, then you advance the hand so you essentially do not try and immediately evict it again. And the only other option is if that hand is pointing to some page where the current reference bit is one, it means we just don't wanna get rid of it. It was used recently. So we just change the one to a zero and then advance the iterator one position and we go around and around until we actually evict something. And then the rule is quite simple for page accesses. We want that to be nice and fast. So if you access just anything on the page, all it does is set the reference bit to one. So changing one bit turns out to be really, really fast. In fact, we can't really do anything better than that. So there's this overly explained diagram here. So we'll assume again, like for the last page replacement, that memory can hold four pages and we assume currently none of the pages are currently in memory. They're all sitting on the disk and we just context switch in this process or something like that. So if we have the accesses to one, two, three, four, five, da-da-da-da-da-da, let's see what the clock algorithm will do and we can't just skip the first four steps because we would need to actually see what it's going to do. So since there are four pages I can fit in memory for the clock algorithm, I can just draw four elements in this list. So and a circular buffer. So they just point to the next one and eventually once we make it to the end, it points to the beginning. So you just go around and around in a circle over and over again. Initially nothing is in memory. I assume page zero refers to nothing and all the reference bits are zero. So if I'm accessing page one, well, what happened? So if I'm accessing some page one, if I just follow the algorithm, the hand is currently pointing at some entry where the reference bit is currently zero. So I would swap it into memory here. So I just put it in there, set it to reference bit to one and advance the iterator of the hand by one. So any questions about that? Yep, yep, yeah. So the two boxes, the one without any color on the background, that's the page number and the one with the light gray, that's the reference bit. All right, any other questions for this one? All right, now we insert access page two. What happens? Yeah, so the hand's currently pointing to something the reference bit is currently zero. So I put in page two there, set the reference bit to one, advance the hand and now I'm done. All right, so what happens when I do page three? Hopefully same thing, inserts it wherever the hand is currently pointing to because the reference bit is zero, changes it to a one, inserts the page, advances the hand. Now we access page four. Likely the exact same thing is going to happen, right? Insert page four, set the reference bit to one, advance the hand. Now this is fun. What happens when we access page five and we follow this algorithm? What's the first step I should do? Yeah, check the reference bit so I'm currently pointing at something where the reference bit is one, so do I get rid of it? No, I set the reference bit from one to zero and I advance the hand. All right, now I do that again. Okay, well, now I'm pointing at two, reference bit is one. What do I do? Yeah, reference goes to zero, advance the hand and you can see kind of looks like a clock now because we're gonna do the world tour in this case. So three, reference bit's currently one, so we set it to zero, advance the hand. Same thing with four, go ahead, advance the hand. Now what do we do? So we're pointing at a page, page one, it's reference bit is zero, so do we get rid of it? Yeah, we get rid of it, so we would swap out page one, swap in page five, set its reference bit to one and advance the hand now, so this is what the clock looks like after we have inserted element five. Any complaints, concerns, anything like that because well, it's a required thing, you get to do this on the final, yay, yeah. Yeah, just be an iterator somewhere in memory that the kernel would be essentially like a global variable in the kernel. Oh, okay, so let's see, what happens? Yeah, so the point of this algorithm is that while we can't implement least recently used, this kind of approximates least recently used because of that reference bit and we give, essentially we give these pages a second shot if they're about to be used or if they get used again because whenever an access happens, we set its reference bit to one. Right now it doesn't look like it's doing all that well, right? Yeah, reference bit just means whether or not we have used this memory. So remember at the beginning, so whatever we're using memory, we're using our program, the reference bit gets set to one whenever you access a page. So the kernel can at least keep track of that or your MMU system or whatever. So it can keep track if you're actually using a page so the idea is if it's used, I'll just keep, I want to keep it around in memory because I wanna kind of approximate least recently used but this isn't guaranteed to always evict the least recently used thing, it just kind of approximates it because it only gets one bit. Yeah, and hopefully that answered the question in the chat so anything else. So this is when it will change. So right now nothing really looks that different than whatever the hell we were doing before with just FIFO or something like that. Seems like it's not really, in fact, FIFO would have made the exact same decisions here, right? So the only difference comes when we bring this reference bit into play. So now if we access page two, before when we just had FIFO, we were like, oh yeah, it's in memory, just carry on. What's going to happen now if I access, if this program accesses page two? Yeah, it flips the reference bit. So if I use page two, that means the reference bit, which just indicates whether or not this page has been used, we'll just get, if it's zero, it just gets reset to one. If it was already one, means it was used sometime in the past, then it would still be one. So all we do is flip the reference bit from two or from zero to one. And now this kind of makes sense too, right? So I just use page two, and now if I had a page fault and I had to replace a page, well because I set the reference bit back to one, it means I wouldn't replace that. So I keep it around because I have used it. So that's where the idea comes from. So now for page three, I access page three, what happens? Yeah, page three's reference bit gets changed from zero to one because, well, I just used it. All right, so now what happens? I try to use page one, it is not currently in memory, so I need to replace something. So what should I replace? Or what's my first step of the clock algorithm? Yeah. Yeah, so I look at what the hand's currently pointing to, go ahead, yeah, and the iterator doesn't change with page access because, well, that would probably be slow. So if we want to do things as fast as possible whenever you're just accessing data, flipping a bit is about the most we can do. So in this case, we go from, we check the reference bit, it's currently one, goes to zero, advance the hand, so we can't replace it. Then here, we're pointing at page three, its reference bit is one, so we change the one to a zero, advance the hand, try and find our next victim. Now we're pointing at four, the reference bit is currently zero, so if the current reference bit is zero, well, we kick page four out, right? Kick page four out, replace it with page one, this reference bit is one, advance the hand. So now, after that access, our clock looks like this. So any questions about that? All right, so next we access page two. What's gonna happen with page two? Do I advance the iterator at all, like the question in chat? No, don't advance the iterator because hopefully this is just reading a byte or something like that or doing something quick. So all I want to do is change the reference bit on page two from zero back to a one. And then when I access page three, well, I would change the reference bit from zero to a one. Now, at this point, if I had another access, it would probably go around an entire cycle again and then evict page five, but at this point, we're assuming we're done. Yep, yeah, only after inserting or when you're checking to swap something in. So yeah, you only move the iterators part of the swap process. Yep, so if the reference bit was already one, you just keep it at one. Because remember, this isn't exactly least recently used. If it was least recently used, well, two is now the most recently used thing, so you definitely want to keep that. In this case, we only have a bit to keep track of it, so if it's one, it just stays as one. And we'd lose some precision because, well, it's not the most recently used thing, but we have to approximate things to actually implement them. Yep, so you could keep track of more things if you want, but the limiting factor for most of these algorithms is what you're keeping track of on every single access. So it's like reading or writing a single byte. So if you're doing more than a single bit, one, you probably need some hardware support for it, and two, then you have to do something smart. So there are different things you could do. This is just one of the options. But it's kind of like scheduling. There's just trade-offs you have to make. The more complex you make this, whoever it needs to make a decision, the slower it gets. You might slow down your accesses, but it might be worth it if you can make a really good decision. Or you're just Google and you turn off swapping completely. Yep. No, so if the page is already in memory, it won't add it again. So everything in memory is unique. You don't duplicate pages or anything like that. All right, yep. Yep. Yep. Yeah, so in this case, if I had another access, well, everything is a one, so I'd have to do like I did at the beginning, check, change the one to zero, check, change the one to zero, check, change the one to zero, check, change the one to zero. Then eventually I end up replacing the page. I've pointed that anyways. But that's just what'll happen. Yep. Yeah, if you want a shortcut to how to do it by hand, if all the reference bits are one, you know it's going to go around an entire circulator. Just change them all from one to zero and replace whatever it's pointing to. So that's a shortcut if you're doing it by hand, which I guess on an exam. Oh, yeah, you probably will. Yeah. For, if you just brought it into memory, the reference bit is one because you just used it, right? I just had to load it into memory because I needed it for something. Okay, any other questions? All right, so given the clock, if we write it all out the same accesses, this is what's actually happened. So for four, like up to this point, it looks like FIFO. And then when we access page two, we set reference bits and stuff. But if you just draw like we did in the last lecture, what's currently in memory and what currently gets swapped out, just nothing happens on two. It was essentially a hit, but we know it would update the reference bit. For this algorithm, for page three, nothing happens for one, while it replaced page four, and then we access page two and then page three. So how many page faults are there? Six. All right, if I wanted to evaluate how good this was in general, what should I compare it to? Least recently used, is there anything that's slightly more optimal? Oh, the optimal algorithm, okay. So let's do some practice. All right, do we want to do the same sequence but optimal just for some practice? Not really, but sure. We've got time in this class, for example. So if you want to end early, we can. All right, so optimal. Same accesses at the top. What's going to happen if I access page one? Yeah, have to bring it into memory. All right, let's skip the first four steps. So now we can do that again. Good up till now. Access page five. What do I throw out? I throw one, right? Do I throw out one? Four. I should throw out four. So if I look into the future, right optimal is looking into the future. So I shouldn't throw out two. I shouldn't throw out three. I shouldn't throw out one. So I'm left with four. So four gets the boot. Whoops. Oh, wait. Sorry, it's five. So four gets the boot for five and now my pages look like this. Now, from this point forward, do I get a page fault at all? No, see, that's why it's called the optimal. I did a really smart thing. So it's just going to be that carried on. How many page faults is that? Five. Yeah, so we got six and we could actually implement it and this had five page faults. So got pretty close. All right. So we got nothing but time for this. So here is the sequence we had at the beginning in the last lecture that we carried for every single example. Do we want to do this with clock? Sure, why not? In fact, well, we can either do this or go home. So if you don't want to go through this, I guess you're free to leave, although I guess you're always free to leave, technically. So to the side, we assume that there were four things in memory. Initially, it's going to look like this. So I'll draw the little clock, point it, try and draw a bit neater and then I'll put the iterator in the middle or the hand. So if I access page one, what'll happen? Yeah, one, one. So one, one. So I'll put the reference bit at the bottom. Let's see, can I change it? I'll change it to be blue. So I had a page fault. And am I done? Is this the complete state of the world at the end of accessing page one? Yeah? Okay, if this is the complete state of the world, what happens when I access page two? Oh yeah, I forgot to move the hand after. Although in this case, it won't really matter because it's going to go around the world again but I should move the hand. All right, so now I access page two. So can I just place it in the memory right now? Yeah, sure can. So I would get two, whoops. I would get two, my reference bit is what? One. And then I would move the hand and now I have brought in page two. All right, can I skip a few steps? All right, so we're going to go ahead, bring in three, bring in four and then it would look like this. Actually I'll leave it in blue, sure. So if I access page three, I have to bring it in. Access page four, I have to bring it in. After I access page four, the clock would look like this. So all good, all on the same. Okay, can't make the same joke twice, not the same page, the same hand clock, I don't know. All right, yeah, I'm out of jokes, all right. So now if I access page one, what happens? Yeah, flip the bit if it's zero to a one. Currently it is a one, so I don't have to do anything. So now I would just write out the same pages again because a page fault did not happen. Access page two, what's going to happen? Nothing, or the reference bit's going to change from one to one, however you want to look at it, doesn't really matter. All right, now I access page five, what happens? Yeah, we'll take the shortcut. So if we want to do this in pain stakingly excruciating detail, while I'm pointing at something, the reference bit is one. So I change it to zero, advance the hand, go ahead. Pointing at a one, change it to zero, advance the hand. Pointing at a one, change it to a zero, advance the hand. Pointing at a one, change it to a zero, advance the hand. Oh, well, here's where we could, I'm sure I'm just changing everything to zero. So now I'm pointing at a zero, what do I do? Yeah, kick one out, replace it with five. So I will draw that here. So one is out, five is in, and in here, I can erase all of this, so we brought page five in. What's its reference bit? Should be set one, and then the hand should point to the next thing. That's kind of crooked, but we can roll with it. All right, two, three, four. All right, I'll agree, all, everyone's good? Yeah, it's good that this is straightforward and boring, it means you know how to do it. So access page two, what happens? Yeah, I don't understand how some, how this can take two lectures, but whatever. All right, so now one, the reference bit went from zero to one, I don't have to do anything else, right, that was it. Won't be as quick as possible on my page references. So now I have five, same pages again. Access page three, what happens? Just flip the bit from a zero to a one, and now looking at this too, while I just use these pages and since I flip the bit from zero to one, means I won't replace them immediately. So now we have five, two, three, four. Now we access page five, what happens? Nothing at all, or one to one, five, two, three, four. Now we access page two, what happens? Nothing again, now we access page one. All right, we gotta get rid of something. So what do I do? Yeah, so I would change the zero to a one, or one to zero, sorry, advance the hand. Oh, it's currently pointing at something with a reference bit of one, means it was used at some time in the past, don't know when, but it was used somewhat recently. So I change the one to a zero, advance the hand. Four, your reference bit is zero, so you are donezo. So four, you are out, and one, you are in. So one is in, it's reference bit is currently one, and we would advance the hand. So now it looks like five, three, two. Yeah, five, two, three, sorry. All right, so turns out we made a poor decision. So now I access page four, I have to bring it back into memory. So what do I do? Yeah, same thing we've been doing this whole time. So reference bit goes from one to a zero, we advance the hand. Oh, well, we're currently pointing at something the reference bit is zero, so it's done. So two gets replaced by page four, we set its reference bit to one, we advance the hand. So now if we write it out to the side, we replaced two with four, and this is what it looks like. So how many page faults did we get there? Seven, which, pretty good. If we remember, what were the numbers from last day? It was like six or something was optimal. So we got seven, and we didn't cheat, we didn't look into the future or anything like that. I think LRU got the same thing, right? I think LRU got six too, but can't actually implement LRU without wasting too much time. All right, any other questions for that? So as a giant hit, if you didn't understand today, you should probably review it again because you might be writing it soon. Soon, like December 8th soon, like our final scheduling is awful soon. Alrighty, so I think I said this in the last lecture, I'll just say it again, so if you're Google or something like that, well, this whole page replacement thing is slow, happens behind your back, so if you just run out of physical memory and swapping starts happening, unless you have a super, super, super, super fast storage device, you're going to notice your performance slows down, and in which case, if you are Google or anyone with more money than cents, you can just go ahead and say, hey, I don't want to debug random performance issues that come up because of swapping, so I will just disable swap, and that way, instead of swapping, what's going to happen? Yeah, once you've run out of memory, well, then your own memory, Mmap would give you an old pointer, you'd probably seg fault if you didn't check for errors, how many of us have checked for errors from malloc? Probably no one, so you'd probably make it a bit harder on yourself because eventually you would just randomly seg fault when you run out of memory, instead of actually like failing with a nice message, but what will actually happen, yeah, and then, well, if you're Google, you just go out and buy more memory and then suddenly your process runs fast again. So on Linux, if you actually physically run out of memory, something runs called an OOM killer, stands for out of memory killer, and it just does the unsurprising thing, looks at a process, says which of you is consuming the most memory, then sends sig kill to it, kill-9, it's gone, and you suddenly just freed up a bunch of memory because you killed, I assume, Chrome at this point. All right, so, any questions about that? So, other things that could happen? So Google is not killing its own application, they're just running the testing infrastructure on something that has like 512 gigs of RAM or something, but there's no issue, this doesn't look like your computer. All right, so, you might also notice that now, while the page size we originally had like that four kilobytes, that was picked in like the 70s, I believe, and yeah, it might not be great for modern applications. So, some options you can do with modern MMU units. So remember, we had like 12 bits for offset and then nine bits for each index. Well, what it will do is essentially just chop off a level and use that for a bigger page size. So, instead of 12 and then the nine for the lowest level page table, it would add them together and then create a page out of that. So you would have a two to the 21 size page or two megabytes, and in that way your L1 table, what was your L1 table? Instead of pointing to an L2 table, it points to something that would be called a huge page instead. So a huge page is just, you're essentially, instead of having another level, you use that for actual memory instead. So, if you have a two megabyte page on, I think you can do this on x86 CPUs now as well, two megabyte pages they might be referred to as huge pages and sometimes that helps you a lot because well, if our TLB only has a certain number of entries in it and I suddenly increase the size of the entry, so each size of the entry maps to two to the nine more bytes, then for the same number of entries, I cover more of the translation in a bigger area, right? So, if I can only hold 10 entries, say if they're only four kilobytes, then I can only cover 40 kilobytes at a time. If they were all 20 megabytes and I had 10 entries and I could cover 20 megabytes of memory in the TLB and speed up things a lot quicker. Then, if you wanna go super crazy, the next, essentially if you chop off two levels of the page table and use that with the offset, then your pages suddenly get up to a gigabyte in size and those, I think they just called them gigapages, so not very creative naming. So, any questions about that fun stuff? Yep, yeah, there's a higher likelihood that the entries are already there and there's also a higher likelihood that you're covering more memory per entry, so it's less likely it'll get kicked out before it actually matters again, so it's more likely it'll be in the cache still. So, that's what a lot of programs are kind of starting to move towards these two megabyte huge pages. If you're running Windows, you can see if you have huge table support. If you have something called huge tables, that's what that means. They try and up your page size to two megabytes. All right, any other questions about that? And you can of course, well, this would go with kind of relates to the file systems, so if I had like a gigabyte page and while suddenly my program's only using a few megabytes, well then I have essentially a bunch of internal fragmentation, right? I can't use the entire giga page, but it has to give me a gigabyte anyway, so I just can't use it. So, two megabytes probably good, but once you get up to a gigabyte, then suddenly you can't run that many processes at a time because, well, you might not even have that many pages. All right, any other questions? All right, so, this one was fairly straightforward, hopefully, if you do not get full marks on this in the final, I don't know what to do. So, please take this. Anyone that is here, if you don't get it, I know what you look like and I will find you. Okay, that sounds more aggressive than it should have been, so I did not mean that, but this should be essentially free marks. So, data structures. We have a circular list of pages in memory. There is a reference bit for each one. It was like graying the slides, draw it however you want. It had a hand iterator pointing to the last element that was examined, algorithm, simple. Check the hand's reference bit, whatever it's currently point to, if it's zero, replace, put that page in, set the reference bit to one, advance the hand. If the reference bit is already one, set to zero, advance the hand. And on any page access that happens, set the reference bit to one. Any questions? All right, we can end early. Sweet 15 minutes this time, new record. All right, so remember, phone for you. We're on.