Good morning. It's March. Welcome to March. The semester is going by fast. So today, we're going to keep talking about memory management. And in particular, today, we're going to figure out how to implement this nice idea that we came up with on Wednesday. So we looked at problems with multiplexing physical memory on the machine. And we came up with this great idea that we could provide every process with this really uniform, nice view of memory. But we don't know how to do that. So today, we're going to talk about one of the main methods that allows us to do this. This is kind of a neat trick, I think, and really a powerful idea. So it's March. And if you guys remember, vaguely, that was kind of our target for having you guys onto assignment two. So just to avoid scaring myself, I won't ask for a show of hands. How many people are finished with assignment one? But if you're not wrapping up assignment one, then you are behind. So if you're behind, then I would think about trying to catch up. Because again, we're moving on. And there's plenty to do on the last two assignments are hard. The last two assignments are the meat of the class. The first two assignments are maybe 20% of the overall programming work. The last two are the heavy hitters. So you guys need to wrap up assignment one, get the primitives done, do your best with the problems, and get on to assignment two. An approach to starting assignment two would be to start looking at the design component before you leave for spring break. So I'm sure you guys have exams and other things. But the design document is something that it's better to kind of sit down with your partner to read the assignment, think about it a bit, sleep on it, come back the next day, talk a little bit more. Writing a good design is not like programming where you just need to sit down and crank it out. You have to read it, think about it, figure out what you don't understand, come back, talk to the core staff, get a clear idea of what you need to do so you can write up a clear design. The more time you spend designing up to a certain point, the less time you'll spend debugging. I would say five, 10 minutes of good design effort can translate to several hours of reduced debugging. So it's a good trade off. It might not feel like one. You might want to be anxious to get onto the coding, but spend some time on the design and give it the effort it deserves. All right, so on Wednesday we talked about address spaces. Anybody have any questions about address spaces before we do some review? So address spaces were these abstractions that we were going to set up to simplify memory management. They allow us to set up processes the same way every time. They will give processes these nice expansive views of memory. And there were all these nice things about them except for the fact that we don't know how to implement them. We don't know how to actually make them work. But any questions about the abstraction before we do some review? OK. So remember, we had these requirements for how we were going to multiplex memory. There were four things that the system needed to be able to do. We talked about granting memory. What do we mean, Alyssa? She's got her laptop open. Cheater. Yes, imagine. Almost exactly that, right? So we need to be able to give processes memory. We're in charge of handing out memory. That's our responsibility. We're the kernel. We have the special powers. We have to use them. But one of our responsibilities is to actually allocate memory. And there's a couple of different times that processes will be able to request memory. We'll talk a little bit more about that today. What do we mean by enforcement? Yeah, so essentially, enforce memory allocations. And this can mean a couple of things. It can mean making sure processes don't seize more memory on the machine that's available to them. We don't want processes gone wild and just kind of like taking things that aren't theirs. We also don't want processes to use memory that belongs to another process. So we don't want processes to be able to grab memory that's regardless of whether it's belong to someone else or it's unused. There are ways that processes get memory. And the kernel, asking the kernel is, well, I shouldn't say they're ways. Asking the kernel is all of those ways. What about reclaiming memory? This was something that was a little different, especially when we talked about CPU, Robert. Yeah, so we wanted to be able to, in certain cases, without necessarily telling a process, but be able to sort of monitor a processes memory, the usage of the memory we've given it and potentially take memory back. And the problem with this is the processes don't necessarily have a good idea of when they're using memory or whether or not the memory they've allocated is in use. When I give a process a CPU, it's executing instructions. Maybe it's sitting there running no ops for some reason. But in general, it's an active use of the processor. But when I hand out memory, what I want to know is, is that memory an active use? Or did I give it to the process? The process did some stuff on it 10 minutes ago, but hasn't touched it since. And we'll never touch it again. So I want to be able to monitor allocations and potentially repurpose unused or lightly used memory when I need to. And then what about revoking memory allocations? Manish. I said, remember, I'm going to play four corners today. Yeah, I need to be able to take memory away from processes. So even if I've handed out memory to a process, you need to be able to say at a certain point, the process is no longer allowed to use this memory. We talked about fragmentation as being a challenge when I manage spatial allocations. Memory is something that I'm going to multiplex spatially. I'm going to divide it up into pieces and hand it out to different processes. So we talked about two types of, so first of all, what is fragmentation? What's the concept of fragmentation? Yeah, Tim? Great. Right, so very specifically, if I can't satisfy an allocation for memory, despite the fact that I have enough memory to satisfy the allocation, so why can't I satisfy this allocation, Sarah? Yeah, because the allocation is contiguous and I've got memory scattered over in little pieces and I don't have a good way to merge it all together. So I have enough memory, but that memory is somehow split up into pieces that are too small to satisfy the allocation request that I'm given. And we talked about two types of fragmentation, internal fragmentation and external fragmentation. What is your name? Yeah, internal fragmentation. Yeah, so can you be more specific? In internal fragmentation, where is the unused memory that I can't reclaim? Yeah, we don't even have to say, I mean, we could talk about fragmentation in a more platonic way. It doesn't even have to be to a process, it could just be inside an allocation. If handed out an allocation, there's a portion of that allocation that's unused, but I can't reclaim it. And then what would external fragmentation be then? AJ, stop saying processes. Allocations, right? Yeah, I know, we talk about process memory allocation. That's fair, but processes are just sitting there in memory. So unused memory is between existing allocations. So I can see there's these little slivers of memory that are available on the system, but I can't get up. So great, we had this really nice idea. Let's create an abstraction for memory management, and let's have that abstraction have as many nice properties as possible. So what do address spaces do? They give each process what kind of view of memory? We kill, OK? So it is individual, but it's also what? How? Welcome to class. Josh, you want to help them? Identical. Every process sees memory on the system in the same way. They all see the same, the memory looks the same to every process. What else do address spaces allow us to do? What else does it make this look like? Sean, give me one. There are four things. Well, no, no, so what properties of the address space? The address space abstraction makes memory look OK. So it is the same size. That's true. Sammy had something. Yeah, looks contiguous. That's important. So I can allocate things in this address space that look contiguous. It may not be contiguous if we'll talk about that today. That's one of the tricks. What else? Yeah, Paul. Yeah, it's private, or mainly private. Again, similar to our IPC mechanisms, the default is private, and if you want to share their mechanisms to do that, but the idea here is that processes memory is private. What else? Dan? Yeah, it's a lot. I make it look like every process is this huge gob of memory. Like I just gave you four gigabytes of address space. That's kind of nice. Last one. Jen, you took yours. I know it's hard to be the last. Maybe I'll just. Plentiful, contiguous, uniform, and private. I'm not even sure what I meant by uniform. It's up there on the slide, so let's sum up something. All right, who remembers this? Why don't we load stuff into the very bottom of the address space? We talked about this at the very end of class, humbly. Yeah, this allows us to catch no pointer problems. Somebody asked after class, well, why do I leave so much memory? I mean, I started the code at like 0x10,000. Well, first of all, remember that, or maybe you don't know this yet, but wasting address space doesn't mean wasting memory. And that's not a huge amount of your address space. That's a pretty small piece of it. And also, remember that if you have a large object and it's null, the offset into that object, if you try to index it, can be quite big. So leaving a lot of empty space means I can catch null pointer exceptions, even if they're on really big arrays. OK, any other questions about address spaces before we talk about how we're going to implement them? All right, so now this is our task. So we've come up with this nice abstraction. This abstraction has features that we like. This abstraction allows us to simplify a lot about how processes lay out memory, about how processes access memory. So our goal now is to figure out how to implement this abstraction. And to some degree, I mean, I think it's clear, even from a sort of inductive approach, that we have to break this direct physical address connection. We can't allow processes to have a direct access to physical memory. And we knew this because we talked about before that, too, it's no longer meaningful to talk about addresses that are inside an address space. It's no longer meaningful to talk about them without talking about the process, because two processes can have the same address that point to different places. The way we're going to do this is we're going to introduce a level of indirection. We're going to introduce a level of indirection, which requires a translation. And a lot of what we're going to talk about the next week or two is this level of indirection and translation and making naturalization fast and making it efficient and all sorts of fun tricks that we can play because we suddenly give ourselves this power. But where have we seen this before already? We've seen this once before where we introduced a level of indirection, an extra level of indirection that allowed us to add some desirable features to something. Yeah. Right hand. Yeah, remember the file handles? So we could have had file table entries point directly to files. But by introducing this extra mapping where we map file table entries to file handles to files, that allowed us to do these nice things after fork. So here's another example where I took a mapping. I added an extra layer of indirection and that gave me some new powers that I wanted. Introducing a level of indirection also gives the kernel control. And the kernel wants control. And we need control. Part of our job is to control how processes access resources. That's what we do in order to multiplex them. So if instead of giving a process, and if I gave a process a direct access to physical memory, then in many ways my hands are tied. It can use. And if the hardware is allowed to translate that, it just used that address, then I'm kind of stuck. It's very difficult to do some of the things that we wanted to do, to be able to reclaim memory, to be able to revoke memory, to be able to do some other nice things. Forcing processes to translate this reference gives us actually a fair bit of extra power. And we use this power in a variety of interesting ways. So here what I've done, what I'm going to do is I'm going to, instead of giving processes access to memory, I'm going to give them a reference to memory. And I'm going to translate that reference for. Turns out that reference looks a lot like an address, and it is an address. And we'll talk about how all this translation happens, and that's kind of the whole goal of memory management. But here are some of the things I can do. So for example, if I give a process of reference to memory, and I'm in control of that references translation, then if I want to revoke access to that memory, I can just stop translating the reference. So the process says, hey, I want to use this memory. And I'm like, I don't know anything about that memory. I told you to stop using it. So that's nice. References allow things to be shared in a kind of nice way. So I can give two different processes, potentially different references that actually point to the same memory. Again, we don't usually talk about this when we start talking about memory management, because we're focused on it being private. But this is a way that I can control sharing of memory if I want to. If I want to allow processes to share memory, this is one way to do it. If I have a reference to an object, I can move the object without altering the process code. I give you a reference to some memory, and I'm in charge of translating it, and I decide to move it somewhere else. I don't have to tell you. I just have to remember where I put it. So you get to keep using the same reference, but I get to play games with where the memory is, which is really, really cool. And also, I can alter reference objects. I don't know what I had in mind with that. But that is something you could do. It's not something maybe that we use in memory management, or if it is, I forgot what it is. So here's an interesting question. What is the interface to memory? So let's ask a different question. What's the interface to files that you guys are familiar with? We talked about this, and you're going to be implementing this fairly soon. What's the implements? Just parts of it, interface to files, Wembley. The interface, if you know it is an object that helps implement this interface, but what's the interface? Yeah, thanks. What's that? Yeah, but again, I'm going to keep asking this question, because I don't want to. No, interface. Yeah, open, close, read, write. That's an interface. An interface is a set of functions that you use to access. I have files, processes. You can open files. They can close files. They can read and write to files. You can alter the file handles through things like doob. They can move the file pointer, whatever. That's the file interface. That's how you use a file. What's the memory interface? Yeah. Yeah, that's how I get memory. But how do I actually use memory? Sirach. Well, that's how you do it as a programmer. But what is the actual interface of the machine level? Yeah, load and store. You guys don't think about this, because the compiler generates this code for you. But at the end of the day, load and store is the interface to memory. It's an interface provided by the processor itself. The kernel does not provide this interface. You don't make a kernel system call to write a byte of memory. That would be the slowest operating system ever. So this is an interface that's provided by hardware. It's partly because we want it to be really fast. Memory's supposed to be fast. Dists are slow, memory's fast. So forcing the kernel to get in the way is going to slow things down a lot. So hardware provides this interface, load and store. And if you look at the instruction set for the MIPS system that you guys are working with right now, and there's usually a variety of types of instructions of this sort. But they all really break down into load and store. Load some, a byte or several bytes sometimes into a register or a set of registers and then store from a set of registers into memory. That's typically the interface. And again, there's different little variants on it. But this is it. So the address-based abstraction, again, we talked about now we need to break this connection between the memory address and physical memory. So what we're going to do is instead of giving processes, user processes, direct access to physical memory through physical addresses, we're going to start forcing them to use these things called virtual addresses. Virtual addresses in many ways look exactly like addresses. They don't require, in fact, that we really change this interface at all. We're giving addresses. These addresses are references. They're references in the way that we talked about just a few slides ago. They're translated. And the kernel is in control of that translation. And we'll talk about what we do to make that translation efficient. But the kernel is in control of that translation. So when we talk about addresses in this class, we always have to be careful to make sure we know what we're talking about. So a physical address, a physical memory address, points to a byte of memory on the machine. A virtual address is really almost an abstraction in itself. And the idea is virtual addresses can be used in loads and stores.  So what does memory act like? What are my expectations about memory? This is not a trick question. It should be fairly simple to answer. Alyssa. Yeah, when I write something there, then I read it back later. It's the same thing. That's what I expect from memory. I store something, and then I load it later. So what's the other, just to distinguish memory from disk? What do I not expect memory to do? Frank, even more basic than that, Tim. Yeah, it's not persistent. We start talking about file systems after spring break. We'll talk about a lot of stuff that we do at the file system level to make stuff persistent. Memory doesn't do that. Memory, machine goes off, there goes your data. You shouldn't rely on things being in memory. But if the machine hasn't crashed, then we expect things in memory to be there when we get there. And the nice thing about introducing this new abstraction is that virtual addresses allow us to add a whole amount of semantics onto memory addresses that physical addresses don't allow. So virtual addresses allow us to move things around. Virtual addresses, actually, at times we can make virtual addresses, and the memory interface to virtual addresses, actually point to things that are permanent. So it's possible that we can make virtual addresses point to something that acts like memory, but is also persistent. Which is kind of cool. So I have a memory-like interface to something that's actually persistent. Kind of useful. And also it allows us to do a lot of protection, which is one of the things that we're really interested in. So when we start talking about virtual addresses, the data that's referenced by a virtual address. When I do a load or store to a virtual address, the data that's referenced can be in a variety of different places. And this memory interface is actually used in a variety of different ways. The first two are the most common. So the first place it could be is in memory. That's pretty common. This is kind of where we expect things to be. In this case, virtual addresses are mapped to physical addresses. So the kernel has to take the virtual address that a process is using and translate it into a physical address. Physical address points to a byte of memory that is loaded in the motherboard on your machine. The virtual address is in abstraction. The nice thing, again, about this is that we can move things around. So something that the process thought was in memory might not be in memory. And this is one of the things we do to try to make things perform well on modern systems. Stuff can also be on disk. In this case, a virtual address actually maps to some byte on disk. So the byte I'm trying to access through a virtual address is actually stored on disk somewhere. But even if I've mapped a virtual address to disk, which you can do through calls that allow you to take a portion of a file and map it into your address space so it looks like memory, the kernel might still be caching that in memory. So even something that you think should go to a file might be cached in memory on its way there. And if we wanted to, we can make virtual addresses do all sorts of other fun things. So for example, virtual addresses can point to memory somewhere else. I can extend the address space abstraction and allow loads and stores to memory address to actually land on some other machine. That could be kind of cool, maybe. The performance isn't terrible. The other very common use for virtual addresses is to allow the operating system to control devices. So we haven't talked about this and we're not going to talk about it too much. But one of the kernel's jobs is to interface with devices, to control devices. And frequently the way you control devices is you send them commands by essentially sending a byte or four bytes of data to them that gets routed to a particular port on the device that tells the device to do something. So if I want to read a particular block from the disk, I write that block into some port on the disk and then I tell the disk to do a read. Frequently the way this is done is by actually taking those ports on the device and making them look like memory addresses. So the way the kernel does is it writes to a particular memory address, a special memory address that it knows maps to the disk and then the disk receives that command. So when processes are using virtual address that point to memory, what do they expect about the permanence of that memory? We just talked about this a second ago. Andrew, if I've set up a virtual address to point to memory, what do I expect about the permanence? About the permanence? Yeah, what do I expect about the data that's written there? Well, I believe. Yeah, so memory stores values transiently, right? So if I write to memory, I expect that the value, if the machine reboots that those values are gone, right? What about if I write to, what about if I've asked the kernel to give me memory address, virtual address is the point to disk? What do I expect about those values that are written there? Yeah, permanently, right? So this is just something to keep in mind when we talk about virtual addresses. So the permanent semantics can also be different, right? So this is trying to just give you some introduction to sort of the richness of virtual addresses, right? Virtual addresses take this memory interface and they change almost everything about it, right? They change the permanence guarantees, they change the locations, right? So here's an interesting question, right? So let's say I have a port on a device, right? So let's say I have the register on a device that I have to write to in order to initiate a read on the device, right? And this can be true for the CPU too and other things, right? What about a virtual address that points to that device port? What do I expect about, you know, if... So let's say I have a device and I write a value into a port on the device and then I do a read. What do I expect to get back? What's that? That's it. Yeah, so this is entirely device dependent, right? Remember, devices aren't memory. So if I tell the... So some devices, it might say, when you write, you can write to this register, but it'll always return zero, right? So devices are changing that memory address themselves, right? And when we look in a few minutes at the virtual memory layout for your kernel, we'll see that that has some implications for caching in particular, right? So the final thing that we get with virtual addresses is this idea of permissions and protection. And there's all sorts of different things I can do here, right? Frequently what I do is I take some virtual addresses, right? That the kernel and the CPU know how to identify. And I say these addresses can only be used by the kernel when I'm running in privileged mode. So I can take a portion of virtual addresses and I can say that they can only be used or translated by the CPU when it's running in privileged mode, right? So this is kind of nice. This allows the kernel to essentially protect access to an arbitrary area of memory from all user processes, right? So I know that no user process can translate this part of memory, right? What's an example of something in memory that the kernel might want to protect from user processes? This is not supposed to be a hard question. Yeah, anything, right? I mean, almost anything, right? The parts of the kernel that the kernel is interested in users messing with are almost the null set, right? So, right, kernel code, no interrupt handlers, whatever, right? I mean, in general, there's large pieces of memory that the kernel wants to make sure that no user process will ever touch, right? Oops, sorry. And then I can also take virtual addresses and essentially assign them almost, you know, arbitrary permissions, right? I can take a bunch of virtual addresses and assign them to one process and say no other process can translate those addresses, right? No other process can use them. I can also assign, you know, read, write, and potentially execute permissions to virtual addresses similar to the way I did for files, right? So read and write, I think, make a little bit of sense, but what would it mean to mark a part of virtual memory address as executable or not executable? Let's say it's not executable. How would that mean? What would potentially cause a fault or a problem? Thor, he's giving me this miserable look. I wish everybody could see it, Tom. If I mark an address as not executable, then what will produce an exception? When would I enforce this property? Yeah, when the CPU tries to read and decode an instruction from that address, right? Remember, that's how CPUs work. They read an instruction from memory, decode it, execute it, and repeat, right? So when the CPU tries to read an instruction, right? So when my PC gets to that value and the CPU tries to read and execute it, the CPU is gonna say this virtual address is not marked as executable and will produce some kind of exception, right? So virtual addresses, a big chunk of virtual addresses, right, so yeah, and then we have to start talking about where do virtual addresses come from, right? So a big chunk of virtual addresses are usually created when the process starts running, right? And they're created by our old friend, exec, and they're created from this blueprint that we have in the ELF file, right? Remember, the ELF file contains this complete description of exactly how the process wants its address based to look, right?  And the virtual addresses that exec creates mainly point to memory, right? Mainly, and there's a couple of different things that exec is gonna do, right? So exec will set up mappings for the code that's used by the process, right? So remember, the code that the process runs has to be in its address space somewhere, right? So, and this gets done typically by exec at boot, right? So, sorry, not at boot when the program starts running. So all that code is loaded in the file, right? If you decode like bin bash, you'll find all the instructions needed to run bash, right? And the ELF format will tell the operating system where bash wants that put, right? So bash will tell the system, I want my code here, right? At some point in the address space, you know? There is usually some data that also gets loaded. This is a little bit of review, because we looked at this when we looked at, looked at process system calls, right? Typically the data, the static data, right? So if you initialize a static variable in your C code, or you create a static global, or even just a global variable, right? The space for those gets allocated when the program begins running, right? They're not dynamically allocated, they're statically allocated. So they get allocated once, right? The heap is set up, you know? So ELF will kind of tell the system where the heap will start, right? But the heap is used for these dynamic allocations, right? That are done by malloc. And these are runtime allocations that are used for dynamic memory management, right? And then the other thing that exec will typically do is, or sometimes this is just a system convention, is set up stack space for the first thread, right? So these are kind of our four main areas of memory within each address space. I have code, right? Stuff I'm going to execute. A space set aside for statically initialized data, a heap that's used by malloc and stacks that are used by each running thread, right? For local variables. So we've looked at this before, but I mean, it's worth looking at again, right? So here's an example of memory mappings for a sample process, right? So this is bash. And this is a simple Linux utility that you can use on your virtual machines or in other places. So what is, so I think we've done this before, right? But as we'll do it again, what's this area right here? Anybody? The top one. Yeah, Spencer. Right, so this is bash code, right? Bash has 876K worth of code, right? That code is going to be, is loaded here, right? This is the virtual address where this code is loaded. What are, how is this code marked? What can bash do to this area? So yeah, it can read and execute. Usually in order to execute, you have to read, right? The processor has to read the instruction and then execute it, right? So typically things are about read and execute, right? What is this, it's not marked write, right?  Yeah, you guys weren't born in the right decade, I guess. So heroic programmers in days past, right, would write programs that would actually modify their own code as they ran, right? So a program that would actually rewrite its own code as it ran, those programs would have a problem with this because they wouldn't be able to write to their code area, right? Typically we don't encourage that programming style anymore, right? But, you know, if you want to enter kind of an obfuscated code competition or something, then that's a good way to do it, right? All right, so now I have this second segment here, 4K, what's in here, look? Yeah, there's some data in there that also came from the bin bash executable. What is it, how is it marked? Yeah, so what would be in here? So it is data that's used by bash, it's not code, but if it's marked read only then, by definition, stuff in here are what? Yeah, it's not marked executable, right? This one's only marked read. Yeah, Sean? Static. Well, they're static, they're constants, right? Can't change them, right? So any constants that are used by bash are loaded here, right? Then what's this area right here, 20K, loaded right above the constants, what's in there? Yeah, Tom? Is that the data? Yeah, so this is read write data, right? This isn't any sort of static data segments that bash might have voted. And this was also initialized from the executable, right? So you have, if you've ever declared an array in C and initialized it statically, then this is where this would end up. And then there are actually two more pieces here that are also read write data, but these are marked as a non, right? And the difference between this segment and these segments is that these are initialized to zero. So if you ever have initialized a big array in C and you haven't put an initializer in for it, then the L file will say allocate space for this and fill it with zeros, right? So it doesn't come from the file, right? It just, because the system knows where to get zeros. And then, you know, here we have our dynamically loaded libraries, right? And then we have a stack for the bash byte, right? Any questions about this memory math in kind of its own? Why would a dynamically loaded library have right permissions or a segment with right permissions? Good question. Anybody want to take a guess? What do dynamically loaded libraries? I mean, what are dynamically loaded libraries? And what do they probably have? Yeah, and like, you're writing the C library, right? What might you have declared inside the C library? It's just more code, right? Just like the code that you wrote in bash, right? So what did bash have in this area right here, this read write area? This is remembering the answer to the question from two minutes ago. Sean? Yeah, constant static variables, just, you know, and any sort of statically initialized global variables that the C library needs. That's what's there, right? That's why it has its own data center, right? Because code needs data, right? You write code, you have a few global variables sprinkled around. Everyone knows it's on a good idea, but you do it anyway, right? Sometimes it is a good idea, right? Sometimes it's what you have to do, right? All right, so, so exec, so remember, so exec creates, you know, exec takes this blueprint and creates all of, and lays out the contents of the executable file within the address space, according to the blueprint in the L file, right? Fork just does this copy, right? So Fork just takes, you know, the parent process and copies the address space, right? And here what I've shown is the semantics of Fork where it's only gonna copy one thread, right? So we talked about Fork and it's kind of gnarly interaction with threads, but here's the case where the semantics of Fork are that it only copies one thread, right? So I discard stacks for threads that aren't copied into the new address space, right? But all the code from the parent process and including the heap and anything else, right? Any memory the parent has allocated just gets copied, right? So I take the address space and I just make a copy of it entire, right? So what ends up happening after Fork, right, is that the child has these same virtual addresses. This is a good example of how virtual to physical translation works, right? But they have to point to different physical memory locations, right? So for example, here's a little piece of code, right? So before I do the Fork, I initialize i to two. Who is this? The parent process, right? Return code is not equal to zero so the return code is a pit at the child, right? So this is my parent. It's going to print the address of this variable and let's say it prints like, you know, OX 20 million 10, right? Whatever, right? This is a, and actually in this case, this variable would be probably be on my stack and so the address would be different. But let's pretend this variable was globally declared, right? Now I set it to four, right? And now I better, after I set this to four, it better print to four, right? Because remember, I have all the same variables as the child, but me and the child should not be sharing memory, right? Fork makes a copy of the address space, but each address space is private, right? So what happens down here, right? So this is who is down here now? Child and what should the first print have to print? What the? What's that? Same virtual address, right? These are virtual addresses, right? They should, right? And what should, but what should this print? Great, three, right? I better have my own private memory, right? So here's a case where, what happened here? This guy ran, he had a virtual address of OX, you know, 20,000 10, he did a store, right? This C code translates to a store instruction, to that virtual address, and then this printed four, right? Then he did a read from it and printed the value, right? This guy did a store to the same virtual address, did a read from it and got a different value, right? So proof positive that if your VM works, these two things now point to different physical memory, right? So one thing you might know about fork, right, is that, we talked about this a little bit before, you're doing all of this copy, right? I'm creating this huge copy of the address space. If the parent has a lot of memory allocated, I potentially have to find a lot of memory to put the child's copy of the address space into. And this is potentially fairly expensive, right? And then frequently, again, the next thing I do after I call fork is what? I call exec. And then what does exec do? exec says, blow away the whole address space, right? So fork is like, I just copied all that stuff, man. I come in and do, so anyway, but we'll talk about a way to solve this next week or maybe after spring break, right? The other place that virtual addresses come from is from this system called called S break, right? And this is something guys will implement for assignment three. S break is quite a simple system call. All it does is it asks the kernel to move what's known as the break point, right? Or essentially the point at which the process heap ends, right? So here's my process. It's been running for a little while. K malloc, sorry, malloc has allocated some variables on the heap. Now malloc runs out of space. So malloc, the malloc library that's running in the user program needs to ask the kernel for more memory, right? It calls S break. It says, could you please move my break point? What does the heap look like after this? What happens to the heap? Looking for someone who hasn't answered a question recently. Yeah. Well, that would be, so what would happen if I suddenly decreased the stack? That would probably be bad, right? There would be some threads out there that wouldn't be too happy, right? But again, I have all this space, right? So what does the heap look like after this? Damn, the heap gets bigger. And the lines around the heap get thicker, right? And the heap text gets taller, right? Yeah, and the heap gets bigger, right? So this is S break, it's not hard, right? This will be by far the easiest part of assignment three. The rest of it's very hard, but this is very easy, right? And the final way, and I don't want to talk about this too much that we create virtual addresses, or a final way is through a system called called Mmap, right? And Mmap is a system called that creates virtual addresses that actually map to a file, right? So that's all I said about it, and we could talk about this later, but Mmap is used in certain cases where I actually want stuff to be permanent, I want to read and write data to a file, but I want to treat it like memory, right? And that can be nice, right? Because read and write sometimes is in the right interface, right? So for example, if I'm a database, I have this complex data structure, I want that data structure to be permanent, I want it to live in a file, I call Mmap, I get a big chunk of memory, that memory points a particular point in a file, now I can use that memory like it's memory, right? And the kernel is in charge of making sure that any changes I make to that memory region end up on disk, okay? So let me see how much time we have, because this stuff is going to be, all right, I'll go through this quickly. This stuff will be covered in recitation, right? This is now sort of OS 161 specific stuff, but this is some example of how some of this works, right? So here's an example, machine memory layout, right? Some of this starts to get very device specific, right? Different devices, different hardware devices implement different memory regions differently, right? And have different semantics about how these things work. The 32-bit MAPS architecture has the following semantics, right? So addresses are 32 bits wide, right? So the smallest address I can use is OX0, the biggest is this thing, right? OXFs. The MAPS architecture defines four address regions, okay? The first two gigabytes, so this is four gigabytes of address space. The first two gigabytes are reserved for process virtual addresses. These addresses can be translated by processes when running in unprivileged mode, right? And they can point anywhere, right? These are translated by the kernel, according to mappings that the kernel sets up, but these are accessible to user processes, right? So the user process has at most two gigabytes of address space it can use, right? Remember we said four initially, and now it's like we're changing the game, right? But it's two, right? Sorry, this is MIPS, right? Machines have like 120 megabytes of memory, right? So it wasn't like a huge restriction to be like, you're gonna have two gigabytes of virtual address space, okay? The next 512 megabytes, and I have a graphic for this, don't worry, is what's called kernel direct mapped addresses. These are only accessible when running in privileged mode, and these are translated through a very, very simple process, right? The way that the MIPS CPU translates addresses in this region is it takes the address and it subtracts OX80 million, right? It lops off the top bit. So where does this address translate to? If the kernel, if you do a write to this address, where does it go? Door. Zero, right? And this actually turns out to be where the exception handlers are located, right? So this can be a kind of a nice technique because this means that there's a portion of the address space that is translated directly by the CPU and the kernel doesn't have to load any mappings, right? So this mapping is done directly, right? The next 512 megabytes are similarly mapped by hardware, but they're not cached, right? And I'm gonna let Aditya talk about in recitation why the caching behavior of these two segments is different, right? The final top one gigabyte are kernel virtual addresses, right? These addresses are translated by the system like in the ways that we've talked about using all these flexible different tools, but they're only accessible when you're running in privileged mode, right? So what ends up, the memory map ends up looking like, right? Lowest virtual address, highest virtual address. User virtual addresses blow 0x80 million, right? This 512 megabyte direct map section that's gonna be quite useful to you, right? And right now all of your kernel addresses are in here and pretty much they always will be, right? You won't really use any of these other areas, right? Kernel virtual addresses, if you get super ambitious for assignment three, you can try to figure out how to use this part of the address space. I don't recommend it, but huge props if you can get it to work. It should work, right? It's just very hard to do properly. And then in this area, this actually turns out to be where devices are mapped, right? And I think, okay, cool. So on Monday, we're gonna talk about the mechanism and policy associated with actually translating these addresses, right?  Both hardware and software are involved and we'll keep going and this'll be fine. So see you on Monday. And this will be the last material on Monday that will be on the exam.


