Compiling a Kernel (2.201.2)

Candidates should be able to properly configure a kernel to include or disable specific features of the Linux kernel as necessary. This objective includes compiling and recompiling the Linux kernel as needed, updating and noting changes in a new kernel, creating an initrd image and installing new kernels.

Key files, terms and utilities include:

make config, xconfig, menuconfig, oldconfig, mrproper, zImage, bzImage, modules, modules_install
mkinitrd (both Red hat and Debian based)
/usr/src/linux
/etc/lilo.conf
/boot/grub/menu.lst or /boot/grub/grub.conf

Getting the kernel sources

The kernel sources for the latest Linux kernel can be found at The Linux Kernel Archives .

The generic filename is always in the form linux-kernel-version.tar.gz or linux-kernel-version.tar.bz2. For example, linux-2.4.18.tar.gz is the kernel archive for version “2.4.18”.

A common location to store and unpack kernel sources is /usr/src.

If there isn't enough free space on /usr/src to unpack the sources it is possible to unpack the source in anoher directory. Creating a softlink after unpacking from /usr/src/linux to the linux subdirectory in that location ensures easy access to the source.

The source code will be available as a compressed tar archive. Compression is done using gzip (.gz extention) or bzip2 (.bz2 extention). Decompressing and unpacking can be done using gunzip or bunzip2 followed by unpacking the resulting archive with tar, or directly with tar using the z (.gz) or j (.bz2) options:

# gunzip linux-2.4.18.tar.gz
# tar xf linux-2.4.18.tar
        

or

# tar xjf linux-2.4.18.tar.bz2
	

See manpages for tar, gzip, bzip2 for more information.

Creating a .config file

The first step in compiling a kernel is setting up the kernel configuration which is saved in the .config file. There are more than 500 options for the kernel, such as filesystem, SCSI and networking support. Many of the options list kernel features that can be either compiled directly into the kernel or compiled as modules.

Some selections imply a group of other selections. For example, when you indicate that you wish to include SCSI support, additional options become available for specific SCSI drivers and features.

The results of all of these choices are stored in the kernel configuration file /usr/src/linux/.config, which is a plain text file that lists the options as shell variables.

To begin, set the current working directory to the top of the source tree:

# cd /usr/src/linux
        

There are several ways to set up .config. Although you can do so, you should not edit the file manually. Instead, select one of the three interactive approaches. An additional option is available to construct a default configuration. Each set up is started using make.

make config

Running make config is the most rudimentary of the automated kernel-configuration methods and does not depend on full-screen display capabilities of your terminal. The system sequentially presents you with questions concerning kernel options. This method can, admittedly, get a bit tedious and has the disadvantage that you must answer all the questions before being able to save your .config file and exit. However, it is helpful if you do not have sufficient capability to use one of the menu-based methods. A big drawback is that you can't move back and forward through the various questions. An example session looks like this:

# make config
rm -f include/asm
( cd include ; ln -sf asm-i386 asm)
/bin/sh scripts/Configure arch/i386/config.in
#
# Using defaults found in arch/i386/defconfig
#
*
* Code maturity level options
*
Prompt for development and/or incomplete code/drivers \
    (CONFIG_EXPERIMENTAL) [N/y/?] y
*
* Loadable module support
*
Enable loadable module support (CONFIG_MODULES) [Y/n/?] 
          

make menuconfig

This configuration method is more intuitive and can be used as an alternative to make config. It creates a text-mode-windowed environment where you may use arrow and other keys to configure the kernel. The make menuconfig command is illustrated below in an xterm.

The make menuconfig menu display.

make xconfig

If you are running the X Window System, the make xconfig command presents a GUI menu with radio buttons to make the selections. The figure below illustrates the top-level make xconfig window.

The make xconfig top-level window.

make oldconfig

The make oldconfig command creates a new .config file, using the old .config and options found in the source, only requiring user interaction for options that that were previously not configured (new options), for instance after addition of new functionality, of upgrading to a later kernel release.

When using the .config from a previous kernel release, first copy the .config from the previous kernel-version to the /usr/src/linux/ directory and then run make oldconfig. The .config will be moved to .config.old and a new .config is created. You will only be prompted for the answers to new questions; the questions for which no answer can be found in the .config.old file.

Note

Be sure to make a backup of .config before upgrading the kernel source, because the distribution might contain a default .config file, overwriting your old file.

Note

make xconfig and make menuconfig runs will start with reconstructing the .config file, using the current .config and (new default) options found in the source code. As a result .config will contain at least all the previously set and the new default options after writing the new .config file and exiting the session without any manual changes.

Compiling the kernel

