 All right, good afternoon. So camera doesn't want to work, so you'll have this very unflattering angle. So hopefully it's OK. So today, one of the last content lectures on virtual memory or sorry, virtual machines. But spoiler alert, it's going to involve virtual memory. So everything involves virtual memory at the end of the day. So virtual machines. So everyone has been using these, well, as long as you're not actually using Linux. So how many people know they actually have been using a virtual machine this whole semester? Ish, like two, three people. So if you're on Windows, or if you're on Mac, and you use the dev container at all, you're actually using a virtual machine and then more stuff on top of that. So we will talk about what you've probably been doing this semester. So so far we've talked about virtualization, mostly in the context of virtual memory, which, remember, virtualization is just like pooling something into thinking it has all the resources. So if we extend a virtualization from just memory to being the entire machine, well, that's what a virtual machine is. So the goal of that is given a single machine. You should be able to run multiple operating systems on that single machine. And each operating system should think it is the only one running, and it should not have any knowledge of any other operating system whatsoever. So there's some terminology when we talk about virtual machines, and the first is the host, and that is the hardware which actually controls everything. So the stream should be up. If it's not, it's the wireless. It should look like it is up, and it's recording. So if not, I'll have to post a recording afterwards. So yeah, things don't seem to be working today. So the hypervisor, sometimes you might call it the virtual machine manager as well. That is the piece of software that controls virtual machines. And where it lives depends on what type of implementation you have. So basically it controls like if you can create virtual machines, how you manage them, what resources they should actually get, what hardware should be protected from one virtual machine because you might not want to share all the virtual machines. For instance, has anyone ever heard of North Star OS? So that is North Korea's official operating system of choice. And if you want to run something like that, perhaps you don't want to let it use the internet. So you could run it in a virtual machine, not give it access to the internet, and see what North Korea is up to. So that's some fun things you can do with virtual machines. So there's two kinds of hypervisors, or in other words, the actual software or hardware that is controlling the virtual machine. So there is a type one, and that's called a bare metal hypervisor. So it's actually hardware supported, and it runs directly on the whole hardware. And your hardware actually needs special support for this, which thankfully all of our CPUs nowadays do have support for this. But this was more of a thing back in the day. And then type two is called a hosted hypervisor. And it simulates a hypervisor in software. So your virtual machine just runs as a normal user application like anything else would. And essentially all of the emulation and things it does, well, it's just running as a user process. So you have to emulate them or fake them somehow. And we'll talk about that a bit today. So it's a bit slower because you have to emulate things. It just runs as a normal user process. It doesn't actually have any special access to hardware. It's just like your program, where you have to talk to the kernel. It is no different whatsoever. So it's going to be a bunch slower, but it doesn't require any special hardware, especially if it is something that maybe it's a new type of CPU that doesn't even exist yet, and you actually need to emulate it. And then the other terminology here is if there's a host, there is a guest. And a guest is the actual virtual machine that's running, that's the kernel running. And it sees its own virtual copy of the host. So whatever the hypervisor wants to expose to it, so it might be your full computer, which is generally what happens for you guys if you're running the dev container. It has the same access to the rest of your hardware. But again, if you want to run North Korea's operating system, probably don't want to give it network access. So here is a diagram of both of them. So the diagram on the left, that part A, is what we've been living in so far. So we know we have hardware. Above hardware is the kernel, which is our special software that runs in kernel mode, which is a special mode on the CPU that actually is accessed to the hardware directly. And then the programming interface between the kernel and your user processes is that big system call interface. And that is the only way to interact with the kernel as a user process. And that's the layer that if we do S-trace, we see all the calls between a process and the kernel. So we can see exactly what all the processes are doing. Yep. So we'll get into it. So it's where the virtual machine manager is kind of depends. So yeah. So at part B there, generally, it's shown above the hardware, so where the kernel would normally be. So directly above the hardware, the virtual machine manager is above that, and it talks directly to hardware. And this is the case for a bare metal hypervisor, which actually needs special hardware support. So on top of the virtual machine manager, which is called the hypervisor, each virtual machine runs on top of that. So it gets a subset of the hardware that's exposed through the virtual machine manager. And then a kernel runs on top of the virtual machine. So it should have access to the same kernel mode instructions and everything. But instead of talking directly to hardware, it actually goes through a virtual machine manager, which then tells it what hardware it can actually talk to. And then in this case, with figure B there, we have three operating systems all running. So they all run their separate kernels, and they all have that same programming interface. So that could be three types of Linux distributions. That could be macOS, Windows, Linux, whatever. But it's just going to run whatever the kernel is for that operating system. It's going to have that same programming interface between process and the kernel. Nothing's really going to change. So there's a difference between virtualization and emulation. So virtual machines are not emulation. So emulation is typically used to translate one ISA or instruction set architecture to another. If you don't know what that means, it's also like your assembly language or the machine code. Like the ones and zeros your CPU actually understands. So if I have x86, any of you running a Windows laptop, well, you might want to run some ARM one. Or if you want to emulate, I don't know, like an Android phone or something like that, those typically use ARM. So that would be emulation translating one instruction set to another. And if you are running a macOS, a new macOS laptop, well, that's actually running ARM. And you might want to emulate x86. And it actually has some hardware support for that. But typically for virtual machines, we don't want to do emulation, or that's not part of it. You can do it in addition to, but it is a different concept. So our guest operating system, what we would like to happen if you want things to run fast is you run execute instructions using the same instruction set architecture. So you're assuming you are running on the same CPU. Otherwise, if you wanted to run on a different architecture, you would have to do emulation on top of this, which is going to be slow. So in some cases, you don't care. So if you're emulating really old hardware, like an original Nintendo system or something like that, that uses an 8-bit CPU. And it is not that complicated. So you can write an emulator for that that actually just translates the instructions. And I've done that before. It's a fun little project. It's kind of satisfying whenever you finally get Mario running on your emulator. It's really fun. But virtual machine can choose to use emulation or not, as long as it's on a different instruction set architecture. But generally, if you're running a modern operating system, the performance is just going to be really, really bad unless you have some hardware support or something like that. So fun things virtual machines enable. Pause and play is a really fun one. So much like our kernel can pause our process and then resume it, well, a hypervisor can pause an entire operating system. So it can pause a kernel, run a different kernel, and it is the same concept. So your hypervisor, guess what, is going to have to have a scheduler. So it will have to schedule what kernel it wants to run at any given time. It would pause the hypervisor, do all the same basic mechanics of switching. But instead of context switching between processes, your context switching between kernels or operating systems. So the hypervisor, that's what would be doing the context switching between virtual machines and then on a virtual machine, that would also do context switching between processes running on that virtual machine. The hypervisor wouldn't actually know what processes are running on that virtual machine. It only cares about your virtual machines. So it would have to save the current state, restore it later, and this is pretty much the same thing as processes, except it's going to be a little more complicated because it would have to save things that are specific to kernel mode that you wouldn't have to do if you are the kernel switching between things in user mode. So it's a bit more complex, but it's essentially the same thing. So the benefit of this, too, is like we can move a process between CPUs? Well, guess what? You could move a virtual machine between physical machines if you really want to. And that is why this is used in the cloud. If you ever heard of the cloud, well, the cloud runs on virtual machines. And it gives you lots of flexibility. So if you have just a room full of computers, well, you can move virtual machines around the computers if one gets overloaded, as opposed to right now. If you want to use the Linux machines in the lab, you have to SSH into a random number you pick. And if it's full, then that's too bad. Just go pick another one, which is kind of lame. What they should be doing and what's been the thing for the last, I don't know, 15 years is you virtualize all of them. And give everyone a virtual machine. And if that machine gets full, it transparently just moves you to a computer that is not being used. And you don't have to do anything. And you are none the wiser. And in fact, it can move you to a computer, even if one just becomes overloaded. And when you started, it wasn't overloaded. And then suddenly a bunch of virtual machines started on it. And then it has to start migrating something. So hopefully we can move towards actually doing that. But right now that is not a thing for you. But when you actually go out into the real world, you will be using virtual machines literally at any company you ever go to. So they provide some nice protection through isolation. So the guests, like, processes are isolated from each other. The guest offering system should also be isolated from each other. And they should also be isolated from the host, just like processes should be isolated from the kernel. So the hypervisor can go ahead, set limits on how much CPU time, how much physical memory the virtual machines get, network bandwidth, et cetera. So if a compromised guest, like if one of the virtual machines gets hacked or whatever, well, it doesn't really matter because it only has access to its own hardware. And it doesn't have access to the entire machine. So it might just wreck that virtual machine. It might use all the memory that is given to that virtual machine, but it won't bring down that actual computer. And if you're running 10 virtual machines on a computer, nine of them would be OK. And that one would just be screwed. And if you are like a system admin or whatever, you can go ahead and destroy it or figure out that it's misbehaving somehow. And because these are all virtual machines, well, you can stop running it. You can roll it back if you want. So typically, virtual machines kind of have a virtual disk. So that's how they know how to load the kernel, what files are on that virtual machine. And typically, people take backups of it. So you could just roll it back to whenever you didn't have a virus, kind of like system restore now, but for virtual machines instead of your actual machine. So they also help with consolidation, which is kind of what I was talking about before. So like in data centers, you'll have lots and lots of servers running. And often, some of them will be idle. No one will be using them. And sometimes, you actually want to share servers. Sometimes, you don't want to share servers depending on whatever the load is on the application on a certain time. So instead of having some lightly used physical systems and trying to guess the load on each one and trying to balance it by hand, well, you just make them all virtual machines. And then what you can do is run as many as a single machine as possible. And if that single machine finally is too overloaded, you just go ahead and say, OK, I need more physical hardware. You go buy another one. And then you distribute the virtual machines across that. And if you find out that gets overloaded, you just get another virtual machine. And you just keep on doing that over and over and over again. So the key abstraction here for a virtual machine is a whole virtual CPU. So remember, when you were doing threads, you kind of had a virtual CPU. But that was only saving user state. So it was only saving registers that you actually can use and modify as part of your program. Well, a virtual machine for, or sorry, a virtual CPU for virtual machines saves the entire state of the processor. So it would have to save all of the things it's doing. For example, what mode is the current CPU in? So is it currently executing in user mode? Is it kernel mode? It would have to save a bunch more state, like, hey, what's the current L2 page table pointing at? And other things like that. So for processes, that process control block was fairly simple. It just had registers. And it kind of acted as a virtual CPU, but only supported things you can access in user mode and then virtualize everything. But a virtual CPU for a virtual machine stores the entire state of the CPU. So when the virtual machine resumes, just like a process control block, well, it loads the data, would load all the registers, would load kernel mode specific registers, and then resume, and you can start executing your virtual machine again. So the guest operating system is still the kernel. It still runs in kernel mode, and that may or may not be true hardware kernel mode, depending on if it is a type one or type two, but you don't change the kernel at all. None of the kernel code changes, so the kernel would still be responsible for setting up page tables and all that fun stuff. So it can still access those instructions that actually change the page table and everything like that. So remember when we were talking about kernel mode, we kind of said x86 user mode was called ring three and kernel mode was called ring zero, which is supposed to be the most privileged one. Well, you can see what date they decided to do that on because hypervisors and virtual machines came along like what, like 15 years ago, and it's actually supposed to be more privileged than kernel mode. So they had to make a new CPU mode called hypervisor mode and because they ran out of numbers, they just used negative one. So negative one is now the most privileged thing because it's a lower number than zero. So in x86, it's called, yeah, it's called ring negative one on most more modern CPUs like an ARM or risk five one. They actually just give them names. It's just called user mode, kernel mode and hypervisor mode, which is probably a bit more sane. But for type two hypervisors, there would not be any special hardware support for it. So the host actually has to create like a virtual user mode. So the kernel would still want to do things like change the root page table and things like that. But as we know, if we're running in user mode, in actual user mode, we're not allowed to do anything with the page table, but it still has to allow you to emulate that and somehow fake it that you can actually change the page table without actually being able to change the page table. So one strategy to do this is called trap and emulate. So this is again for type two hypervisors that are just running as user mode and trying to run like kernel mode only instructions. So any privilege instruction you try to execute generates a trap, which will say, hey, you're not allowed to execute this instruction. You are in the wrong mode. So if you really wanted to in your application, you could find out the actual assembly instruction to change the root page table and you could try executing it if you really wanted to. But as part of the protections of the kernel and everything, if you try and do that, it will send you a signal. It will say, hey, illegal instruction and probably give you a reason that, hey, you can't do that. So if you are actually implementing a hypervisor as a normal process and software, well, instead of just saying, oh, illegal instruction, I just need to quit out because I did something bad and exit, you actually get to handle it. So instead of whenever you see that your guest kernel tried to change the page table or something like that and generated a trap, well, instead of just failing the process, you get to emulate what it was actually trying to do. So in software, you would have to emulate the page tables and you would have to emulate the page table pointer and then as part of it, instead of just failing, you would just emulate actually doing that. So you would have to represent the entire CPU in software or the entire kernel mode part of the CPU in software. So this is going to be really, really slow. So instead of doing just one instruction, well, instead you would have to do one instruction. It generates a signal for you. Then you have to handle it. You have to emulate whatever it was supposed to do. And then you can resume. So it's going to be a lot slower than actually just running it with hardware support. That wouldn't need to do that, but it's something you can actually do. So this is kind of what it looks like visually. So your guest operating system, it would be just running as a normal user process on your actual kernel and your layer there is just done in software. So the VMM or virtual machine manager, this is also in user mode and it's supposed to be emulating your kernel. So if that guest kernel tried to execute some privileged instruction, like changing the root page table, well, it would generate a trap or an exception or a signal, something that you can handle. So you would need to catch that trap, handle it by emulating what it was actually supposed to do on the hardware and then update your model of that CPU. So you would have to keep track again of like the L2 root page tables and the register you actually use to change that root page table. So you'd update your virtual CPU and then you can return. Then you just start executing that kernel again until it runs something else you have to emulate or until it goes to user mode and then they kind of agree. So this doesn't always work. So this is actually useful if you have a very nicely designed CPU where there's a clear separation where some instructions only work in kernel mode and if you try and do them in user mode they just don't work, you get an exception. So that is unfortunately not how real CPUs work. So x86 is full of ill-advised decisions where there's not really a clear distinction. So on x86, well, that virtual machines didn't exist in the 70s when they first made the architecture. So they decided to use some shortcuts, some neat tricks that essentially makes trap and emulate pretty much, well it does make it impossible. So one example is an instruction called popf and it loads a flags register from the stack but the flag register changes depending on what CPU mode you're in. So if the current CPU mode is in user mode that instruction would modify a user flags mode register and if the CPU was actually in kernel mode whenever it executed that instructions it would modify the kernels flags register. So depending on what CPU mode you're in while it changes the behavior of the instruction and if we're writing a type two hypervisor we're actually in hardware, always in user mode so we couldn't tell whether or not it would just execute it as a user mode and then update the wrong register when in reality we would want to actually emulate updating the kernels flag register instead of doing the user flag register. So this wouldn't generate a trap or anything like that so you can't trap and emulate and these are called special instructions and we need a different approach for them. So they need something called binary translation. So binary translation pretty much would check if that virtual CPU of the guest is supposed to be in user mode. We don't have to do anything because the two modes agree, right? The guest offering system thinks it's in user mode and it's actually really in user mode so we don't have to do anything special. But if that guest thought it was in kernel mode while our software emulation of that virtual CPU would also be in kernel mode and the two modes would not agree. So if you are in this situation where the hypervisor knows that it's supposed to be in kernel mode and it's actually not, well it needs to slow the kernel down and actually start inspecting every instruction before it actually executes to see if it's one of these special case instructions. So it would have to slow it down, start inspecting every instruction before it actually executes and if it is a special instruction it needs to be translated to some instructions with the same effect or do that same emulation step again. And if it checks and finds that that is not a special instruction well it can just execute it natively and you're paying for the checks essentially. So the kernel would use a CPU instruction to like switch from user mode to kernel mode so the hypervisor could handle that using normal trap and emulate so that would always be instruction you can do trap and emulate so you can always know what CPU mode your guest kernel or guest offering system is supposed to be in. So overall because of this you only have to pay this if it's actually running in kernel mode. Thankfully our kernels try and stay out of the way and actually let us run our programs so the penalty you pay for doing this typically isn't actually that high and you can actually use this have fairly good performance and actually run it. And fun fact, so this binary translation is not just for hyperviders so everyone's hopefully used Valgrind at some point so Valgrind uses binary translation so that's why it's so slow if you actually care about performance it's like a hundred times slower because it's essentially doing the same thing so it is going to check if you call malloc by just looking at what instructions you call and every memory access instruction well it's gonna check that it's gonna do some type of check so that's why Valgrind is so slow and you don't have to recompile so fun fact, so yeah this is what it looks like visually so again you have your hypervisor which is just running in software trying to emulate a whole other operating system call it the guest so if the guest is actually just running user mode processes it doesn't have to do anything if that guest is supposed to actually be in kernel mode well then our hypervisor is going to have to inspect every instruction before it executes check if it's a special instruction if it's a special instruction we'd have to emulate it or just update what actually should happen if that actually executed in kernel mode and then you can keep on executing code as you wish and inspecting it as long as you are in kernel mode so, oh yeah, was that a question? so what do you mean that they don't correspond? yeah, so another thing you can do with virtual machines is yeah do exactly what you said and support instructions that don't actually exist on the native machine and you do the same thing so you can emulate it so it's just gonna be much slower but you could actually support your virtual machine could use hardware instructions that your actual hardware doesn't actually have so yeah, that's also something you can do and also it's really nice for testing and stuff like that too especially if you have an old system as long as you have support for this so yeah, to speed all this up so type two is kind of slow and this is where we get into hardware hypervisors so 2005, which is yeah, long time ago Intel introduced its first virtualization called VTX and in 2006 AMD followed suit and they called it of course something different it's called AMD-V and if you actually have a desktop computer and you try and use a virtual machine and it's really, really slow well you might have to actually go into your BIOS and actually enable virtualization and they called it a whole bunch of different names so it might be named something different so like Intel, while they were developing it it was called codename Vanderpool and they published the specification as virtual machine instructions so if you actually need to enable this in your computer it might be called one of three things it might be called VTX, Vanderpool or VMX which is kind of annoying and then for AMD it's also maybe called three names so their names were Pacifica or secure virtual machine SVM or AMD-V but they all mean essentially the same thing just different names for different points of development but if you have a desktop machine and you need to use a virtual machine you probably need to enable one of those so yeah, those all added the concept of ring native one or hypervisor mode and what typically happens now is when you boot up your machine well that host kernel is going to claim the hypervisor and also act as a hypervisor so if you boot up Linux it would see oh hey this machine has support for a hypervisor I'm going to be the hypervisor and claim mode negative one and then claim control over it and Windows is gonna do the same thing so if you enable virtualization on Windows well when Windows boots up it's gonna be like hey I am the hypervisor now I will control everything and in fact I think Windows 11 if you have certain features enabled will run itself as a virtual machine and just have a hypervisor that doesn't really do that much and that's for security reasons so it can make sure to verify the actual kernel so that you're not like cheating and valorant or something like that or doing something bad so yeah so when you boot up your host would be able to claim the hypervisor and it would be the one managing that negative one mode and you can set, it would set isolation for the guests and what hardware to virtualize and it would actually act as a hypervisor so it's kind of this weird dual role that most modern kernels have. All right so other fun things we can do so we can have virtualized scheduling so if there's only one CPU on the physical machine your guests aren't going to know so like emulating instructions that might not exist we can also emulate CPUs that might not exist so even if your machine had a single core you could have a virtual machine that has I don't know eight cores or something like that the guest is not going to be none the wiser because it doesn't actually know what's actually on the physical machine but the host can just fake it to the guests like you actually just fake running multiple processes at the same time and in actuality you just switch really, really fast. So this presents a fun scheduling problem to the hypervisor so now you would need to map virtual CPUs to physical CPU cores or you just schedule them like processes so we're kind of used to doing that. And like a normal kernel that would have kernel threads that go ahead and do some work for you while the hypervisor would also have hypervisor threads that do some behind the scenes cleanup and all that fun stuff. So one approach to do this, to simplify matters is it's a bit simpler. So if there's more physical cores on your machine then all of the virtual CPU cores across all of your virtual machines while you can just assign a virtual machine to run on a certain CPU core and never change it so it just has unfettered access to that one CPU core it can just use it as far as it's hard to content and the host could go ahead and use any spare cores that are running on the machine like some server machines have like 128 cores so it can run a lot of virtual machines and have some leftover to do its own stuff without having to swap back and forth between cores because as we all know, context switching is not free so it would take time so if we don't have to context switch that's fantastic. So if you do have to share that's where things get complicated and they call that over committing in resources so I'm presenting more virtual CPUs and I have physical CPUs on my machine. If they're at equal numbers you could still map them one to one because the hypervisor is kind of like a kernel where it should mostly stay out of your way most of the time so you could still map them one to one and then context switch whenever the hypervisor needs to run it only runs for a little bit and you kind of hope that's okay. So, but if you don't in general you'd have to use a scheduling algorithm just like you used for processes and it is the same thing you can do round robin you can do first in first out you can do whatever you want. So, oh, no question, no, no, okay. So, having over commitment of CPU causes some additional problems too so guest operating systems sometimes they might be running soft real-time tasks where you actually want to have dependable times and if it's always running on that CPU well you would have really dependable times because you don't get context switch you have exclusive access to that CPU but if your virtual machine gets context switch and it has no knowledge of if it is going to be context switch at all just randomly that virtual machine is going to just notice that sometimes it's really slow in between two instructions so it would get context switched out it would have no say in the matter and then when it gets context switch back in it would know a lot of time has passed and it didn't even get to execute an instruction like for example if you're like operating system normally is running round robin it has a time slice of like 10 milliseconds while the guest is not going to have consistent time slices of 10 milliseconds while if it ran on the actual physical machine the time slice would definitely be 10 milliseconds every time but because it's on a virtual machine it might get context switched at any point so it would start a context switch count up to like two milliseconds get context switched out for maybe like 100 milliseconds and then come back maybe wait another few milliseconds and then suddenly 100 and some odd milliseconds have passed when really you really only want 10 so this can make processes miss deadlines they wouldn't actually miss if they were actually running on a physical host where they just controlled the CPU directly so in this case virtualization will cause some observable behavior especially in performance and you may not want to do this especially if something is really really performance sensitive. All right so we haven't talked about virtual memory now so but now virtual memory is going to get a lot more complex too so remember virtual memory every process just thinks it has the entire address space and then the kernel is mapping all of those addresses well now the guest kernel thinks it has access to physical memory and it's virtualizing the process but now we're virtualizing the operating system so that's not actually accessing physical memory it's actually accessing virtual memory now so it's managing virtual memory and the hypervisor is actually managing the physical memory so now there are two levels of page tables so the hypervisor has its own set of page tables for the virtual machine and then the virtual machine has a set of page tables for each process so if you thought one level one multi-level page table is fun wait until you have to nest them together it's really fun so this also gets worse if memory is over committed as well so if your virtual machines use more memory than you actually have physical memory well now there's going to have to be some paging too and the hypervisor would do the paging and move stuff to disk randomly so it just gets more and more complicated it's page tables and paging all the way down to the bottom unfortunately so yeah like I said the guest still does the same guest things thinks it controls physical memory does page table arrangement but the hypervisor manages the guest's virtual memory so there's actually nested page tables so every memory access the guest makes well needs to get translated again for the guest and there's now two sets of multi-level page tables which you can imagine is really really slow to translate so instead of doing you know three steps I have to do three steps and then another three steps and then I finally get my memory so for some over committed memory you know maybe the hypervisor can do some double paging so the hypervisor would do its own page replacement algorithm and then sometimes you might not want this because as a guest kernel you might actually know your memory patterns better because you are controlling processes you probably know what's actually being used when and where so you might want to be the one implementing paging yourself instead of letting the hypervisor do it but this is mostly to illustrate that hey it has the same problem and there's two levels where you can solve the problem and the answer is always not that clear so there's some fun things we could do so remember copy on write well guess what hypervisors can do that too so just like you can share pages between processes if they're both only being read or they're otherwise supposed to be the same it's a bit more complicated if you are a hypervisor and you want to share pages between guests so a guest could share pages if they are completely the same and they're essentially duplicates of each other so whenever it creates new virtual machines there's no concept of fork or whatever so if you fork you know that the processes are supposed to have the same duplicated memory at the time of the fork but for virtual machines they're completely independent and you don't actually know if their memory is actually exactly the same or not so if you want to share do that same copy on write optimization between virtual machines like you would do copy on write between processes well the hypervisor would have to actually observe the memory that the pages use of the guest's operating systems and see if the pages are exactly the same so a fast way you can do this is you would scan all the pages and generate a hash for them so you generate a hash for them based off the contents of the pages and then instead of checking byte for byte whether two pages are the same to speed it up you only check that the hash of two pages are the same so if the hash of two pages are the same that doesn't necessarily mean they're exactly the same that means they could be the same or you could have got lucky and they actually were two different ones that hashed the same thing but you know for sure they're definitely they're not completely different so if the hashes are the same then you do the expensive check of checking if the pages are the same byte for byte and then if the hypervisor detects that hey both these pages between different guests are exactly the same then it can do that copy on write optimization between them and they can actually go ahead and share memory so then at that point aside from the detection of it it's the same copy on write we have before so nothing really changes about that all right good sweet so yeah it's just more and more and more of the same thing all right so more fun things you can do with it so you can just provide like a virtualized graphics card or something like that but that would be emulating hardware which would be really really slow so like a hypervisor can multiplex one device to multiple virtual machines so like a network card so it can share your physical network card and kind of multiplex it between each virtual machine so they can all share that network card just like multiple processes can all share your network card and like the same question before the hypervisor could also just emulate hardware that doesn't actually exist on that machine so even if you don't have a network card or something like that you could emulate a network card and the virtual machine would just use it and then depending on how you emulate it you can actually see the output of that network card or do whatever you want with it but another thing that is a bit newer is the hypervisor can also map one physical device to one virtual machine and give that virtual machine exclusive access to that device and the hypervisor would still be involved in some translation but you could theoretically just say hey only let that one virtual machine have access to this hardware but that's going to be a bit slow because every time you wanna communicate with the hardware the hypervisor's still going to be in the way checking something you'd have to make calls to the hypervisor so one new solution here is to remove the hypervisor during runtime and that is something called IOMMU and that allows the hypervisor to map devices virtual memory and then map that exclusively to the guest so now the virtual machine is the only one with access to that hardware and that allows things like giving a virtual machine physical access to a GPU or something like that so it can run it at native speeds and you don't suffer any performance penalty of trying to translate things to a GPU you give a virtual machine that GPU directly and then you can let it run your machine learning job or do whatever you want or if you were trying to I don't know like use Linux every day but you really like games or something like that well you could give a Windows virtual machine a GPU and then run everything at full speed and you just only run Windows in a virtual machine without really any penalty so they also boot from a virtualized disk so they would have a virtualized disk of course so and that's all a virtual machine is so like a normal operating system you just boot it from your hard drive every time and it initializes everything while a virtual machine is just represented by a virtual disk so a virtual machine is basically just a disk image that contains the contents of a physical disk and it's essentially what you guys did is that rain? Jesus all right great this is gonna be a fun way home it's definitely gonna snow during our exam I know it okay yeah so sorry back on track so the virtualized disk image is gonna be like your lab six it's just gonna be like a file system that's represented by a file but instead of just having a bunch of lame hello world files and everything like that it would have the contents of your actual disk if you were to actually just boot it on your machine so you just tell the hypervisor hey boot that virtual disk image and just boot it normally and it would start up your virtual machine so usually it's just one big file like essentially what you did for lab six but much bigger and some formats allow you to split it up but that's not really important and the guest kernel would just see that file as a normal disk that it has full control over so that disk image is all you really need for a virtual machine and makes it really easy to move around so I could just give you a disk image and say run that as a virtual machine and you go ahead and run it if you are using something like open box you might see a virtual machine represented as like an OVA file or some other formats but basically that's just a big ass disk image and then some information about the virtual machine like how many virtual machines it should have and how much memory it should have so some useful things virtual machines can be used to isolate an application if you want to distribute it in a way that is really reproducible so again let's like assume our application uses a dynamic library that we talked about back in lab or lecture 10 where if someone changes the ABI or changes the library it could screw up that application completely and maybe they change something else that suddenly changes behavior and you don't want your program to break well what you could do instead is make a virtual machine that contains just your application with all the libraries it needs and just give that to people to run and then it's the same thing every time if you update the libraries on your system it doesn't affect the virtual machine so it'll be a nice consistent way to like deploy an application but it kind of sucks because your application is probably gonna be fairly small and the virtual machine will contain a kernel which is really really big and that's where Docker comes in so Docker is what you guys have been using they are called containers and they aim to be faster to give you all the same benefits without supplying a kernel as well so the hypervisor like sets limits on the CPU time network everything like that which is really good but the Linux kernel and only the Linux kernel has made something called C groups or container groups that allow you to set those things but for normal processes so you can isolate processes to a namespace give them only a certain percent CPU and kind of have those nice hypervisor features while just actually using the Linux kernel and not having to run a whole virtual machine so containers essentially have the same concept of virtual machine except you're essentially sharing the kernel but trying to get all those benefits and Docker and those containers only run on the Linux kernel so that's why I told you you are using a virtual machine so if you're using Windows or Mac you are actually using a Linux virtual machine and that Linux virtual machine is running your Docker desktop so even though you don't know you're using a virtual machine you're using Linux because it only support in Linux so if you're using Docker on Linux you're not using a virtual machine because it's just using your actual kernel all right so that's it so virtual machines they virtualize an entire physical machine so they allow multiple operating systems to share the same hardware just like multiple processes can share the same CPU provide some nice isolation and the hypervisor is the thing that controls all the allocation and however much percent it gets of CPU time and all that stuff type two hypervisors implemented only in user space they're slower because they need to emulate essentially kernel mode type one hypervisors are supported by hardware there's some other ways to speed them up hypervisors there's some problems with them because they might over commit on resources physically move a VM but sometimes that is something you actually desire and containers are trying to have all the benefits of virtual machines without having to supply a kernel every time so that's it for the content just remember phone for you we're all in this together oh and next lecture will be a small like kind of review thing and I will bring chocolate to entice you and you can work on lab six so we're not gonna do that much for