I apologize in advance. So it's a job work with the HSU and Maria, the ATIS, and so on. And we're dealing with the collision search, and we're motivated by applications in the symmetric crypto. So first, I give some details about the comparative context and then draw a brief state of the art of quantum collision search before presenting or on your algorithm. So where are we? Well, we're studying symmetric cryptography where we have generic attacks, which define the ideal security of our surface. We also have this constant effort of fraternizes, which increases confidence in the permitting we use. However, I would rather focus on generic attacks. So no fraternizes for today. And these generic attacks that we apply, offer, for example, the HSU search, birthday paradox, collision finding, and so forth. As mentioned, the title, we are studying the situation from the point of view of quantum adversary. So someone who has access to an investable universal scalable quantum computer. So we already know that in the asymmetric setting, RSA and HCC can be broken by such a computer. So that yesterday, and this is why there is much work going on on designing new primitives. Symmetric cryptography, the situation is a bit different. So we know that a ruler can speed up a quality factor of any kind of exhaustive search. So this is quantum-generic attacks. But there has been less work on this topic. So the hypothesis is changing, as can be seen from this session. So now we need to focus a bit more on what can be done using quantum computers in cryptanalysis and what other generic attacks could be created. So let's focus on collision search using quantum algorithms. The problem we're studying is the classical region search problem. So we have, let's say, black dot quantum function, which is a hash function. And we hope for a computer to have the same image. In the classical world, this is done in 2 to the 8 of 2. So it's a pre-route of the domain with a polynomial memory using Polarth-Roh method. And we have a related problem, which is multi-target pre-image search. So searching for the pre-image of one target among many. And this can be done using exhaustive search. And what can be done in the quantum saving? So we have Polarth-Roh there. If you want to apply global algorithm to search for collisions, actually, this doesn't help. The time complexity and the correlation complexity is 2 to the n over 2. But we have this algorithm by Grasseur, we have that, which is so other, which gives you a great complexity to the n over 3, which is actually optimal. But they didn't precisely analyze the time and time complexity and qubits used. And we'll go back on this later. Because contrary to the previous talk, we're much more interested in the time complexity and the number of qubits used in the circuit model. So we count the number of quantum gates that we apply, and we count the number of qubits that our circuit is using. This seems to be a rather relevant cost metric for spectrographers. So we also have this last algorithm by Ominiage, which was actually created for the element distinguish problem. It works as well with 2 to the n over 3 queries, qubits, and time, and it's optimal as well. So in short, the situation seems rather subtle. The collision lower bound has been reached. But these available quantum algorithms have become on the downside. It's that if we want to work before the classical bound, we need more than a little number of qubits. And actually, having that many qubits could be as hard as having a large distributed quantum computer. Since you can use qubits as well for storing memory or for computing. And this large quantum memory, we would like to reduce it, or even to get rid of it. There was a challenge asked by the government for better quantum time quantum memory trade-offs for these problems. And this is what we're dealing with. So we are restricting the number of qubits available to what seems to us a more reasonable quantum computer. So it's a phenomenal number of qubits. This is what Grover's algorithm used, actually. So we have these limited resources. And we are focusing on the theoretical algorithm than on implementation details. So no error correcting codes, for example, only the circuit model. So this is our results. So we have a new line here. We're able to improve on the classical 2-2-2-2 with a suboptimal number of qubits. But do you think this one is a number of qubits? So a very small number of qubits. And there is also a small amount of classical memory that I have to explain later. But I'm keeping it there. So now let's see how to do it. OK. First, we need a tool, Grover's search, classical 1. So you have access to an efficiently computable test function. So this is an oracle that will pay you whether a state is good or bad. The capitation is for quantum states. So basically ket x represents the string x. And this oracle is implemented, since it is an efficient function, as a quantum unitary that acts not only on states but also on superpositions of states, since it's a linear operator. So we're looking for an x such that f of x equals 1. It's a good state. And we know that if there is only one solution and we want to do the n, we get 1 in time to do the n over 2. So quality improvement of the classical 1. And there are 2-2-2 solutions. We get the superposition of all of them in time to do the n minus t over 2. OK. So this is the first tool we need. Grover is used as a subroutine in Brassar, we have a TAPS agreement in 1988. And this works in two states. So it's so material, actually. First, we need to perform a number of arbitrary classical queries to the finite age. And then we need to search for a collision on one of these queries. So the oracle for good states is there. He takes basically whether we found the collision. And the number of queries is 2 to the n over 3 for the initial list, so for the first step. And the second step, since we have 2 to the n over 3 solutions, we have the same number of queries. So everything is fine from this point of view. But while we're implementing this oracle, we need, of course, to query the function age in superposition. But age is, let's say, a hash function whose their function is public. So we can query this. No problem in this first step. And then we need to answer a query of the form, does this element appear in the list? Where this element is in superposition, because the function age is called as a quantum variable. And this is easy if you have 2 to the n over 3 quantum memory, so qubits, to store the whole list, because you're going to use a quantum round model, for example, and ask superposition membership query in little time. But if you haven't, because we're restricted the number of qubits, if you haven't any quantum data structure, this cannot be done. So how are we going to do it? There is a way, but it's different. Actually, there is always this way you can do. You haven't any quantum data structure to hold the list. So we're going to check against all elements in linear time, in the size of the list, sequentially. So ask if it's the first one, or the second one, and go all through the list, without holding any data and memory at one point. And this takes much more time than before. And this makes the cost much higher than before. So if you haven't any quantum data structure, actually the cost of the mass of random tasks algorithm becomes much higher. It becomes even higher than the classical one. So it's not helpful. Because each time we compute an iteration, we need to query this superposition to the list. And each time we query this, we need to do the n over 3 computations to go all through the list. So we're going to improve on that. And we need two ideas to do this. The first one is to use a distinguish points method. And the second one is to adapt the size of the list. We don't need it to be 2 to the anniversary. In the end, we balance the cost, and we hopefully obtain what I claimed earlier. So let's take an arbitrary definition of distinguish points. So all x's with image starts with a prefix of zeros. And what we can do now is build a list of distinguish points only. And then when we search, search for equation, but only among the distinguish points. So we are making the Grover, transforming the Grover subroutine into an integer application, where we first have to build the serve space, which is a space of distinguish points. And then I need to iteration of Grover test. Membership to our list. So we also have two steps here. First, building a list, and second, looking for a collision of this list. Well, now that we have done that, we actually don't need the list to be of this size anymore. So let's drop the end of our free. We take a list of distinguish points of the time we want. First, we build the list. So we need a list for each point to Grover search. And then we need to search for a collision on this list. So collision that will, of course, happen on distinguish points. So we build the serve space of distinguish points. We perform or sequential membership queries to this. And we write this into an integer application. So this is how it works, and this is how you can summarize it in natural language. So now we need to optimize those two parameters. And check that I'm not mistaken. So here all day, we take this of size, let's say, to the end of our five, and the prefix of size, to end of our five. So distinguish points will be points having a prefix of to end of our five zeros, with image as a prefix of to end of our five zeros. And the whole time of this procedure, first step and second step becomes to the end of our five. And the qubits that we use in this procedure are polynomial number for the first step and the second step, not more than Grover's algorithm. And there is something else coming, of course, because between the first step and the second step, we need to store that list of distinguish points we are working with. And that list has size to the end of our five. So somewhere we need the classical memory, of size to the end of our five elements. But to the end of our five is hopefully low for practical applications. There is also a way to make this algorithm run in parallel, so if you have not only one quantum processor but multiple quantum processors, so if you take n equals 128, and we have a range where we can still outperform the classical parallelization. And this, of course, has also a slightly better parallelization overhead than Grover's algorithm, which parallelization is not very well. Actually, the reality problem of multi-tagging pre-image search can be attacked in the same way. So here we gave beforehand the list of targets, and we're looking for the pre-image of one of these targets. Actually, we look for some x such that h of x is in this list. So what we need to do is only search among distinguish points with the same method. So we take that there is a cost of the t, which will spoon through the whole list of targets to select the distinguish points. And then we perform the same search operation as before. The lowest complexity achievable with this method, since we are counting these cells here, is two to the three end of the set. OK, so now we know that with a quantum computer and only a written number of qubits, so a restricted quantum computer, we can search for a collision on hash functions due to the two end of the five and for pre-image in this time. And we can give concrete parameters for it. For example, if you have a n equals 160 bits, classical collision search would take time to do the 80. And with this algorithm, what the time becomes to do the 64. And there is this classical list that needs to be stored anyway. But it takes only 100 gigabytes. This is very similar for multi-target pre-image search. Now the applications in photography, because we can think now to multi-user setting, for example, where this is an instance of multi-target pre-image search, where basically you have created the same message using lots of different keys. And you're looking for one of the keys. So this is an instance of the same problem that you can attack with this quantum algorithm. So what do we do? Basically, we can not know that we can accelerate collision search in a quantum setting, even with a restricted quantum memory. So I put it on your quantum memory as an overall algorithm. The question is now, are we doomed to say at least two n over five experiments? Or can we reach the level of 2 to the n over 3 using perhaps more advanced techniques? We looked on quantum walks. But for example, quantum walks use large amounts of quantum memory, so they weren't of any help so far. We also planned using this algorithm as a building block for more intricate quantum attacks. And perhaps also use similar methods to reduce quantum memory requirements for other types of attacks. Thank you.