The following sequence of make commands leads to the building of the kernel and to the building and installation of the modules.
  1. make dep
  2. make clean
  3. make zImage/bzImage
  4. make modules
  5. make modules_install

make dep

The dep object examins source files for dependencies. The resulting table of dependencies is stored in a file called .depend. There will be a .depend file in each directory containing source files. The .depend files are automatically included in subsequent make operations.

make clean

The “clean” object removes old output files that may exist from previous kernel builds. These include core files, system map files and others.

make zImage/bzImage

The zImage and bzImage objects both effectively build the kernel. The difference between these two is explained in another paragraph.

After compilation the kernel image can be found in the /usr/src/linux/arch/i386/boot directory (on i386 systems).

make modules

The modules object builds the modules: device drivers and other items that were configured as modules.

make modules_install

The modules_install object installs all previously built modules under /lib/modules/kernel-version The kernel-version directory will be created if non-exsistent.

Installing the new kernel

After the new kernel has been compiled, the system can be configured to boot it.

The first step is to put a copy of the new bzImage on the boot partition. The name of the kernel file should preferably contain the kernel-version number, for example: vmlinuz-2.4.10:

# cp /usr/src/linux/arch/i386/boot/bzImage /boot/vmlinuz-2.4.10
        

The currently available kernel versions can be found in the directory /boot/ as shown below:

# ls -l /boot
-rw-r--r--    1 root     root       426824 Mar 23  2001 vmlinuz-2.2.18pre21
-rw-r--r--    1 root     root       739881 Sep 27 10:42 vmlinuz-2.4.10
-rw-r--r--    1 root     root       728156 Sep 21 15:24 vmlinuz-2.4.9
        

Next, configure the bootmanager to contain the new kernel.

Lilo and version-specific names

lilo.conf could look like this:

# Boot up this one by default.
default=vmlinuz-2410

image=/boot/vmlinuz-2.4.10
        label=linux-2410
        read-only
image=/boot/vmlinuz-2.4.9
        label=linux-249
        read-only
image=/boot/vmlinuz-2.2.18pre21
        label=linux-2218
        read-only
          

Run lilo to add the new kernel and reboot to activate the new kernel.

Lilo and fixed names

