System initialization is yet anotherparticularity of Unix systems. As explained in Chapter 2, the kernel's lastinitialization action is to start the initprogram. This program is in charge of finalizing system startup byspawning various applications and starting some key softwarecomponents. In most Linux systems, init mimicsSystem V init and is configured much the sameway. In embedded Linux systems, the flexibility of System Vinit is overkill since such systems are rarelyrun as multiuser systems.
There is no actual requirement for you tohave a standard init program, such as System Vinit, on your root filesystem. The kernel itselfdoesn't really care. All it needs is an applicationit can start once it's done initializing the system.For instance, you can add aninit=PATH_TO_YOUR_INITboot parameter to tell the kernel to use yourinit, which could be your main application.There are, however, drawbacks to this approach, since yourapplication will be the one and only application the kernel everstarts. Your application would then be responsible for starting otherapplications on the system. Furthermore, if your applicationunexpectedly dies, its exit will cause a kernel panic followed by asystem reboot; as would an unexpected exit of System Vinit. Though this may be the desired behavior insome cases, in most cases, the system is most likely rendereduseless. For these reasons, it is usually much safer and useful toactually have a real init on your rootfilesystem.
In the following subsections, I will cover the standardinit package found in most Linux distributions,the BusyBox init, and Minit, a miniatureinit provided by the author of embutils and dietlibc.
As with other issues in Unix, init is a broadsubject. There are quite a few documents that discuss Linuxinit at length. Chapter 5 of RunningLinux describes the mainstream workstation and serverinit setups. Alessandro Rubini wrote a veryinteresting piece about init that goes into thenuances of the various initialization schemes. His article isavailable at http://www.linux.it/kerneldocs/init/.
6.8.1 Standard System V init
The standard initpackage found in most Linux distributions is written by Miquel vanSoorenburg and is available at ftp://ftp.cistron.nl/pub/people/miquels/sysvinit/.By using this package, you get the same flexibility to configure yourtarget's startup as you would in configuring thestartup of a workstation or a server. However, the extrafunctionality and flexibility requires additional space. Also, itrequires that you keep track of the development of yet anothersoftware package. The package includes the following commands:halt, init,killall5, last,mesg, runlevel,shutdown, sulogin,utmpdump, and wall.
The package can be cross-compiledeasily. First, download the package and uncompress it into your${PRJROOT}/sysapps directory. For my controlmodule, I used sysvinit Version 2.84. Then, move into thepackage's source directory and build it:
$ cd ${PRJROOT}/sysapps/sysvinit-2.84/src $ make CC=powerpc-linux-gcc
Replace the value of CC to match thecross-compiler for your target. With the package now built, we caninstall it on the target's root filesystem:
$ make BIN_OWNER="$(id -un)" BIN_GROUP="$(id -gn)" \ > ROOT=${PRJROOT}/rootfs install
This command will install all the binaries in thetarget's root filesystem but will fail afterward,since the Makefile tries to install the manpages on the rootfilesystem as well. You can modify the Makefile to avoid this, butyou can also ignore the failure.
The command just shown set theBIN_OWNER and BIN_GROUPvariables to be that of your own current user. By default, theMakefile attempts to install the various components and set theirownership to the root user. Since you aren't loggedin as root, the Makefile would fail. The ownership of the binariesmatters little on the target, since it isn't amultiuser system. If it were, however, you need to log in as root andthen run the install command. Be very careful, in any case, toappropriately set the value of ROOT to point toyour target's root filesystem. Otherwise, you mayend up overwriting your workstation'sinit with a target binary. Alternatively, toavoid having to log in as root, you could still run the installcommand using your normal user privileges and then use thechown command as root to change the privilegeson each file installed. This, however, involves going through theMakefile to find each file installed and its destination.
With initinstalled on your target's root filesystem, you willneed to add the appropriate /etc/inittab fileand fill the /etc/rc.d directory with theappropriate files. In essence, /etc/inittab willdefine the runlevels for your system, and the files in/etc/rc.d will define which services run on eachrunlevel. Table 6-5 listsinit's seven runlevels andtheir typical use in a workstation and server distribution.
Runlevel |
Description |
---|---|
0 |
System is halted |
1 |
Only one user on system, no need for login |
2 |
Multiuser mode without NFS, command-line login |
3 |
Full multiuser mode, command-line login |
4 |
Unused |
5 |
X11, graphical user interface login |
6 |
Reboot the system |
Each runlevel corresponds to a certain set of applications. Whenentering runlevel 5 on a workstation, for example,init starts X11 and the user is prompted toenter his username and password using a graphicallogin. When switching between runlevels, theservices started in the previous runlevel are shut down and theservices of the new runlevel are started. In this scheme, runlevels 0and 6 have a special meaning. Particularly, they are used forstopping the system safely. This may involve, for example, unmountingall the filesystems except the root filesystem and remounting theroot filesystem read-only so that no filesystem corruption occurs.
On most workstations, the default runlevel at system startup is 5.For an embedded system, it can be set to 1, if no access control isnecessary. The system's runlevel can be changedafter system startup either using init ortelinit, which is a symbolic link toinit. In both cases, the newly issuedinit command communicates with the originalinit through the/dev/initctl fifo. To this end, we need tocreate a corresponding entry in our target's rootfilesystem:
$ mknod -m 600 ${PRJROOT}/rootfs/dev/initctl p
For more information on the format of/etc/inittab and the files found in/etc/rc.d, refer to the resources providedabove.
6.8.2 BusyBox init
Amongthe commands it supports by default, BusyBox providesinit-like capabilities. As with the originalmainstream init, BusyBox can handle thesystem's startup. BusyBox initis particularly well adapted to embedded systems, because it providesmost of the init functionality an embeddedsystem typically needs without dragging the weight of the extrafeatures found in System V init. Also, becauseBusyBox is a single package, there is no need to keep track of anadditional software package when developing or maintaining yoursystem. There are cases, however, where BusyBoxinit may not be sufficient for your system.BusyBox init, for example, does not providerunlevel support.
Since I already described how to obtain, configure, and buildBusyBox, I will limit this discussion to the setup of theinit configuration files.
Because/sbin/init is a symbolic link to/bin/busybox, BusyBox is the first applicationto run on the target system. BusyBox identifies that the commandbeing invoked is init and immediately jumps tothe init routine.
The init routine of BusyBox carries out thefollowing main tasks in order:
-
Sets up signal handlers for init.
-
Initializes the console(s).
-
Parses the inittab file, /etc/inittab.
-
Runs the system initialization script./etc/init.d/rcS is the default for BusyBox.
-
Runs all the inittab commands that block (action type:wait).
-
Runs all the inittab commands that run only once (action type:once).
Once it has done this, the init routine loopsforever carrying out the following tasks:
-
Runs all the inittab commands that have to be respawned (action type:respawn).
-
Runs all the inittab commands that have to be asked for first (actiontype: askfirst).
During console initialization, BusyBox determines whether the systemwas configured to run the console on a serial port (by passingconsole=ttyS0 as a kernel boot parameter, forinstance). If so, BusyBox versions prior to 0.60.4 used to disableall virtual terminals. Since 0.60.4, however, BusyBox continuesthrough its initialization without disabling virtual terminals. If infact there are no virtual terminals, its attempts to start shells onsome virtual terminals later will fail anyway, so there is no need todisable virtual terminals outright.
Afterhaving initialized the console, BusyBox checks for the existence ofan /etc/inittab file. If no such file exists,BusyBox uses a default inittab configuration. Mainly, it sets updefault actions for system reboot, system halt, andinit restart. Also, it sets up actions to startshells on the first four virtual consoles,/dev/tty1 through/dev/tty4. BusyBox will complain if youhaven't created these device entries.
If an /etc/inittab file is found, it is parsedand the commands it contains are recorded inside internal structuresto be carried out at the appropriate time. The format of the inittabfile as recognized by BusyBox is well explained in the documentationincluded in the BusyBox package. The documentation provided in theBusyBox package includes an elaborate example inittab file.
Each line in the inittab file follows this format:
id:runlevel:action:process
Althoughthis format resembles that of traditional System Vinit, take note that the meaning ofid is different in BusyBoxinit. Mainly, the idis used to specify the controlling tty for the process to be started.You can safely leave this entry empty if the process to be startedisn't an interactive shell. Interactive shells, suchas BusyBox's sh, should alwayshave a controlling tty. BusyBox'ssh will actually complain if it has nocontrolling tty. BusyBox completely ignores therunlevel field, so you can leave it blank.The process field specifies the path ofthe program to run, along with its command-line options. Theaction field is one of eight recognizedactions to be applied to process asdescribed in Table 6-6.
Action |
Effect |
---|---|
sysinit |
Provide init with the path to the initializationscript. |
respawn |
Restart the process every time it terminates. |
askfirst |
Similar to respawn, but is mainly useful forreducing the number of terminal applications running on the system.It prompts init to display"Please press Enter to activate thisconsole." at the console and wait for the user topress Enter before restarting the process. |
wait |
Tell init that it has to wait for the process tocomplete before continuing. |
once |
Run process only once without waiting for them. |
ctrlaltdel |
Run process when the Ctrl-Alt-Delete key combination is pressed. |
shutdown |
Run process when the system is shutting down. |
restart |
Run process when init restarts. Usually, theprocess to be run here is init itself. |
The following is a simple inittab file for my control module:
::sysinit:/etc/init.d/rcS ::respawn:/sbin/getty 115200 ttyS0 ::respawn:/control-module/bin/init ::restart:/sbin/init ::shutdown:/bin/umount -a -r
This inittab file does the following:
-
Sets /etc/init.d/rcS as the systeminitialization file.
-
Starts a login session on the serial port at 115200 bps.
-
Starts the control module's custom softwareinitialization script.
-
Sets /sbin/init as the program to execute ifinit restarts.
-
Tells init to run theumount command to unmount all filesystems it canat system shutdown and set the others as read-only to preserve thefilesystems.
The id is left blank in this case, becauseit doesn't matter to the normal operation of thecommands. runlevel is also left blank,since it's completely ignored by BusyBox.
As shown earlier, however, none of these actions will take placeuntil init runs the system initializationscript. This script can be quite elaborate and can actually callother scripts. Use this script to set all the basic settings andinitialize the various components of the system that need specialhandling. Particularly, this is a good place to:
-
Remount the root filesystem in read-write mode.
-
Mount additional filesystems.
-
Initialize and start networking interfaces.
-
Start system daemons.
Here is the initialization script for my control module:
#!/bin/sh # Remount the root filesystem in read-write (requires /etc/fstab) mount -n -o remount,rw / # Mount /proc filesystem mount /proc # Start the network interface /sbin/ifconfig eth0 192.168.172.10
The above initialization script depends on the existence of an/etc/fstab file in the target'sroot filesystem. I will not discuss the content and use of this fileas it is already discussed in depth in RunningLinux. Nevertheless, here's the/etc/fstab file I use for my control moduleduring development:
# /etc/fstab # device directory type options # /dev/nfs / nfs defaults none /proc proc defaults
In this case, I mount the target's root filesystemon NFS to simplify development. We will discuss filesystem types inChapter 8 and NFS mounting in Chapter 9.
6.8.3 Minit
Minit is partof the miniaturized tools developed by Felix von Leitner, such asdiet libc and embutils. Minit is available from http://www.fefe.de/minit/.[8] As with the other tools distributed byFelix, Minit requires a properly configured diet libc.
[8] Aswith the other tools available from fefe.de, thelast slash ("/") isimportant.
Minit's initialization procedure is a completedeparture from the traditional System V init.Instead of using a /etc/inittab, for instance,Minit relies on the existence of a properly built/etc/minit directory. Firdtjof Busse wrote adescription of how Minit operates at http://www.fbunet.de/minit.shtml. Firdtjofalso provides pointers to example /etc/minitdirectories.
Unfortunately,as of Version 0.8, Minit is not yet as mature as the other toolsprovided by Felix. Its Makefile, for instance, is unable to deal withinstalling the various components in a root filesystem other than thehost's own. For the time being, Minit is notappropriate for an embedded system, but it may be worth consideringsometime in the near future.