Bontal Modi's L is under fire after her husband Pretty Ugly gets knocked out by Kasper Novist. Mufasa aka Kasper Novist promised to knock Pretty Ugly out, but nobody thought that would happen in one round. To everybody's surprise, rapper Pretty Ugly lost the match in a sweet K.O match. Yesterday, the highly anticipated celebrity boxing match between rappers Kasper Novist and Pretty Ugly took place at the Sunbed Arena at Times Square Casino in Pretoria last night. What was supposed to last five rounds, ended up being an embarrassing two-second one-round match. Pretty Ugly got knocked out so many times the memes are absolutely hilarious. His wife Bontal Modi's L accompanied him to the ring like the supportive wife she is. However, right after the first round, Bontal's disappointed face made rounds on social media. Others comforted the couple against the mean tweets from trolls. I hope Bontal continues supporting her man. They managed to secure the bag tonight for one minute of work. He's still a winner, wrote Atorian Goddess. At least he has Bontal, that's a win for life, at Lalum Thokosa Sea. Bontal sees her husband being cyberbullied every other two weeks and she allowed him to fight Kasper, and he got trapped, strength and love to her, at Seelstan 231. I think I respected that Bontal was on his man's corner regardless of how the outcome of the fight would be. She knew it would be a 50-50th spot supported him to the end. Joke aside, pretty ugly might have lost but he's got a woman, I respect them, said Ator Elis 201.  He gave Kasper his flowers and congratulated him on the win. He admitted though that boxing is not easy and that he has never been knocked out so easily. Kasper on the other hand also thanked pretty for agreeing to step in the ring and said people will be talking about boxing because of them.


