As soon as the Linux kernel has been booted and the root file system (/) mounted, programs can be run and further kernel modules can be integrated to provide additional functions. To mount the root file system, certain conditions must be met. The kernel needs the corresponding drivers to access the device on which the root file system is located (especially SCSI drivers). The kernel must also contain the code needed to read the file system (ext2, reiserfs, romfs, etc.). If the root file system is already encrypted, a password is needed to mount the file system.
For the problem of SCSI drivers, a number of different solutions are possible. The kernel could contain all imaginable drivers, but this might be a problem because different drivers could conflict with each other. Also, the kernel would become very large because of this. Another possibility is to provide different kernels, each one containing just one or a few SCSI drivers. This method has the problem that a large number of different kernels are required, a problem then increased by the differently optimized kernels (Athlon optimization, SMP). The idea of loading the SCSI driver as a module leads to the general problem resolved by the concept of an initial RAM disk: running user space programs even before the root file system is mounted.
The initial RAM disk (also called initdisk or initrd) solves precisely the problems described above. The Linux kernel provides an option of having a small file system loaded to a RAM disk and running programs there before the actual root file system is mounted. The loading of initrd is handled by the boot loader (GRUB, LILO, etc.). Boot loaders only need BIOS routines to load data from the boot medium. If the boot loader is able to load the kernel, it can also load the initial RAM disk. Special drivers are not required.
The boot loader loads the kernel and the initrd to memory and starts the kernel. The boot loader informs the kernel that an initrd exists and where it is located in memory. If the initrd was compressed (which is typically the case), the kernel decompresses the initrd and mounts it as a temporary root file system. A program called linuxrc is then started. This program can now do all the things necessary to mount the proper root file system.
As soon as linuxrc finishes, the temporary initrd is unmounted and the boot process continues as normal with the mount of the proper root file system. Mounting the initrd and running linuxrc can be seen as a short interlude during a normal boot process.
The kernel tries to remount initrd to the /initrd immediately after the actual root partition is booted. If this fails because the mount point /initrd does not exist, for example, the kernel attempts to unmount initrd. If this does not work, the system is fully functional, but the memory taken up by initrd cannot be unlocked, so will no longer be available.
The only requirements for the program linuxrc in the initrd are: it must have the special name linuxrc, it must be located in the root directory of the initrd, and it must be executable by the kernel. This means that linuxrc may be dynamically linked. In this case, the shared libraries in /lib must be completely available in initrd. linuxrc can also be a shell script. For this to work, a shell must exist in /bin. In short, initrd must contain a minimal Linux system that allows the program linuxrc to be run. When SUSE LINUX is installed, a statically-linked linuxrc is used to keep initrd as small as possible. linuxrc is run with root permissions.
As soon as linuxrc terminates, initrd is unmounted and discarded, the boot process carries on as normal, and the kernel mounts the real file system. What is mounted as the root file system can be influenced by linuxrc. It just needs to mount the /proc file system and write the value of the real root file system in numerical form to /proc/sys/kernel/real-root-dev.
Most boot loaders, including GRUB, LILO, and syslinux, can handle initrd. Give individual boot loaders instructions for accessing initrd as follows:
As the loading address of the initrd is written to the loaded kernel image, the initrd command must follow the kernel command.
Further parameters can be appended to the line.
The initrd has been used for some time for the installation: the user can load modules and make the entries necessary for installation. linuxrc then starts YaST, which carries out the installation. When YaST has finished, it tells linuxrc where the root file system of the newly installed system is located. linuxrc writes this value to /proc and reboots the system. Then YaST starts again and installs the remaining packages in the system.
In the past, YaST offered more than forty kernels for installing in the system. The main difference between the kernels was that each of them contained a specific SCSI driver. This was necessary to be able to mount the root file system after booting. Further drivers could then be loaded afterwards as modules. As optimized kernels are now available, this concept is no longer feasible — by now, over one hundred kernel images would be needed.
This is why an initrd is now used to start the system normally. The way it is used is similar to the method for installation. The linuxrc used here, however, is simply a shell script with the task of loading a given module. Typically, this is just one single module — the very SCSI driver needed to access the root file system.
An initrd is created by means of the script mkinitrd (previously mk_initrd). In SUSE LINUX, the modules to load are specified by the variable INITRD_MODULES in /etc/sysconfig/kernel. After installation, this variable is automatically set to the correct value (the installation linuxrc saves which modules were loaded). The modules are loaded in exactly the order in which they appear in INITRD_MODULES. This is especially important if several SCSI drivers are used, because otherwise the names of the hard disks would change. Strictly speaking, it would be sufficient just to load those drivers needed to access the root file system. However, all SCSI drivers needed for installation are loaded by means of initrd because later loading could be problematic.
As the initrd is loaded by the boot loader in the same way as the kernel itself (in its map file, LILO records the location of the files), the boot loader LILO must be updated every time the initrd is modified. This is not necessary for GRUB.
A custom kernel can often lead to the following problem: out of habit, the SCSI driver is hard-linked to the kernel, but the existing initrd remains unchanged. When you boot, the following occurs: The kernel already contains the SCSI driver, so the hardware is detected. initrd, however, now tries to load the driver as a module. With some SCSI drivers, especially with aic7xxx, this leads to the system locking. Strictly speaking, this is a kernel error. An already existing driver should not be allowed to be loaded again as a module. The problem is already known from another context, however (serial drivers).
There are several solutions to the problem. Configure the driver as a module (then it will be correctly loaded in the initrd. Alternatively, remove the entry for initrd from the file /etc/grub/menu.lst or /etc/lilo.conf, depending on your boot loader. An equivalent to the latter solution is to remove the variable INITRD_MODULES then run mkinitrd, which then realizes that no initrd is needed.
It is quite possible in the future that an initrd will be used for many more and much more sophisticated things than loading modules needed to access /.
Root file system on software RAID (linuxrc sets up the md devices)
Root file system on LVM
Root file system is encrypted (linuxrc queries the password)
Root file system on a SCSI hard disk on a PCMCIA adapter
For more information, see /usr/src/linux/Documentation/ramdisk.txt, /usr/src/linux/Documentation/initrd.txt, and the man page for initrd.