How does virtualizing differ from emulation, in terms of structure?

Solution 1:

Originally, you couldn't let the guest OS use real hardware because you had no way to control it. If you tried to run it on the real CPU, you had no guarantee that it would hand control back to the host OS.

Virtualization as you describe it is implemented in the hardware by allowing certain rules and restrictions to be applied at a hardware level, which can be managed by the host OS. This allows the host OS to set up rules about what the guest can and cannot do, and then actually run the guest on real hardware. If the guest tries to do something with the real hardware that violates the rules (such as trying to access a disk device), the hardware will suspend the guest and send the host an interrupt, which allows the host to provide a response (such as returning data from an emulated disk device), and then resume the guest.

Here's a simplified example of the process:

Host OS: Hey CPU, I need you to run this code virtualized. Call me if it wants to do something that isn't just executing instructions.

Host CPU: You got it!
Host CPU saves all host registers and state, and then starts executing Guest OS code

Guest OS: I'm alive! Hey CPU, can you get me this file?

Host CPU: Uh... sure. One moment.
Host CPU saves all guest registers and state, and then restores all host registers and state
Host CPU: Hey Host OS, the Guest wanted this file!

Host OS: Oh, well give them this: File from virtual hard drive

Host CPU: You got it!
Host CPU saves all host registers and state, restores guest registers and state, and then starts executing Guest OS code
Host CPU: Here's that file!

Guest OS: Sweet, thanks!

The key difference here is in an emulator, the guest OS is never actually running on the hardware. With virtualization, the host OS configure limitations into the CPU, and then actually runs the guest code on the physical CPU. The example above is extremely simplified, but memory, disk i/o, and even networking can be controlled on the latest of today's processors, allowing them to be interfaced safely without having to bother the host OS each time. As long as the guest doesn't try to go outside of the virtualized boundaries, then the Host OS may not have any code running if it has nothing to do at a given point in time.


To add a bit of perspective, this is just one more step in a long history of virtualization and control. (No guarantees that this is in correct order or is exhaustive, but should give a good starting overview)

Originally, there was no virtualization. Processes all shared the same memory space, all had full access to hardware, and the ability to multi-task was entirely dependent upon one process stopping itself and giving control to the next process. If the OS wanted to have any sort of control over a process, it had to run the process in an emulator (nobody did, because it was too painfully slow).

First was Privileged Memory: certain actions that can only be performed by special regions of memory. These regions are occupied by the OS, allowing it to act as a gateway to to these privileged actions. An example is the ability to read/write data to hardware. This prevents processes from reading/writing directly to the harddrive, and instead forces them to ask the OS to read/write for them. This means the OS can check if the process has permission before performing the action.

Next came virtualized "time", as it were. The OS could configure the CPU to interrupt the active process at set intervals, allowing it to take control of scheduling and switch between processes. The OS could now run processes directly on hardware and still prevent them from misusing CPU time. This was provided by a hardware timer.

Next came virtualized memory: The problem with shared memory is that any process could read the memory of any other process. What happens when Mary's program reads Bob's password from his web browser? Virtual memory allows the OS to map the memory that a process sees to different parts of physical memory, or even move them out of physical memory entirely (to the page file). Any time a process tries to read or write to memory, the VMMU (virtual memory management unit) of the CPU looks up where it is mapped to in physical memory, and performs the action there. If it's mapped out of memory, then the CPU calls the OS to fetch the page into memory from the page file.

Alright, so at this point we've reached about the beginnings of the X86 processor, where we can safely run processes and can actively prevent them from taking over the system unless the OS specifically allows them to do so. At this point, processes are effectively "virtualized". This support has been around for a long time, so you don't really hear people talking about virtualized processes, since it's just assumed that all processes are virtualized now.

Why are virtualized OS's special, though? Why can't we just start one up as a process and let it do it's own thing? Well, the problem is that as an OS, the guest system expects to be able to access and use the same controls that the host uses to control processes - basically, the OS expects to be supreme ruler of the computer, and they simply don't work if that's not the case. "Hardware Virtualization" extensions (AMD-V for AMD, and VT-x for Intel) allow the Host OS to provide a virtualized set of virtual process controls (virtual privileged memory, virtual hardware timers, virtual virtual memory).

Solution 2:

How do we enable an entire OS to run as a process in a virtual machine, but get it to run independently while still making use of the actual CPU?

(The following is simplified a lot.)

By leveraging the same or similar mechanism that the OS uses to keep user-mode processes in line, mostly.

User mode processes will cause CPU exceptions when they try to do something they are not allowed to do.

So, if we have an OS kernel run in user mode, anytime it tries to do something like access hardware directly, it will cause an exception. A hypervisor can pick up on that exception and respond with emulated or virtualized behavor, instead of causing a system crash like a normal kernel would.

It can perform the hardware access on behalf of this kernel, perform a modified hardware access (i.e. access a part of a file instead of a direct disk sector access), or whatever else you might dream up.

CPU virtual machine extensions basically extend the whole "supervisor" or "protected" mode of the CPU out one more level to do exactly this, and also provide an additional "nesting level" of virtual memory so paging is easier to virtualize.