It's the time of year when speculations of the new theme of American Horror Story run rampant and Ryan Murphy relishes in knowing everything that we don't. But now we officially have a title for the new Murder House and Coven crossover season of American Horror Story, Apocalypse. Ladies and gentlemen, this is Matt Rogers. For the past few months avid fans have been speculating that the much anticipated eighth season will be a fallout or a nuclear-based On set photos from alleged insiders showed the AHS logo with a radioactive symbol alongside it. Creator Ryan Murphy then added fuel to the fire saying that fans should take notice of what will happen in October of 2019, a full year after the season premieres. And Reddit user Gatto1980 noticed that quote, October 6th will be the 58th anniversary of President Kennedy telling Americans to build nuclear bomb shelters. I know it's kind of a stretch and probably not right, I just thought it might have something to do with the nuclear title of the season that's been floating around. End quote.  I must admit I love the worlds he creates, but he just can't help himself but spoil some of the best surprises of the past seasons. And if you ask me it seems like a desperate attempt to seem relevant. It's a miracle that he managed to keep the sixth season Roanoke under wraps until the premiere. Speaking of Roanoke, I was obsessed with the ambiguous teasers that were dropped for this season and now that we have the official posters for Apocalypse it makes me wonder if it was foreshadowed in some of the season six teasers. So Ryan Murphy tweeted back in June quote, the Coven murder house AHS crossover season won't be happening next year because it's happening this year. AHS number eight witches ruled this September end quote. And rumor has it the Cordelia Fox's Coven from season three will be taking on the spawn of Evan Peters Tate from season one. Rumored to be the Antichrist and I guess represented by the red baby in the poster being grabbed by the hand of Satan or at least some sort of demon. Since then Ryan Murphy has continued to spout spoilers to Entertainment Weekly saying quote, it's a character from season one that will be thrust into the world that you're left with at the end of Coven which is sort of like the male female witch academy. And then all of those characters will intertwine which is confusing because some of them are from seasons one and three like Sarah Paulson will probably be playing 18 characters end quote. I don't think anyone will be mad no matter how many roles Sarah Paulson plays because she owns every single one that she steps into the shoes of. On a side note Paulson has just been announced as one of the main stars in M Night Shyamalan's new thriller Glass. But while we're on the topic of cast alongside Paulson FX has confirmed Kathy Bates, Evan Peters, Billy Lord, Billy Eichner, Cheyenne Jackson, Edina Porter and recently confirming Emma Roberts as Madison Montgomery and newcomer Hollywood veteran Joan Collins. All I want is just one more season with Jessica Lang. Freak Show was a terrible season to send her off with and she obviously hasn't retired from acting like she said she would as she worked with Murphy in his other FX show Feud. So he is hoping and although Fiona Good is surely dead I'm sure they could find a place for Constance who is actually the one raising the spawn of Satan at the end of season one. Murphy has said we're going to see so many fan favourites return it's going to feel like the love boat and as much as I love seasons one and three I just hope that cameos and fan service don't get in the way of good storytelling like there was in season two in Asylum. Now it's been said that there's going to be a hook in episode five of this upcoming season and apparently it's going to be like nothing we've ever seen before. Roanoke had something along those lines with it breaking the fourth wall about halfway through but I guess this is taking it to a whole new level. But let me know what do you guys think? Are you excited for an end of the world take on American Horror Story or do you think that Ryan Murphy is clinging on to fan favourites to feed his insatiable need for attention? I'll be chatting with you guys in the comments but until next time thanks so much for hanging out if you had a good time then spank that like button and if you subscribe during this video then welcome aboard this is Matt Rogers and that is all


