Linux Root Filesystem Primer
From Buici
In order to build a root filesystem for Linux, you must be familiar with the way the system boots. This primer focuses on the part of the boot process as the kernel transitions into executing user-mode applications. See the ApexBootloader for information about early system startup.
The named source files refer to version 2.6.11 of the kernel. Other kernel versions may vary in some details, but the concepts should continue to hold true.
Contents |
The First Program
The kernel makes it's first transition to user-mode in one of two places. When the filesystem is loaded as an initrd, it executes the program /linuxrc from the file init/do_mounts_initrd.c. But this is a fleeting reference. The normal transition to user-mode occurs in init/main.c where the kernel executes a series of programs until one of them doesn't return. These programs include /sbin/init, /etc/init, /bin/init, and /bin/sh. If a program is given to the kernel on the command line as init=filename, then the kernel will first attempt to execute filename before checking for the others.
In order for this first program to execute, several features must be present in the root filesystem. This first step is the most difficult because there tends to be few diagnostics explaining the reason that the kernel cannot start the first program.
Init
On most system, the first program will be init or some relative. This isn't required, but it does provide a few important features. We'll assume you are using busybox init which is a reasonable subset of the sysv init program. The main thing it lacks is support for run-levels.
There are two important things that init does.
- Invokes the initialization scripts.
- Spawns interface shells.
The default inittab for the busybox init looks like this.
::sysinit:/etc/init.d/rcS ::askfirst:-/bin/sh
On system startup, it executes /etc/init.d/rcS to perform the one-time system initializations. This is usually either a C program, a shell script, or a shell script that calls other shell scripts.
The second line opens the default console device and asks the user to press the Enter key to start an interactive shell. Once started, it will usually complain that the shell cannot access the tty and therefore it is disabling job control. This can be fixed by explicitly setting the serial device. Note that the name of the serial device is specific to the kernel driver.
::sysinit:/etc/init.d/rcS ttyAM0::askfirst:-/bin/sh
Shared Libraries
Most systems require shared link libraries. Shared libraries serve two purposes: LGPL compliance in allowing users to change the library without needing access to program sources, and disk/memory space efficiency.
The program ldd will tell you which libraries a given executable requires at runtime. But, this program can only be run against executables for the same target as the host. In other words, it cannot read ARM compiled programs when it is being run on a ix86 host. There is an alternative.
If you don't know which libraries are needed by the init program, use strings and grep. The double backslash escapes to a single backslash which prevents grep from interpreting the period as a regex meta-character.
# strings target/sbin/init | grep \\.so /lib/ld-linux.so.2 libc.so.6
In this case, we appear to need only the dynamic linking loader, and the C library.
Devices
For user applications to have access to all of the physical devices, the /dev directory needs have the necessary files. The necessary and sufficient set of files for a particular system depends on the target hardware. Below are the files
total 0 brw-r--r-- 1 root root 3, 0 May 20 07:43 hda brw-r--r-- 1 root root 3, 1 May 20 07:43 hda1 brw-r--r-- 1 root root 3, 2 Jun 2 13:46 hda2 brw-r--r-- 1 root root 3, 3 Jun 2 13:46 hda3 brw-r--r-- 1 root root 7, 0 May 20 07:43 loop0 brw-r--r-- 1 root root 7, 1 Jun 2 13:47 loop1 crw-r--r-- 1 root root 1, 3 May 20 07:42 null crw-r--r-- 1 root root 5, 0 May 20 07:43 tty crw-r--r-- 1 root root 4, 1 May 20 07:43 tty1 crw-r--r-- 1 root root 1, 5 May 20 07:42 zero
The /dev/console is needed by the linuxrc program. It isn't clear that anything else is required.
Supporting ldconfig
It is a good idea for there to be an /etc/ld.so.conf to keep ldconfig from complaining. It will search /lib and /usr/lib directories automatically. It may be necessary to add the X11 library directory.
Mounts
Many programs require /proc to be mounted. It's a good idea to do this early. Mounting /sys doesn't hurt either. It may be useful to mount /tmp in ram, but this depends on the availability of memory.
The easy way to handle this is to include an /etc/fstab file and call mount -a in the startup scripts. Here is a example fstab.
# /etc/fstab proc /proc proc defaults 0 0 sysfs /sys sysfs defaults 0 0 tmpfs /tmp tmpfs defaults 0 0
Init Scripts
There is no requirement that init scripts be used to initialize the system. It is a common convention, but may be unwarranted excess for an embedded system.
Consider the constraints. Init scripts provide convenient mechanisms for adding features to the system. The order of execution is well defined. On the down side, each script requires at least one block of storage in the filesystem. Each scripts will require an invocation of the shell. It is usually worth spending a couple of disk blocks and a few milliseconds at startup for the sake of init scripts if the system will have any upgradability.
Niceties
Contents Of The Initial Ramdisk
The files that you will need on the ramdisk are the minimum requirements to be able to execute any commands.
- /linuxrc The script that is run to mount the msdos file system (see below).
- /lib/* The dynamic linker and the libraries that the programs need.
- /etc/* The cache used by the dynamic linker (not strictly needed, but does stop it complaining).
- /bin/* A shell interpreter (ash because it is smaller than bash. The mount and losetup programs for handling the DOS disk and setting up the loopback devices.
- /dev/* The devices that will be used. You need /dev/zero for ld-linux.so, /dev/hda* to mount the msdos disk and /dev/loop* for the lopback device.
- /mnt An empty directory to mount the msdos disk on.
The initial ramdisk that I used is listed below, the contents come to about 800kB when the overhead of the filesystem are taken into account.
total 18 drwxr-xr-x 2 root root 1024 Jun 2 13:57 bin drwxr-xr-x 2 root root 1024 Jun 2 13:47 dev drwxr-xr-x 2 root root 1024 May 20 07:43 etc drwxr-xr-x 2 root root 1024 May 27 07:57 lib -rwxr-xr-x 1 root root 964 Jun 3 08:47 linuxrc drwxr-xr-x 2 root root 12288 May 27 08:08 lost+found drwxr-xr-x 2 root root 1024 Jun 2 14:16 mnt ./bin: total 168 -rwxr-xr-x 1 root root 60880 May 27 07:56 ash -rwxr-xr-x 1 root root 5484 May 27 07:56 losetup -rwsr-xr-x 1 root root 28216 May 27 07:56 mount lrwxrwxrwx 1 root root 3 May 27 08:08 sh -> ash ./dev: total 0 brw-r--r-- 1 root root 3, 0 May 20 07:43 hda brw-r--r-- 1 root root 3, 1 May 20 07:43 hda1 brw-r--r-- 1 root root 3, 2 Jun 2 13:46 hda2 brw-r--r-- 1 root root 3, 3 Jun 2 13:46 hda3 brw-r--r-- 1 root root 7, 0 May 20 07:43 loop0 brw-r--r-- 1 root root 7, 1 Jun 2 13:47 loop1 crw-r--r-- 1 root root 1, 3 May 20 07:42 null crw-r--r-- 1 root root 5, 0 May 20 07:43 tty crw-r--r-- 1 root root 4, 1 May 20 07:43 tty1 crw-r--r-- 1 root root 1, 5 May 20 07:42 zero ./etc: total 3 -rw-r--r-- 1 root root 2539 May 20 07:43 ld.so.cache ./lib: total 649 lrwxrwxrwx 1 root root 18 May 27 08:08 ld-linux.so.1 -> ld-linux.so.1.7.14 -rwxr-xr-x 1 root root 21367 May 20 07:44 ld-linux.so.1.7.14 lrwxrwxrwx 1 root root 14 May 27 08:08 libc.so.5 -> libc.so.5.3.12 -rwxr-xr-x 1 root root 583795 May 20 07:44 libc.so.5.3.12 ./lost+found: total 0 ./mnt: total 0
--Elf 17:13, 6 April 2006 (PDT)

