 I think three talks that are really closely related to mine. So there's like context in Curen's talk in the talk on beta values and in the talk that just happened right now. So I needed one more terminal. So that's not the size, you'll see, there. All right, there we go. There's, it's both silly and actually important that my slides are in the terminal because I'm gonna do some of the bad things that Andreas was leading to and kind of break my computer. So ordinarily when we're writing programs we kind of like to keep the memory usage a bit low. So here's like an example of the thing that I hate which is Firefox using two gigabytes on my four gigabyte laptop. This is like half of the course I have like 200 tabs open at any given time and they all have images and document object models and Java script and whatever. So yeah, for this talk we're gonna try and write a program that stores lots of data but without using a lot of memory. So you might just think, sure, we'll just store it on disk. We can store all the data we want in a file, that's fine. But we're actually gonna store lots of data in memory but without using a lot of memory. Not making too much sense, bear with me. We're gonna store it in kernel memory. So this is where some of the stuff Kieran was talking about comes in. Instead of keeping the data in our own user's memory we're gonna store it in the kernel's memory. So what is kernel memory and what is the difference between kernel and user memory? So on our machines there are two kinds of code that run. There's user code which is the normal code we write most and run most of the time. So it's the code that, you know, prints, fizz, buzz, very important. We look at cat pictures, very important. And you know, all other manner of kinds of stuff that we do and these programs have data, they have local variables, data structures and so on and that is all stored in user memory. And the kernel code is doing all kinds of important but sort of mundane things like figuring out which threads are gonna run on which cores, keeping track of which files are open in which processes as Andreas just demonstrated. And so it's got a lot of bookkeeping and it keeps all of this stuff in lots of like run queues and file descriptor tables and who knows what. So that's what I mean by kernel memory. Stuff that the kernel has access to but that user programs don't. And it's really important that user code can't access kernel memory because if you could, then you could just go and say, oh, here's my file descriptor table. I'm just gonna pretend I can read the password file. Okay, so user code cannot access kernel memory and that's actually enforced by a piece of hardware, the memory management unit. I think it's really like kind of weird and cool and mind-blowing that there's like a piece of hardware that's like a little permission grantor access denier. It's actually silicon in your computer. Okay, so now like recap, we're gonna store lots of data in memory without using memory. We're gonna store it in kernel memory but we can't access kernel memory. Still not making very much sense here but just bear with me. This is the interface I kind of wanna go for. We wanna have a kernel buffer that we can read and write to anywhere. So read it in offset into some buffer, some number of bytes and similar for write and we're gonna store the data in pipes. Andreas was storing file descriptors in sockets. I'm going to store data in pipes. So pipes are these mundane things. This is very small but this is actually from my command history. I'm running awk which is piping to sort which is piping to unique which is piping back to awk to sort. I have no idea what I was doing. This is actually in my command history but each of these vertical bars is a pipe that is a place where one process can send data to another process. It writes data into the pipe and then it sits there for some amount of time until the next process down the line reads it out. So I have a pipe. So pipes are very much like pipes. They have ends. You can put things in one end and you can take things out the other end. This part's gonna go poorly. But so let's see. So there's the read end and the write end. So we can write into the pipe and then there's the buffer which is the data will, bits and bytes don't have friction. Anyway, they'll sit here for a little bit until then in the buffer until we can read them out, right? So that's like pretty cool. This buffer is in kernel memory. The user has access to the ends through file descriptors but the actual data that's in there is stored in the kernel. And in fact, Linus in a mailing list thing posting actually just described a pipe once as a kernel buffer the user has control over. You don't have direct access to it but you have control. You choose what's in there and when to take it out. So we've covered how we can only read and write at the ends but let's just say I actually really wanted one of the things in the middle and I don't have access to the middle, right? I can't get it out. It's not gonna work. You have to get access to it. You're gonna have to take stuff out from the end and put it back in at the other end. This, only one, only one so far. There's not much more physical props so just one and I'll give it back to you, Daniel. So how do we get a random access buffer out of this? Given we can only write into one end and read out the other end. Well, I kind of alluded to it just there. We can read things out until we get to the part we want writing back all along and then when we have the part we want we give it to whoever was asking for it. An alternate way is we could just read everything out of the pipe, which would be spill everything onto the table, find the parts we want and then write it all back into the pipe at the end. So now we've got a building block which is we can store data in a pipe but pipes are only so big. The buffer is some number of kilobytes usually but we could create a bigger buffer by having many pipes. I've only got one. But having many pipes and then doing, you know, some offset and div mod kind of math to make it all work out and well how big can we go? So Linux actually lets us set a pipe buffer size and which seems great and the max is a megabyte. You can set any given pipe to have a megabyte buffer. This is cool, so far so good. So let's just create many big pipes, right? Then we have many, many megabytes that we're storing in the kernel. Problem is that there's a per user limit on the total number of big pipes, actually the total size of the big pipes which is 64 megs and I don't like this because it's a per user limit not a per process limit which means that you have to run as root or something to kind of go past it which is not what I want to be doing. So I'm gonna ignore this idea. Instead, find a way to store more without needing to bypass a per user limit. So there's a code path in this, the code that checks whether you're creating too many too big pipes that promises you will always be able to create a pipe. Like the pipe system call will never fail. It just might give you a really, really small pipe. It's like, it might just give you a four kilobyte pipe but you will always be able to get a four kilobyte pipe no matter what. Okay, so you get the idea where I'm going, right? So we've got 64K, I don't know what Linux Andres is running. My machine has 64K file descriptors in the processes but anyway 64K, 4K buffers. But actually no, because pipe has two file descriptors one at each end. So actually only 32K pipes, 128 megs. If you're like, I'm not good at binary math, I use Google, about 128 megs for a single process. That's pretty good, but let's go a bit further. So how about we have right once pipes? We put a bunch of these glass beads into the pipe and then we just discard the right end. It's gonna sit here in the buffer. We've got the read end and we can get it out whenever we want. And then when we want to read out, we take this stuff out, close that, like discard the whole pipe, get a new pipe. Same pipe, but get a new pipe. And put the stuff back in and then discard that end again. So you can do this like cycle loop thing where you're able to like have only one file descriptor but with four kilobytes each. So close the right end after whatever. So you've got 64K pipes now, just 256 megs per process, which is pretty good. But can we go further? Remember earlier I said I'm not interested in per user limits? These limits that I'm talking about are per process. So we can have multiple processes, right? Okay, so now each process can have 256 megs of kernel memory that it locks up. So, okay, I've got nothing running on this machine because I don't want to lose anything. I have a top window, forgive, maybe I can make this a bit bigger. Is that readable? Nope, okay, we have to stop here. All right, my program that I'm gonna be running is called something beginning with K. And over here, I have this little program called Kbuff which stashes data in whatever, kernel buffers. And it's got some nice little commands. So we can run Kbuff, closing the right ends, keeping, so 65, 532 pipes is the number we can get and we can try, actually, just, sorry, resetting back to how it was before. This is the default number of, the maximum number of files that can be open on my system at any given time. So, we've got this thing and we can run, we can try and open, create 20 processes with 64K pipes each with four kilobytes which is way more memory than my machine has. And if I run this, I get like a bunch of errors. It's enfile is the thing and if we run ernoenfile, it's like too many files open in system, okay. But let's just run that again and look at what's going on over here. And you can see the memory usage kind of go up a bit. But it didn't go up enough, I only went up by about two gigs. I've got two gigs of kernel buffers here. Ah, I want a bit more. So, this is the only part where I cheat and I can increase, as the sysadmin, the total number of files to like a million. And then, I'm just gonna make sure I didn't miss any slides because we're not gonna see any more slides. Um, okay, so there's the total file descriptor limit which we just saw. There's two gigabytes is about as much as we can get with 384K pipes. But we can raise the file limit to a million. We're gonna hit the memory limit. And Kieran talked about the out of memory killer. I didn't really know what it did viscerally. Um, until I did this. Um, um, killer, this is the last slide, that's great. So I'm gonna try again to create 20 processes with 60, there's more memory than I've got. And the system's gonna kind of stop functioning, give it a few more seconds and the um killer is probably just killing who knows what right now. I don't, I have no idea. Oh, there we go. Oh, so when I did this before, it did not kill my login session. Here it's killed my login session. So I, last time I had a terminal left to show something, this time I did not. But there you go. These are some really, really wacky, weird things you can do to abuse all of this stuff in the kernel. Thank you.