So instead of trying to combat consumer behavior, at Loop we're trying to play into consumer behavior where we tell you, look, you don't have to clean your used packaging once it's done, you don't have to sort it, you can in fact treat it like garbage. But when you put it in your garbage bin instead of ending up in a landfill or better a recycling center, it ends up back with Loop and gets cleaned and reused. Loop is all about how do we match the convenience of disposability by giving consumers the closest thing to a disposable experience while acting reusable. And I think this is a great lesson in sustainability business models is sustainability ought not to be a sacrifice because then we're going to be only appealing to one or two percent of the public. If sustainability could be better, a better future where it happens to be more sustainable then consumers will flock to that in a much bigger way. In January 2017 at the World Economic Forum we launched the world's first shampoo bottle with Procter & Gamble made 25 percent from ocean plastic and we were celebrating by this point in time our business had been 15 years of straight growth and it was a great moment to step back and ask ourselves, is recycling and making from recycled materials the core of the terror cycle business the answer to waste? And in that moment, I remember this so clearly at Davos, we realized that it is critically important but only the solution to the symptom of waste and that question led on a journey to say okay, if recycling is not the foundational answer to eliminating the idea of waste, what is? And we realized that the real foundational cause of waste is disposability or using things once which then asked the question how do we make durability or reusability mainstream and at scale? And that's where Loop was born.


