Last update:

Overview

This semester we will attempt to work on a graphical desktop image of Ubuntu. In previous semesters we worked with a non-desktop Debian distro of Linux. I still like non-desktops when the going gets real. In my opinion, and in the overwhelming percentage of of large server farms, servers do not run desktops. But Ubuntu seems to be the current clear favorite distro of younger hackers, and it can be installed without desktop, if ever you need to create a server farm.

By the way, here's my current specs:

Preparation for a build

You will need to work as root for some or all of the build. On a production machine, or to save your sanity, you tend not to work as root. Root has all powers, including the ultimate power to kill your machine in the blink of an eye. As in, no recovery. None. No "undo destruction of machine", no "restore to known state". Gone.

Here are the responses to this situation:

Many Linux machines force the use of sudo. That do this by locking out root from logging in. The command "su" changes your user credentials to become root (or any other user in the form "su [username]") after prompting for a password. On a linux machine that has locked out root, "su" does not work. They want you to use "sudo [command]". It will run "[command]" as root and then return to normal user mode.

If you want to run as root on a machine that locks out root, use:

   sudo /bin/bash
Now you are running in a root shell.

Running as root, or preceding these commands with "sudo", update your install:

   apt-get update
   apt-get upgrade
Then install the needed packages to build the kernel. My notes say you will need these packages (mileage may vary):
  apt-get install kernel-package ncurses-dev bzip2

Download the kernel from the Ubuntu/Debian repository:

   cd /usr/src
   apt-get install linux_source
   tar jxf linux-source-3.2.0.tar.bz2
If the directory does not exist, build it first.

How to use a config file:

Before building a kernel, you need to configure the build. This states the options that the kernel will include, will choose whether certain functionality is built-in or placed in a separable module; and what functionality and drivers will be omitted from the build altogether. The config files for a linux build is a hidden file with name ".config" which is placed in the root of the build tree. You unpacked your source into the directory /usr/src/linux-source-3.2.0, so the full pathname of the config file is /usr/src/linux-source-3.2.0/.config.

The class/misc folder in the subversion repo has a configuration file that tries to limit the build to the fewest sources. As it is, on my machine the initial build took 2 hours. Subsequence builds are faster, as only the affected source files are recompiled.

Find the configuration file and copy it to the .config in the appropriate directory. You must then run:

   make oldconfig
This builds a .config using the options indicated by the existing .config. If just place the .config there it won't work and build, it won't work. Somehow it knows and it will just overwrite it with an automatically constructed .config file.

To manipulate the .config file, once you have used oldconfig, use:

   make menuconfig
This is a graphical view that works using the "curses" library, so that it works even without a graphical desktop. You will need ncurses-dev installed for this to work.

Extra credit: If you look at last year's minlin config, I was able to work the build down to about 40 minutes by working the config file, removing elements of the build. The tricky part is if you remove something you need then the kernel doesn't boot. I give students this challenge for extra credit:

1 extra credit point for each 10 minutes you take off the build. Winning config files will be posted for the aid of the class, after which they become the new standard to beat. Place the config file in the appropriate proj directory along with a file config-notes.txt that explains what you did and how much time it saved.

Building the kernel

