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 UML—its 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 con- structed 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 imageis 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.
Booting UML Successfully 25
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 possi- ble 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.
26 Chapter 2 A Quick Look at UML
The UML instance will also swap if its workload exceeds its physi- cal 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 swap- ping at the same time is interesting and can lead to pathological perfor- mance 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.
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 under- lying virtual machine shows through in only a couple of places. One is when the root filesystem is checked5:
/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.
4. Consider the case where both the host and the UML instance are swapping at the same time. They may both choose the same page to swap out. If the host swaps it out first, then when the UML instance swaps it, the host will need to read it back from disk so that the UML instance can write it to its own swap device. This will cause the page to be read and written a total of three times, when only once was desirable. This will increase the I/O load on the host at a time when it is already under stress. Solutions for this sort of situation are under investigation and will be described in Chapter 10. 5. The fsck message refers to /dev/ubd0 rather than /dev/ubda. Devices
can be specified with either numbers or letters. Using letters is generally fa- vored since it is similar to current practice with other drivers, such as nam- ing IDE disks hda, hdb, and so on. It also makes the use of multiple ubd
devices within UML less confusing. There’s less expectation that ubdb on the command line corresponds to minor number 1 inside the UML instance, as the use of ubd1 does. In fact, ubdb has minor number 16 (to allow for par- titions on ubda). The one case where numbers are needed is when you are plugging a large number of disks into a UML instance. There is no letter equivalent of ubd512, so you’d have to use a number to describe this device.
Booting UML Successfully 27
~/linux/2.6/2.6.10 22849: ./linux mem=128M ubda=/home/jdike/roots/debian_22 Checking for /proc/mm...not found
Checking for the skas3 patch in the host...not found Checking PROT_EXEC mmap in /tmp...OK
Linux version 2.6.11-rc1-mm1 ([email protected]) (gcc version 3.3.2 20031022 (Red Hat Linux 3.3.2-1)) #83 Thu Jan 27 12:16:00 EST 2005
Built 1 zonelists
Kernel command line: mem=128M ubda=/home/jdike/roots/debian_22 root=98:0 PID hash table entries: 1024 (order: 10, 16384 bytes)
Dentry cache hash table entries: 32768 (order: 5, 131072 bytes) Inode-cache hash table entries: 16384 (order: 4, 65536 bytes) Memory: 126720k available
Mount-cache hash table entries: 512 (order: 0, 4096 bytes) Checking for host processor cmov support...Yes
Checking for host processor xmm support...No
Checking that ptrace can change system call numbers...OK Checking syscall emulation patch for ptrace...missing Checking that host ptys support output SIGIO...Yes
Checking that host ptys support SIGIO on close...No, enabling workaround Checking for /dev/anon on the host...Not available (open failed with errno 2) NET: Registered protocol family 16
mconsole (version 2) initialized on /home/jdike/.uml/igpn9r/mconsole VFS: Disk quotas dquot_6.5.1
Dquot-cache hash table entries: 1024 (order 0, 4096 bytes) io scheduler noop registered
io scheduler anticipatory registered io scheduler deadline registered io scheduler cfq registered NET: Registered protocol family 2
IP: routing cache hash table of 512 buckets, 4Kbytes
TCP established hash table entries: 8192 (order: 4, 65536 bytes) TCP bind hash table entries: 8192 (order: 3, 32768 bytes)
TCP: Hash tables configured (established 8192 bind 8192) NET: Registered protocol family 1
NET: Registered protocol family 17 Initialized stdio console driver Console initialized on /dev/tty0
Initializing software serial port version 1 ubda: unknown partition table
VFS: Mounted root (ext2 filesystem) readonly. line_ioctl: tty0: ioctl KDSIGACCEPT called INIT: version 2.78 booting
Activating swap...
Checking root file system...
Parallelizing fsck version 1.18 (11-Nov-1999)
/dev/ubd0: clean, 9591/131328 files, 64611/262144 blocks
Calculating module dependencies... depmod: get_kernel_syms: Function not implemented
done.
Loading modules: cat: /etc/modules: No such file or directory
(continues)
28 Chapter 2 A Quick Look at UML
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 complain-
modprobe: Can’t open dependencies file /lib/modules/2.6.11-rc1-mm1/modules.dep (No such file or directory)
Checking all file systems...
Parallelizing fsck version 1.18 (11-Nov-1999) Setting kernel variables.
Mounting local filesystems...
mount: devpts already mounted on /dev/pts none on /tmp type tmpfs (rw)
Setting up IP spoofing protection: rp_filter. Configuring network interfaces: done.
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. System Clock set. Local time: Thu Jan 27 18:51:28 EST 2005
Cleaning: /tmp /var/lock /var/run.
Initializing random number generator... done. Recovering nvi editor sessions... done. INIT: Entering runlevel: 2
Starting system log daemon: syslogd syslogd: /dev/xconsole: No such file or directory
klogd.
Starting portmap daemon: portmap.
Starting NFS common utilities: statd lockdlockdsvc: Function not implemented .
Starting internet superserver: inetd. Starting MySQL database server: mysqld. Not starting NFS kernel daemon: No exports. Starting OpenBSD Secure Shell server: sshd. Starting web server: apache.
/usr/sbin/apachectl start: httpd started Debian GNU/Linux 2.2 usermode tty0 usermode login:
Looking at a UML from the Inside and Outside 29
ing 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 UML—the ability to quickly create and destroy vir- tual machines.