In a world of politics dominated by the strange, the deranged, and outright insane, we'll now take a moment to shine a light on the craziest of what politics has to offer. This is your weekly dose of stupidity. Now this week, we have a gentleman who walked into a fast food restaurant, they have a mask requirement.  Take a look.  He's a counter. He pissed on the counter. It's disgusting. Can I just point out? He's projecting. That's some Olympic level projection right there. Wow. Yeah, those fast food employees do not get paid enough to deal with him. I don't care if they were making $2,000 per hour. There's no amount of money that is worth it to like deal with that level of delusions. This is a microcosm of a bigger issue. We've seen these weird anti-mask meltdowns across the country. I have a video coming out tomorrow of anti-mask parents showing up to school boards across the country threatening violence, assaulting people. On top of that, we have this phenomenon where as these mask mandates get reinstituted by governors, you have local sheriff departments penning these letters, grandstanding basically saying, we're not going to enforce your mask mandates when it's most of the time up to businesses because there's not really action at the federal level to enforce mask mandates. I don't necessarily know that our commentary can add to that wonderful video that is going to send me into another doomer week. Jordan, what are your thoughts on the state of American life? You know, Mike, I have traveled for six years, probably, I don't know, probably been to about 35 states. There's a lot of empty land in America, a lot of empty land where if you really wanted to do some organizing, you could really remove, well I don't say remove, but just kindly move a certain swath of the population to that land where they could live, you know, just put a wall up, as Trump would say, and they could live unvaccinated, massless, pee wherever they want, and you know, if they don't even want to have a government there, that's fine, as long as they're walled in. Because again, I said it earlier, this is not about masks and it's not about vaccines. It is about the insane culture war that, you know, frankly, we don't have time, but like a lot of it has been perpetuated by the media over the last 30 years, Fox, Rush Limbaugh, to a less, you know, on the other side, you know, MSNBC, now CNN, but mostly right wing lunatics, and the Republican Party, which has really become kind of a quasi-media apparatus, masquerading his government.  Because I saw this video and I was speechless. I actually am not shocked. I mean, that's the thing. I agree with what Jordan's saying here. This is a group of incredibly radicalized folks. So you've got Proud Boys, you've got neo-Nazis, you've got Trump supporters. They've all sort of coalesced together in this sort of a cult that they're in and they're just jumping from cause to cause mindlessly. I don't even think, like if you'd asked several of these people if they were super anti-vaccine 10 years ago, I doubt they would have said yes, yet here we are. So they're very radicalized. I think it's a culture war issue as Jordan is saying. And it's just really sad that this is what America is like, this is America. Like this is where we're at. Like it's amazing to me that, yeah, I know, it's shocking, right? It's shocking that it's gotten this ridiculous, but here we are. And I don't think we've seen the worst of it yet. I think we're going to see more violence in the streets as well. So it is a sign of the times. It is dystopian as the name of the show is stating. And I think it's going to get much worse because these guys are incredibly radicalized. And when this is done, they'll just find something else to get behind. Because really, nobody's taken away your freedoms, folks. These were folks that were fine with private businesses being able to make their decisions previously, right? It's a private business. They should be able to say, this is what I want in my establishment, right? But now that it's, well, you're telling me I have to wear a mask and that's against my personal freedom, now that they framed it in that sort of right-wing rhetoric, it's an entirely different conversation. But here's the thing. They were very happy with right-wingers not selling cakes to gay people. That's right. Exactly.  Exactly. So they can choose where that applies, right? Yeah. I'm really thinking of doing an app or at least a Facebook page about the businesses in Asheville that are not enforcing the mask mandate. I mean, I'm trying to, I go into these businesses and I have one of these encounters every day. I was in the Ingalls, which is the supermarket. And this was the first day. So they could have a pass. Maybe they didn't find out about it. But one of the guys at the cash register had a mask. The other one didn't. And I said, you know, they just declared a state of emergency in Buncombe County, and you're supposed to be wearing masks and doors now. And they kind of ignored me.  And as I almost walked out the door, I heard him mock me. It's the law, you know? And then my son's own store, he's working in the ACE hardware. Hi, James. He, I went in there the other day and, you know, the women behind the cash register, they were wearing masks. But this guy that was walking right in front of me, customer, was not masked. And I said, you know, we're supposed to be wearing masks. And I'm so tentative now. It's usually not like me. But I never know. These people are going to hit me with the baseball bat or not because I don't know. We're supposed to be wearing masks now.  And he took his t-shirt and went above his mouth, you know?  I've had both my vaccines. And I said, but you could still be a carrier. And she said, I don't want to hear your opinion. I don't know how much we can do as individuals, you know, I feel like we've got to start boycotting these places. Get the word. Yeah, I don't. I guess I don't understand why they're like so severely politicizing what should be only a public health crisis. But that is what they're choosing to do. And it's become so problematic. We had a couple of them show up at different target stores wearing women's underwear over their heads. Wow. Yeah. And they said, this is a mask, you have to let us in. And I'm like, why are you doing this? I mean, yeah, that's intense. Like, the idea of like, so you said Nashville and, you know, one of the things that, yeah. So, so, so Asheville, like, okay, I thought Tennessee. So it's not Tennessee. You're right. Okay, so that's a fair state. It still fits my, my conversation about this point is like, I wish like the moral consistency in the hippo, we've already talked about hypocrisy already once already today. But the hypocrisy of how we manage things, for example, since I'm a trans rights activist, and I, you know, work for a national nonprofit and a national researcher on this stuff, you see things like in Tennessee, and then in North Carolina, they actually are like for seeing businesses to do things. And the law and the government says that's the way it is.  But if you say, no, it's my personal freedom to send my kid, or no, it's my personal freedom, like in Arkansas, to give my kid hormone replacement therapies. They're like, no, it's not, it's the government's right to be able to control that aspect of your body and not let you do the things that you want to do.  I remember I lived in Texas, and I remember the Texas conservative values that I was raised with.  And we have the right to refuse service to anyone. I remember being raised with businesses having that right to refuse service to anyone. So now all of a sudden when businesses start doing that to them, that's when they start getting mad. And what Jordan said about the bakers, like, it's like, oh my God, like the whole world ended because somebody was forced to be a part of that public space.  Like it's just like, it's so infuriating, especially for me as a woman of color that is trans, right, that has all of these minority intersections and people that don't want to wear a freaking cloth on their face literally try to say that their oppression trumps everything that anybody has ever faced, but yet critical race theory. Yet, you know, cops aren't racist. Oh yeah, none of that exists. But yet you oppress me because I don't have a mask.    They'll say both on the same time. And so that's the world that it is for like people who realize like these people are literally crying oppression in the worst ways when our country has been founded on oppressions of minorities. And now because some Karen or some guy wants to just say, I don't want to wear a mask. It's just, I know I'm extreme of consciousness, angry about this, but I love it. The hypocrisy is annoying. And a lot of them are comparing it to the Holocaust. They absolutely are, which is so offensive. It's so offensive. It's not a genocide, folks. It's a public health crisis. Just stop that right now. It's so infuriating.