There are several philosophies of Linux kernel building. A simple, standard kernel build uses the Makefile in the root of the source tree, and follows these steps:

  1. make
  2. make modules (I don't think this is necessary.)
  3. make modules_install
  4. make install
  5. update-initramfs -c -k [kernel revison name]
  6. update-grub
Please look at last year's notes for actual screen captures of the build.

Warm builds use only:

  1. make
  2. make install
  3. update-initramfs -u -k [kernel revison name]

Run make help in the root of the build source for information on the build targets. Plain make builds the kernel (make vmlinux) and the modules (make modules). Modules are each a separate file, with a .ko extension. They are installed by being copied into the special directory /lib/modules/[kernel revision name]. Kernel modules are parts of the kernel that are dynamically loaded as needed.

Note that once you build, as long as you do not do make clean or update the configuration file, you just use mode vmlinux to remake only the kernel. Furthermore, only what has changed will be recompiled and relinked. This should only be a few minutes.

A script of the build is here.

Extra credit: On the first version of this page I recommended make vmlinux instead of make in order to speed up warm builds. This doesn't work as I intended. I am still looking for the faster warm build and I give you this challenge for extra credit:

Is there a target that will speed up warm kernel builds? That is, why doesn't make vmlinux do what we want it do, and how can we fix that? And does it make the build faster and why? Place your answers in the appropriate proj directory in a file named warm-builds.txt.

Booting your kernel

After building and installing the kernel and the modules, you need to build the initial root directory. In case you google for additional help on this, beware that the initial root directory process has changed recently. It now uses a simple cpio into a ramfs, rather than imaging a RAM-disk.

At boot, the boot loader knows only enough about reading from disk so that it can copy the kernel from a file on disk into the memory, and then begin running the kernel (early boot). The kernel itself might rely on modules to provide the code necessary to access the file systems on disk. These modules are files that might reside on the file systems which the kernel cannot yet read.

Linux solves this problem by having a partition on disk in a file format that the boot loader and the initial kernel know how to read. Generally this is ext2, and the partition is later remounted into the runtime file tree at /boot. The kernel is stored in this partition, as well as an archive of the initial file system required by the kernel during early boot. The kernel creates a ramfs and unpacks this archive into the ramfs and mounts this as the initial root directory.

This root serves the kernel during early boot, but is later jettisoned when the real root directory is discovered on disk and mounted over the initial root, called re-rooting or pivoting on root.

A RAM disk is a disk-like storage system that uses RAM as the final destination for the data rather than a magnetic disk. It is preferred for speed, however it is volatile — is goes away on reboot. Sometimes that's a plus, as for with temporary files that need to be erased on reboot anyway.

Linux uses a very intelligent buffer cache to shuttle data blocks to and from the disk. Each buffer in the buffer cache remembers what disk block it mirrors. A read to a disk to can satisfied without going out to the disk if the block has recently been read or written, if the buffer that held that block is still in the cache.

With such a sophisticated buffer cache, it was discovered that a RAM disk can be created more efficiently by stopping the transfer at the buffer cache, and marking the buffer in the cache which carries data to the RAM disk as "permanent". No need to copy an in-RAM buffer in the buffer cache to an in-RAM buffer in the RAM disk. Just sitting permanently in the buffer cache is sufficient to create an in-RAM file system.

This scheme was christened the ramfs.

The process of creating a initial root directory is automated by the update-initramfs script. It creates a file called initrd.img-[kernel revision name] in the /boot directory. You need to run this program or your kernel won't complete boot. If you watch your boot output carefully, you will see that the boot process seemed to go well up until the first file access to the file system. The error messages will be about not finding a root file system, or not recognizing it. That means the initrd was not properly formed or placed.

A single linux install can boot to any of several kernels. You will not be overwriting the old kernel with the new kernel. You will be providing an option at boot to run either the old kernel or the new kernel. Since the modules are in directories with names that correspond to the kernel names, the option will be to run the new kernel and all the new modules or the old kernel and all the old modules.

Kernel names differ because of the various revision numbers, or because you added a local suffix to the name, such as "-burt". (Note hyphen as first character.) You can specify a local suffix as an option in the .config file, using menuconfig.

The boot loader boots the system. There are three boot loaders you might see: lilo, grub and grub2. The Ubuntu we are using installs grub2. Before rebooting, you have to inform grub2 of your new kernel, so it can put in in the boot menu. You also have to modify its behavior a slight bit. By default, grub2 does a quiet boot, and it does not show the boot menu. (Booting with the shift key depressed will show the boot menu, and I think it cancels quiet boot.)

Edit /etc/default/grub as follows:

    #GRUB_HIDDEN_TIMEOUT=0 # comment out this line
    GRUB_CMDLINE_LINUX_DEFAULT="" # set this to the empty string
Then run update-grub. This will include your kernel in the boot menu, and create a new boot that is verbose and which shows the boot menu.

Now you are ready to try your new kernel. Type reboot and your machine will reboot. At the boot menu, select your kernel. Hopefully it will boot properly. Once booted, use uname -r to see the revision number of the kernel you are running. If it is yours, congratulations!