Although it’s not really emulation, virtualization deserves a mention here. Whereas emulation is the process of one machine pretending to be of another type, virtualization is the process of one machine pretending to be two or more of the same type.
The x86 architecture is believed to be one of the most difficult to virtualize. Actually, this isn’t entirely true; starting with the 80386, all x86 CPUs have supported virtualization. The virtual 8086 mode allows the creation of independent virtual 8086 machines. This capability was used in Windows 3.x to allow MS-DOS applications to be run at once. These days, it isn’t particularly useful; very few programs that would run on an 8086 are still around, and those that are would run much faster in a full system emulator than they ever did on a real 8086.
The problem with the x86 instruction set is that it contains a small number of instructions that cannot be trapped. When attempting to virtualize something like IBM’s POWER architecture, you can run the guest OS at a lower privilege level than normal. Trying to execute a privileged instruction, however, causes a trap that’s caught by the hypervisor, which then emulates the instruction. With x86, there are a small number of instructions for which this isn’t possible.
The two common approaches to avoid this problem are binary rewriting and para-virtualization. Binary rewriting, as used by VMware, scans the instruction stream and replaces any occurrences of these instructions with calls to the virtualizer’s replacements. Para-virtualization is similar, but performs the replacement at compile time; the guest OS must be modified to call special hypervisor functions, rather than the standard privileged instructions. This is the approach used by Xen.
A virtualized system must perform some emulation; the privileged instructions are emulated even if the unprivileged ones are not. Usually, however, virtualization extends beyond the CPU. It’s useful for every virtual machine to have a network interface, for example. Unfortunately, your host machine only has one. Here you start to have a problem; most network interfaces were not designed with virtualization in mind. The simple solution is to provide every guest with an emulated NIC and multiplex them in the host. This technique is relatively easy on any *NIX system that already supports virtual interfaces (most do).
The situation is somewhat more complex when it comes to graphics. Most UNIX-like systems do some form of graphics virtualization already, in the form of virtual terminals. You can even run a GUI on some of these. If you’ve tried this, you may have noticed that when you switch from a text terminal to a terminal running X, the screen flickers quite a few times—something that doesn’t happen between text terminals. The reason is that X has absolutely no idea what the current state of the video hardware is. Worse, many graphics cards don’t have well-documented ways of switching between all of their supported modes. The solution is to drop back to a very simple mode, redo the initialization of the more complex graphics modes, and then redraw the screen. While this should be done transparently, it’s impossible because most graphics cards don’t have any mechanism for retrieving and restoring the internal state.
It is theoretically possible in a system like Xen to write virtualization-aware graphics drivers, but it wouldn’t be a trivial undertaking. The Vista driver model also requires graphics cards to support virtualization, so this should be available in the Windows world shortly after the GNU HURD port of Duke Nukem Forever is released.