Another way to enable the new kernel is by creating symbolic links in the root directory to the kernel images in /boot. It is a good practice to have at least two kernels available: the kernel that has been used so far (and which is working properly) and the newly compiled one (which might not work since it hasn't been tested yet). One is called vmlinuz and the other is called vmlinuz.old. Like this the labels in /etc/lilo.conf don't need to be changed after installation of a new kernel.

First point vmlinuz.old to the latest known-working kernel image (in our example: /boot/vmlinuz-2.4.9) and point vmlinuz to the new kernel image (in our example: /boot/vmlinuz-2.4.10):

# ls -l vmlinuz*
lrwxrwxrwx 1 root root 19 Sep 19 17:33 vmlinuz -> /boot/vmlinuz-2.4.9
lrwxrwxrwx 1 root root 25 Sep 19 17:33 vmlinuz.old -> /boot/vmlinuz-2.2.18pre21
# rm /vmlinuz.old
# ln -s /boot/vmlinuz-2.4.9 /vmlinuz.old
# rm /vmlinuz
# ln -s /boot/vmlinuz-2.4.10 /vmlinuz    
          

lilo.conf could look like this:

# Boot up this one by default.
default=Linux

image=/vmlinuz
        label=Linux
        read-only
image=/vmlinuz.old
        label=LinuxOLD
        read-only
          

Run lilo to add the new kernel and reboot to activate.

# lilo
Added Linux *
Added LinuxOLD
          

If the new kernel doesn't work, reboot again, press SHIFT to have the LILO boot loader activate the boot menu and select the last known working kernel to boot.

Note

Don't forget to run lilo after changing lilo.conf, otherwise the new kernel can't be booted because lilo.conf is not consulted at boot time.

Grub bootmanager

Just like with Lilo both version specific and fixed kernel image names kan be used with Grub.

A kernel configuration entry in /boot/grub/menu.lst (or /boot/grub/grub.conf) could look something like:

title           GNU/Linux, kernel 2.6.8
root            (hd0,0)
kernel          /boot/vmlinuz-2.6.8 root=/dev/hda1 ro
initrd          /boot/initrd.img-2.6.8
savedefault
boot
          

mkinitrd

The list of key files, terms and utilities also mentions mkinitrd. The following text can be found on The Linux Head Quarters :

Using the initial RAM disk (initrd)

initrd adds the capability to load a RAM disk by the boot loader. This RAM disk can then be mounted as the root filesystem and programs can be run from it. Afterwards, a new root filesystem can be mounted from a different device. The previous root (from initrd) is then either moved to the directory /initrd or it is unmounted.

initrd is mainly designed to allow system startup to occur in two phases, where the kernel comes up with a minimum set of compiled-in drivers, and where additional modules are loaded from initrd.

Operation

When using initrd, the system boots as follows:

  1. the boot loader loads the kernel and the initial RAM disk

  2. the kernel converts initrd into a “normal” RAM disk and frees the memory used by initrd.

  3. initrd is mounted read-write as root

  4. linuxrc is executed (this can be any valid executable, including shell scripts; it is run with uid 0 and can do basically everything init can do)

  5. when linuxrc terminates, the “real” root filesystem is mounted

  6. if a directory /initrd exists, the initrd is moved there, otherwise, initrd is unmounted

  7. the usual boot sequence (e.g. invocation of /sbin/init ) is performed on the root filesystem

Note that moving initrd from / to /initrd does not involve unmounting it. It is therefore possible to leave processes running on initrd (or leave filesystems mounted, but see below) during that procedure. However, if /initrd doesn't exist, initrd can only be unmounted if it is not used by anything. If it can't be unmounted, it will stay in memory.

Also note that filesystems mounted under initrd continue to be accessible, but their /proc/mounts entries are not updated. Also, if /initrd doesn't exist, initrd can't be unmounted and will “disappear” and take those filesystems with it, thereby preventing them from being re-mounted. It is therefore strongly suggested to generally unmount all filesystems (except of course for the root filesystem, but including /proc) before switching from initrd to the “normal” root filesystem.

In order to de-allocate the memory used for the initial RAM disk, you have to execute freeramdisk after unmounting /initrd.

initrd adds the following new options to the boot command line options:

initrd= (LOADLIN only)

Loads the specified file as the initial RAM disk. When using LILO, you have to specify the RAM disk image file in /etc/lilo.conf, using the INITRD configuration variable.

noinitrd

initrd data is preserved but it is not converted to a RAM disk and the “normal” root filesystem is mounted. initrd data can be read from /dev/initrd. Note that the data in initrd can have any structure in this case and doesn't necessarily have to be a filesystem image. This option is used mainly for debugging.

Note that /dev/initrd is read-only and that it can only be used once. As soon as the last process has closed it, all data is freed and /dev/initrd can't be opened any longer.

root=/dev/ram

initrd is mounted as root, and /linuxrc is started. If no /linuxrc exists, the normal boot procedure is followed, with the RAM disk still mounted as root. This option is mainly useful when booting from a floppy disk. Compared to directly mounting an on-disk filesystem, the intermediate step of going via initrd adds a little speed advantage and it allows the use of a compressed file system. Also, together with LOADLIN you may load the RAM disk directly from CDrom or disk, hence having a floppy-less boot from CD, e.g.: E:\loadlin E:\bzImage root=/dev/ram initrd=E:\rdimage

Installation

First, the “normal” root filesystem has to be prepared as follows:

# mknod /dev/initrd b 0 250 
# chmod 400 /dev/initrd
# mkdir /initrd
        

If the root filesystem is created during the boot procedure (i.e. if you're creating an install floppy), the root filesystem creation procedure should perform these operations.

Note that neither /dev/initrd nor /initrd are strictly required for correct operation of initrd, but it is a lot easier to experiment with initrd if you have them, and you may also want to use /initrd to pass data to the “real” system.

Second, the kernel has to be compiled with RAM disk support and with support for the initial RAM disk enabled. Also, all components needed to execute programs from initrd (e.g. executable format and filesystem) must be compiled into the kernel).

Third, you have to create the RAM disk image. This is done by creating a filesystem on a block device and then by copying files to it as needed. With recent kernels, at least three types of devices are suitable for that:

a floppy disk (works everywhere but it's painfully slow)
a RAM disk (fast, but allocates physical memory)
a loopback device (the most elegant solution)

We'll describe the RAM disk method:

  1. make sure you have a RAM disk device /dev/ram (block, major 1, minor 0)

  2. create an empty filesystem of the appropriate size, e.g.

    # mke2fs -m0 /dev/ram 300   
    (if space is critical, you may want to use the Minix FS instead of Ext2)
                  

  3. mount the filesystem on an appropriate directory, e.g.

    # mount -t ext2 /dev/ram /mnt
                  

  4. create the console device:

    # mkdir /mnt/dev
    # mknod /mnt/dev/tty1 c 4 1
                  

  5. copy all the files that are needed to properly use the initrd environment. Don't forget the most important file, /linuxrc Note that /linuxrc must be given execute permission.

  6. unmount the RAM disk

    # umount /dev/ram
                  

  7. copy the image to a file

    # dd if=/dev/ram bs=1k count=300 of=/boot/initrd
                  

  8. deallocate the RAM disk

    # freeramdisk /dev/ram
                  

For experimenting with initrd, you may want to take a rescue floppy (e.g. rescue.gz from Slackware) and only add a symbolic link from /linuxrc to /bin/sh, e.g.

# gunzip /dev/ram
# mount -t minix /dev/ram /mnt
# ln -s /bin/sh /mnt/linuxrc
# umount /dev/ram
# dd if=/dev/ram bs=1k count=1440 of=/boot/initrd
# freeramdisk /dev/ram
          

Finally, you have to boot the kernel and load initrd . With LOADLIN, you simply execute

LOADLIN initrd=
e.g. LOADLIN C:\LINUX\VMLINUZ initrd=C:\LINUX\INITRD
          

With LILO, you add the option INITRD= to either the global section or to the section of the respective kernel in /etc/lilo.conf, e.g.

image = /vmlinuz
initrd = /boot/initrd
          

and run /sbin/lilo

Now you can boot and enjoy using initrd.

Setting the root device

By default, the standard settings in the kernel are used for the root device, i.e. the default compiled in or set with rdev , or what was passed with root=xxx on the command line, or, with LILO, what was specified in /etc/lilo.conf It is also possible to use initrd with an NFS-mounted root; you have to use the nfs_root_name and nfs_root_addrs boot options for this.

It is also possible to change the root device from within the initrd environment. In order to do so, /proc has to be mounted. Then, the following files are available:

/proc/sys/kernel/real-root-dev
/proc/sys/kernel/nfs-root-name
/proc/sys/kernel/nfs-root-addrs
          

real-root-dev can be changed by writing the number of the new root FS device to it, e.g.

# echo 0x301 >/proc/sys/kernel/real-root-dev
          

for /dev/hda1. When using an NFS-mounted root, nfs-root-name and nfs-root-addrs have to be set accordingly and then real-root-dev has to be set to 0xff, e.g.

# echo /var/nfsroot >/proc/sys/kernel/nfs-root-name
# echo 193.8.232.2:193.8.232.7::255.255.255.0:idefix \
   >/proc/sys/kernel/nfs-root-addrs
# echo 255 >/proc/sys/kernel/real-root-dev
          

If the root device is set to the RAM disk, the root filesystem is not moved to /initrd, but the boot procedure is simply continued by starting init on the initial RAM disk.

Usage scenarios

The main motivation for implementing initrd was to allow for modular kernel configuration at system installation. The procedure would work as follows:

  1. systems boots from floppy or other media with a minimal kernel (e.g. support for RAM disks, initrd, a.out, and the ext2 FS) and loads initrd

  2. /linuxrc determines what is needed to (1) mount the “real” root FS (i.e. device type, device drivers, filesystem) and (2) the distribution media (e.g. CD-ROM, network, tape, ...). This can be done by asking the user, by auto-probing, or by using a hybrid approach.

  3. /linuxrc loads the necessary modules

  4. /linuxrc creates and populates the root file system (this doesn't have to be a very usable system yet)

  5. /linuxrc unmounts the root filesystem and possibly any other filesystems it has mounted, sets /proc/sys/kernel/..., and terminates

  6. the root filesystem is mounted

  7. now that we're sure that the filesystem is accessible and intact, the boot loader can be installed

  8. the boot loader is configured to load an initrd with the set of modules that was used to bring up the system (e.g. /initrd can be modified, then unmounted, and finally, the image is written from /dev/ram to a file)

  9. now the system is bootable and additional installation tasks can be performed

The key role of initrd here is to re-use the configuration data during normal system operation without requiring the use of a bloated “generic” kernel or re-compilation or re-linking of the kernel.

A second scenario is for installations where Linux runs on systems with different hardware configurations in a single administrative domain. In such cases, it is desirable to generate only a small set of kernels (ideally only one) and to keep the system-specific part of configuration information as small as possible. In this case, a common initrd could be generated with all the necessary modules. Then, only /linuxrc or a file read by it would have to be different.

A third scenario might result in more convenient recovery disks, because information like the location of the root FS partition doesn't have to be provided at boot time, and the system loaded from initrd can use a user-friendly dialog and can also perform some sanity checks (or even some form of auto-detection).

Last but not least, CDrom distributors may use it for better installation from CD, either using a LILO boot floppy and bootstrapping a bigger ramdisk via initrd from CD, or using LOADLIN to directly load the ramdisk from CD without need of floppies.

Since initrd is a fairly generic mechanism, it is likely that additional uses will be found.

Copyright Snow B.V. The Netherlands