The problem was that we didn't tell UML what its root device was. This is an important special case of a more general property of UMLits hardware is configured on the fly. In contrast to a physical system, whose hardware is fixed, a virtual system can be different every time it is booted. So, it expects to be told, either on the command line or later via the mconsole interface, what hardware it possesses. Here, we will configure UML on the command line. The first order of business is to give it a proper root device so that it has something it can boot. As I mentioned earlier, UML devices are virtual and constructed from host resources. Specifically, UML's disks are generally (but not always, as we will see later) files in the host's filesystem. For example, here is the filesystem we will boot: host% ls -l ~/roots/debian_22 -rw-rw-r-- 1 jdike jdike 1074790400 Jan 27 18:31 \ /home/jdike/roots/debian_22 One obvious thing here is that the filesystem image is very large. file will tell us a bit more about it: host% file ~/roots/debian_22 /home/jdike/roots/debian_22: Linux rev 1.0 ext2 filesystem data This tells us that the data in this file is an ext2 filesystem image. In other words, we can loopback-mount it and see that it contains a full filesystem: host# mount ~/roots/debian_22 ~/mnt -o loop host% ls ~/mnt bfs boot dev floppy initrd lib mnt root tmp var bin cdrom etc home kernel lost+found proc sbin usr In fact, when mounting this as its root filesystem, UML will do something very similar to a loopback mount. The UML block driver operates by calling read and write on this file on the host, analogous to a block driver on the host doing reads and writes on a physical disk. The loopback driver on the host is doing exactly the same thing, except from within the host kernel, rather than from a process, where the UML block driver is. So, in order to provide this file to UML as its root device, we need to tell the UML block driver (the ubd or UML Block Device driver) to attach itself to it. This is done with this option: ubda=~/roots/debian_22 This is the easiest way to initialize a UML block device, and it simply says that the first UML block device is to be attached to the file ~/roots/debian_22. Internally, UML tells the kernel initialization code to use the ubda device as its default root device (this can be overridden by specifying a different device with the root= switch, as the panic mes-sage suggested). I'm going to add one more option to the command line to make the virtual machine's configuration more explicit: mem=128M This makes UML believe it has 128MB of physical memory but does not actually allocate 128MB on the host. Rather, this creates a 128MB sparse file on the host. Being sparse, this file will occupy very little space until data starts being written to it. As the UML instance uses its memory, it will start putting data in the memory backed by this file. As that happens, the host will start allocating memory to hold that data. Since the file is fixed in size, the UML instance is limited to that amount of memory. Its memory consumption will approach this limit asymptotically as it reads file data from its own disks and caches it in its memory. Since the host will be allocating memory for the UML instance dynamically, as needed, the actual consumption will be less than the maximum for a time. This conserves memory, making it possible to run a greater number of not-too-active UML instances than would be possible otherwise. The host memory consumption will, in this case, be at most 128MB. Even if the UML instance is fully using its memory, the host memory consumption may be less, as it may have swapped out some of the UML memory. The UML instance, like any other process that has been swapped out, will be unaware of this and will use its memory as though it is present in the host's memory. The host kernel is responsible for swapping data back in as needed in order to maintain this illusion. The UML instance will also swap if its workload exceeds its physical memory. This is entirely independent from the host swapping the UML instance's memory. Each system will swap when it needs more memory, so if the host is short of memory and the UML instance has plenty, the host will swap and the UML instance won't. Conversely, if the UML instance is short of memory and the host isn't, the UML instance will swap and the host won't. The case where both are swapping at the same time is interesting and can lead to pathological performance problems. [4]
So, the UML command ends up looking like this: ~/linux mem=128M ubda=/home/jdike/roots/debian_22 Figure 2.3 shows the results. Figure 2.3. Output from the first successful boot of UML
This is much more interesting than the last attempt. We get to see the filesystem booting. Note that it's almost exactly the same as it would be if the same filesystem were booted on the host. The underlying virtual machine shows through in only a couple of places. One is when the root filesystem is checked [5]:
/dev/ubd0: clean, 9591/131328 files, 64611/262144 blocks where we see the UML device name, /dev/ubd0, rather than hda1 or sda1 as on a physical machine. The other is when the boot scripts try to synchronize the internal kernel clock with the system's hardware clock: Setting the System Clock using the Hardware Clock as reference... line_ioctl: tty1: unknown ioctl: 0x4b50 hwclock is unable to get I/O port access: the iopl(3) call \ failed. The UML serial line driver is complaining about an ioctl it doesn't implement, and the hwclock program inside UML is complaining that it tried to execute the iopl instruction and failed. These are both symptoms of hwclock trying different methods of accessing the hardware system clock and failing because the device doesn't exist in UML. The UML kernel does have access to a clock, but it is not one that hwclock will recognize. Rather, it is simply a call to the host's gettimeofday. After that, you'll notice that a relatively small number of services are started, but they do include such things as NFS, MySQL, and Apache. All of these run just as they would on a physical machine. This boot process took about 5 seconds on my laptop, demonstrating one of the conveniences of UMLthe ability to quickly create and destroy virtual machines. |