Chapter 2. System Startup (2.202)

Revision: $Revision: 1.22 $ ($Date: 2007/04/26 12:06:48 $)

This topic has a total weight of 5 points and contains the following 2 objectives:

Objective 2.202.1 Customizing system startup and boot processes (2 points)

This topic includes being able to edit the appropriate system startup scripts to customize standard system run levels and boot processes, interacting with run levels. Some knowledge about the LSB (the Linux Standard Base Specification) is required. For historical reasons a section about creating custom initrd images is included.

Objective 2.202.2 System recovery (3 points)

This topic includes being able to properly configure and navigate the standard Linux filesystem, configuring and mounting various filesystem types, and manipulating filesystems to adjust for disk space requirements or device additions.

Customizing system startup and boot processes (2.202.1)

This topic includes being able to edit the appropriate system startup scripts to customize standard system run levels and boot processes, interacting with run levels. Some knowledge about the LSB (the Linux Standard Base Specification) is required. For historical reasons a section about creating custom initrd images is included.

Key files, terms and utilities include:

insserv
chkconfig
startproc
checkproc
killproc
startpar
/etc/insserv.conf
/etc/insserv.conf.d/
/etc/init.d/
/etc/inittab
/etc/rc.d
mkinitrd Described in the section called “mkinitrd”

The Linux Boot process

A description of the boot process can also be found at Vanderbilt University.

The Linux boot process can be logically divided into six parts. They are as follows:

  1. Kernel loader loading, setup, and execution (bootsect.s)

    In this step the file bootsect.s is loaded into memory by the BIOS. bootsect.s then sets up a few parameters and loads the rest of the kernel into memory.

  2. Parameter setup and switch to 32-bit mode (boot.s)

    After the kernel has been loaded, boot.s takes over. It sets up a temporary IDT and GDT (explained later on) and handles the switch to 32-bit mode.

    Detailed information on IDT, GDT and LDT can be found on sandpile.org - The world's leading source for pure technical x86 processor information.

  3. Kernel decompression (compressed/head.s)

    The kernel is stored in a compressed format. This head.s (since there is another head.s) decompresses the kernel.

  4. Kernel setup (head.s)

    After the kernel is decompressed, head.s (the second one) takes over. The real GDT and IDT are created, as is a basic memory-paging table.

  5. Kernel and memory initialization (main.c)

    This step is the most complex. The kernel now has control and sets up all remaining parameters and initializes everything remaining. Virtual memory is setup completely and the first processes are created.

  6. Init process creation (main.c)

    In the final step of booting, the Init process is created.

Kernel Loader (linux/arch/i386/boot/bootsect.s)

When the computer is first turned on, BIOS loads the boot sector of the boot disk into memory at location 0x7C00. This first sector corresponds to the bootsect.s file. The BIOS will only copy 512 bytes, so the kernel loader must be small. The code that is loaded by the BIOS must be able to load the remaining portions of the operating system and pass control onto the next file.

The first thing that bootsect.s does when it is loaded is to move itself to the memory location 0x9000. This is to avoid any possible conflicts in memory. The code then jumps to the new copy located at 0x9000. After this, an area in memory is set aside (0x4000-12) for a new disk parameter table. To make it so that more than one sector can be read from the disk at a time, we will try to find the largest number of sectors that can be read at a time. This will help speed reads from the disk when we begin loading the rest of the kernel.

Before this is done, setup.s is loaded into memory in the memory space above bootsect.s, 0x9020. This allows setup.s to be jumped to after the kernel has been loaded. Now the disk parameter table is created. Basically, the code tries to read 36 sectors, if that fails it tries 18, 15, then if all else fails it uses 9 as the default.

If at any point there is an error, little can be done. In most cases, bootsect.s will just keep trying to do what it was doing when the error occurred. Usually this will end in an unbroken loop that can only be resolved by rebooting by hand.

At last we are ready to copy the kernel into memory. bootsect.s goes into a loop that reads the first 508Kb from the disk and places it into memory starting at 0x10000. After the kernel is loaded into RAM, bootsect.s jumps to 0x9020, where setup.s is loaded.

Parameter Setup (linux/arch/i386/boot/setup.s)

setup.s makes sure that all hardware information has been collected and gathers more information if necessary. It first verifies that it is loaded at 0x9020. After this is verified, setup.s does the following:

  1. Gets main memory size

  2. Sets keyboard repeat rate to the maximum

  3. Retrieves video card information

  4. Collects monitor information for the terminal to use

  5. Gets information about the first and possibly second hard drive using BIOS

  6. Looks to see if there is a mouse (a pointing device) attached to the system

All of the information that setup.s collects is stored for later use by device drivers and other areas of the system. Like bootsect.s, if an error occurs little can be done. Most errors are “handled” by an infinite loop that has to be reset manually.

The next step in the booting process needs to use virtual memory. This can only be used on a x86 by switching from real mode to protected mode. After all information has been gathered by setup.s, it does a few more housekeeping chores to get ready for the switch to 32-bit mode.

First, all interrupts are disabled. Once the system is in 32-bit mode, no more BIOS calls can be made. The area of memory at 0x1000 is where the BIOS handlers were loaded when the system came up. We no longer need these, so to get the compressed kernel out of the way, setup.s moves the kernel from 0x10000 to 0x1000. This provides room for a temporary IDT (Interrupt Descriptor Table) and GDT (Global Descriptor Table). The GDT is only setup to have the system in memory. All paging is disabled, so that described memory locations correspond to actual memory addresses. At this point, extended (or high) memory is enabled.

Setup also resets any present coprocessor and reconfigures the 8259 Programmable Interrupt Controller. All that remains now is for the protected bit mask to be set, and the processor is in 32-bit mode. After the switch has been made, setup.s lets processing continue at /compressed/head.s to uncompress the kernel.

Kernel Decompression (linux/arch/i386/boot/compressed/head.s )

This first head.s uncompresses the kernel into memory. The kernel is gzip-compressed to make sure that it can fit into the 508Kb that bootsect.s will load. When the kernel is compiled, bootsect.s, head.s , and /compressed/head.s are not compressed and are appended to the front of the compressed kernel. They are the only three files that must remain uncompressed.

head.s decompresses the kernel to address 0x1000000. This corresponds to the 1Mb boundary in memory. head.s does a bit of error checking before it decompresses the kernel to ensure that there is enough memory available in high memory.

Right before the decompression is done, the flags register is reset and the area in memory where setup.s was is cleared. This is to put the system in a better known state. After the decompression, control is passed to the now decompressed head.s.

Kernel Setup (linux/arch/i386/kernel/head.s)

The second head.s is responsible for setting up the permanent IDT and GDT, as well as a basic paging table. Before anything is done, the flags register is again reset. The first page in the paging system is setup at 0x5000. This page is filled by the information gathered in setup.s by copying it from its location at 0x9000.

Next the processor type is determined. For 586s (Pentium) and higher there is a processor command that returns the type of processor. Unfortunately, the 386 and 486 do not have this feature so some tricks have to be employed. Each processor has only certain flags, so by trying to read and write to them you can determine the type of processor. If a coprocessor is present that is also detected.

After that, the IDT and GDT are set up. The table for the IDT is set up. Each interrupt gets an 8-byte descriptor. Each descriptor is initially set to ignore_int. This means that nothing will happen when the interrupt is called. All that ignore_int does is, is save the registers, print “unknown interrupts”, and then restore the registers.

Each IDT descriptor is divided into four two-byte sections. The top four bytes are called the WW, while the bottom four are the CW. The WW contains a two-byte offset, a P-flag set to 1, and a Descriptor Privilege Level. The CW has a selector and an offset. In total the IDT can contain up to 256 entries.

At this point the code sets up memory paging. In the x86 architecture, virtual memory uses three descriptors to establish an address: a Page Directory, a Page Table, and a Page Frame. The Page Directory is a table of all of the pages and what processes they match to. The Page Directory contains an index into the Page Table. The Page Table maps the virtual address to the beginning of a physical page in memory. The Page Frame and an offset use the beginning address of the physical page and can retrieve an actual location in memory. The three structures are setup by head.s. They make it so that the first 4Mb of memory is in the Page Directory. The kernel's virtual address is set to 0xC0000000, or the top of the last gigabyte of memory.

Each memory address in an x86 has three parts. The first is the index into the Page Directory. The result of this index is the start of a specific Page Table. The second part of the 32-bit address is an offset into the Page Table. The Page Table has a 32-bit entry that corresponds to that offset. The top 20 bits are used to get an actual physical address. The lower 12 bits are used for administrative purposes. The physical address corresponds to the start of a physical page. The third part of the 32-bit address is an offset within this page, equal to a real memory location.

Almost everything is set up at this point. Now control is passed to the main function in the kernel. Main.c gains control.

Kernel Initialization (linux/init/main.c)

All remaining setup and initialization functions are called from main.c. Paging, the IDT and most of the other systems have been initialized by now. Main.c will make sure everything is in its proper place before it tries to start some processes and give control to init.c.

A call to the function start_kernel() is made. In essence all that start_kernel() does is run through a list of init functions that needed to be called. Such things as paging, traps, IRQs, process schedules and more are setup. The important work has already been done for memory and interrupts. Now all that has to be done is to have all of the tables filled in.

Init process creation (linux/init/main.c)

After all of the init functions have been called main.c tries to start the init process. main.c tries three different copies of init in order. If the first doesn't work, it tries the second, if that one doesn't work it goes to the third. Here are the file names for the three init versions:

  1. /etc/init

  2. /bin/init

  3. /sbin/init

If none of these three inits work, then the system goes into single user mode. init is needed to log in multiple users and to manage many other tasks. If it fails, then the single user mode creates a shell and the system goes from there.

What happens next, what does /sbin/init do?

init is the parent of all processes, it reads the file /etc/inittab and creates processes based on its contents. One of the things it usually does is spawn gettys so that users can log in. It also defines so called “runlevels”.

A “runlevel” is a software configuration of the system which allows only a selected group of processes to exist.

init can be in one of the following eight runlevels

runlevel 0 (reserved)

Runlevel 0 is used to halt the system.

runlevel 1 (reserved)

Runlevel 1 is used to get the system in single user mode.

runlevel 2-5

Runlevels 2,3,4 and 5 are multi-user runlevels.

runlevel 6

Runlevel 6 is used to reboot the system.

runlevel 7-9

Runlevels 7, 8 and 9 are also valid. Most of the Unix variants don't use these runlevels. On a particular Debian Linux System for instance, the /etc/rc<runlevel>.d directories, which we'll discuss later, are not implemented for these runlevels, but they could be.

runlevel s or S

Runlevels s and S are internally the same runlevel S which brings the system in “single-user mode”. The scripts in the /etc/rcS.d directory are executed when booting the system. Although runlevel S is not meant to be activated by the user, it can be.

runlevels A, B and C

Runlevels A, B and C are so called “on demand” runlevels. If the current runlevel is “2” for instance, and an init A command is executed, the things to do for runlevel “A” are done but the actual runlevel remains “2”.

Configuring /etc/inittab

As mentioned earlier, init reads the file /etc/inittab to determine what it should do. An entry in this file has the following format:

id:runlevels:action:process

Included below is an example /etc/inittab file.

# The default runlevel.
id:2:initdefault:

# Boot-time system configuration/initialization script.
# This is run first except when booting in emergency (-b) mode.
si::sysinit:/etc/init.d/rcS

# What to do in single-user mode.
~~:S:wait:/sbin/sulogin

# /etc/init.d executes the S and K scripts upon change
# of runlevel.
#
# Runlevel 0 is halt.
# Runlevel 1 is single-user.
# Runlevels 2-5 are multi-user.
# Runlevel 6 is reboot.

l0:0:wait:/etc/init.d/rc 0
l1:1:wait:/etc/init.d/rc 1
l2:2:wait:/etc/init.d/rc 2
l3:3:wait:/etc/init.d/rc 3
l4:4:wait:/etc/init.d/rc 4
l5:5:wait:/etc/init.d/rc 5
l6:6:wait:/etc/init.d/rc 6
# Normally not reached, but fall through in case of emergency.
z6:6:respawn:/sbin/sulogin

# /sbin/getty invocations for the runlevels.
#
# The "id" field MUST be the same as the last
# characters of the device (after "tty").
#
# Format:
#  <id>:<runlevels>:<action>:<process>
1:2345:respawn:/sbin/getty 38400 tty1
2:23:respawn:/sbin/getty 38400 tty2
          

Description of an entry in /etc/inittab:

id

The id-field uniquely identifies an entry in the file /etc/inittab and can be 1-4 characters in length. For gettys and other login processes however, the id field should contain the suffix of the corresponding tty, otherwise the login accounting might not work.

runlevels

This field contains the runlevels for which the specified action should be taken.

action

The “action” field can have one of the following values:

respawn

The process will be restarted whenever it terminates, (e.g. getty).

wait

The process will be started once when the specified runlevel is entered and init will wait for its termination.

once

The process will be executed once when the specified runlevel is entered.

boot

The process will be executed during system boot. The runlevels field is ignored.

bootwait

The process will be executed during system boot, while init waits for its termination (e.g. /etc/rc). The runlevels field is ignored.

off

This does absolutely nothing.

ondemand

A process marked with an on demand runlevel will be executed whenever the specified ondemand runlevel is called. However, no runlevel change will occur (on demand runlevels are “a”, “b”, and “c”).

initdefault

An initdefault entry specifies the runlevel which should be entered after system boot. If none exists, init will ask for a runlevel on the console. The process field is ignored. In the example above, the system will go to runlevel 2 after boot.

sysinit

The process will be executed during system boot. It will be executed before any boot or bootwait entries. The runlevels field is ignored.

powerwait

The process will be executed when the power goes down. init is usually informed about this by a process talking to a UPS connected to the computer. init will wait for the process to finish before continuing.

powerfail

As for powerwait, except that init does not wait for the process's completion.

powerokwait

This process will be executed as soon as init is informed that the power has been restored.

powerfailnow

This process will be executed when init is told that the battery of the external UPS is almost empty and the power is failing (provided that the external UPS and the monitoring process are able to detect this condition).

ctrlaltdel

The process will be executed when init receives the SIGINT signal. This means that someone on the system console has pressed the CTRL-ALT-DEL key combination. Typically one wants to execute some sort of shutdown either to get into single-user level or to reboot the machine.

kbdrequest

The process will be executed when init receives a signal from the keyboard handler that a special key combination was pressed on the console keyboard. Basically you want to map some keyboard combination to the “KeyboardSignal” action. For example, to map Alt-Uparrow for this purpose use the following in your keymaps file: alt keycode 103 = KeyboardSignal.

process

This field specifies the process that should be executed. If the process field starts with a “+”, init will not do utmp and wtmp accounting. Some gettys insist on doing their own housekeeping. This is also a historic bug.

The /etc/init.d/rc script

For each of the runlevels 0-6 there is an entry in /etc/inittab that executes /etc/init.d/rc ? where “?” is 0-6, as you can see in following line from the earlier example above:

l2:2:wait:/etc/init.d/rc 2
          

So, what actually happens is that /etc/init.d/rc is called with the runlevel as a parameter.

The directory /etc contains several, runlevel specific, directories which in their turn contain runlevel specific symbolic links to scripts in /etc/init.d/. Those directories are:

$ ls -d /etc/rc*
/etc/rc.boot  /etc/rc1.d  /etc/rc3.d  /etc/rc5.d  /etc/rcS.d
/etc/rc0.d    /etc/rc2.d  /etc/rc4.d  /etc/rc6.d
          

As you can see, there also is a /etc/rc.boot directory. This directory is obsolete and has been replaced by the directory /etc/rcS.d. At boot time, the directory /etc/rcS.d is scanned first and then, for backwards compatibility, the /etc/rc.boot.

The name of the symbolic link either starts with an “S” or with a “K”. Let's examine the /etc/rc2.d directory:

$ ls /etc/rc2.d
K20gpm       S11pcmcia   S20logoutd  S20ssh      S89cron
S10ipchains  S12kerneld  S20lpd      S20xfs      S91apache
S10sysklogd  S14ppp      S20makedev  S22ntpdate  S99gdm
S11klogd     S20inetd    S20mysql    S89atd      S99rmnologin
          

If the name of the symbolic link starts with a “K”, the script is called with “stop” as a parameter to stop the process. This is the case for K20gpm, so the command becomes K20gpm stop. Let's find out what program or script is called:

$ ls -l /etc/rc2.d/K20gpm
lrwxrwxrwx 1 root root 13 Mar 23 2001 /etc/rc2.d/K20gpm -> ../init.d/gpm
          

So, K20gpm stop results in /etc/init.d/gpm stop. Let's see what happens with the “stop” parameter by examining part of the script:

#!/bin/sh
#
# Start Mouse event server
...
case "$1" in
  start)
     gpm_start
     ;;
  stop)
     gpm_stop
     ;;
  force-reload|restart)
     gpm_stop
     sleep 3
     gpm_start
     ;;
  *)
     echo "Usage: /etc/init.d/gpm {start|stop|restart|force-reload}"
     exit 1
esac
          

In the case..esac the first parameter, $1, is examined and in case its value is “stop”, gpm_stop is executed.

On the other hand, if the name of the symbolic link starts with an “S”, the script is called with “start” as a parameter to start the process.

The scripts are executed in a lexical sort order of the filenames.

Let's say we've got a daemon SomeDaemon, an accompanying script /etc/init.d/SDscript and we want SomeDaemon to be running when the system is in runlevel 2 but not when the system is in runlevel 3.

As you've read earlier, this means that we need a symbolic link, starting with an “S”, for runlevel 2 and a symbolic link, starting with a “K”, for runlevel 3. We've also determined that the daemon SomeDaemon is to be started after S19someotherdaemon which implicates S20 and K80 since starting/stopping is symmetrical, i.e. that what is started first is stopped last. This is accomplished with the following set of commands:

# cd /etc/rc2.d
# ln -s ../init.d/SDscript S20SomeDaemon
# cd /etc/rc3.d
# ln -s ../init.d/SDscript K80SomeDaemon
          

Should you wish to manually start, restart or stop a process, it is good practice to use the appropriate script in /etc/init.d/ , e.g. /etc/init.d/gpm restart to initiate the restart of the process.

The LSB standard

The Linux Standard Base (LSB) defines an interface for application programs that are compiled and packaged for LSB-conforming implementations. Hence, a program which was compiled in an LSB compatible environment will run on any distribution that supports the LSB standard. LSB compatible programs can rely on the availability of certain standard libraries. The standard also includes a list of mandatory utilities and scripts which define an environment suitable for installation of LSB-compatible binaries.

The specification includes processor architecture specific information. This implies that the LSB is a family of specifications, rather than a single one. In other words: if your LSB compatible binary was compiled for an Intel based system, it will not run on, for example, an Alpha based LSB compatible system, but will install and run on any Intel based LSB compatible system. The LSB specifications therefore consist of a common and an architecture-specific part; "LSB-generic" or "generic LSB" and "LSB-arch" or "archLSB".

The LSB standard lists which generic libraries should be available, e.g. libdl, libcrypt, libpthread and so on, and provides a list of processor specific libraries, like libc and libm. The standard also lists searchpaths for these libraries, their names and format (ELF). Another section handles the way dynamic linking should be implemented. For each standard library a list of functions is given, and data definitions and accompanying header files are listed.

The LSB defines a list of 130+ commands that should be available on an LSB compatible system, and their calling conventions and behaviour. Some examples are cp, tar, kill and gzip.

The expected behaviour of an LSB compatible system during system initialization is part of the LSB specification. So is a definition of the cron system, and are actions, functions and location of the init scripts. Any LSB compliant init script should be able to handle the following options: start, stop, restart, force-reload and status. The reload and try-restart options are optional. The standard also lists the definitions for runlevels and listings of user- and groupnames and their corresponding UID's/GID's.

Though it is possible to install an LSB compatible program without the use of a package manager (by applying a script that contains only LSB compliant commands), the LSB specification contains a description for software packages and their naming conventions.

Note

LSB employs the Red Hat Package Manager standard. Debian based LSB compatible distributions may read RPM packages by using the alien command.

The LSB standards frequently refers to other well known standards, for example ISO POSIX 2003. Also, any LSB conforming implementation needs to provide the mandatory portions of the file system hierarchy as specified in the Filesystem Hierarchy Standard (FHS) , and a number of LSB specific requirements.

The bootscript environment and commands

Initially, Linux contained only a limited set of services and had a very simple boot environment. As Linux aged and the number of services in a distribution grew, the number of initscripts grew accordingly. After a while a set of standards emerged. Init scripts would routinely include some other script, which contained functions to start, stop and verify a process.

The LSB standard lists a number of functions that should be made available for runlevel scripts. These functions should be listed in files in the directory /lib/lsb/init-functions and need to implement (at least) the following functions:

  1. start_daemon [-f] [-n nicelevel] [-p pidfile] pathname [args...]

    runs the specified program as a daemon. The start_daemon function shall check if the program is already running. If so, it shall not start another copy of the daemon unless the -f option is given. The -n option specifies a nice level.

  2. killproc [-p pidfile] pathname [signal]

    shall stop the specified program, trying to terminate it using the specified signal first. It that fails, the SIGTERM signal will be sent. If a program has been terminated, the pidfile should be removed if the terminated process has not already done so.

  3. pidofproc [-p pidfile] pathname

    returns one or more process identifiers for a particular daemon, as specified by the pathname. Multiple process identifiers are separated by a single space.

In some cases, these functions are provided as stand-alone commands and the scripts simply assure that the path to these scripts is set properly. Often some logging functions and function to display status lines are also included.

Changing and configuring runlevels

Changing runlevels on a running machine requires comparison of the services running in the current runlevel with those that need to run in the new runlevel. Subsequently probably some processes need to be stopped and others to be started.

Recall that the initscripts for a runlevel “X” are grouped in directory /etc/rc.d/rcX.d (or, on newer (LSB based) systems, in /etc/init.d/rcX.d). Their names there determine how the scripts are called: if the name starts with a “K”, the script will be run with the stop, if the name starts with a “S”, the script will be run with the stop option. The normal procedure during a runlevel change is to stop the superfluous processes first and then start the new ones.

The actual init scripts are located in /etc/init.d. The files you find in in the rcX.d directory are symbolic links which link to these. In many cases, the start- and stop-scripts are symbolic links to the same script. This implies that such init scripts should be able to handle at least the start and stop options.

For example, the symbolic link named S06syslog in /etc/init.d/rc3.d might point to the script /etc/init.d/syslog, as may the symbolic link found in /etc/init.d/rc2.d, named K17syslog.

The order in which services are stopped or started can be of great importance. Some services may be started simultaneously, others need to start in a strict order. For example your network needs to be up before you can start the httpd. The order is determined by the names of the symbolic links. The naming conventions dictate that the names of init scripts (the ones found in the rcN.d directories) include two digits, just after the initial letter. They are executed in alphabetical order.

In the early days system administrators created these links by hand. Later most Linux distributors decided to provide Linux commands/scripts which allow the administrator to disable or enable certain scripts in certain runlevels and to check which systems (commands) would be started in which runlevel. These commands typically will manage both the aforementioned links and will name these in such a way that the scripts are run in the proper order.

The insserv command and its configuration files

If you want to automate the making and breaking of symbolic links which determine what to run in which runlevel, you need at least some kind of administration to keep track of the various dependencies. The standardized method to do this is to make use of special comment blocks, that contain information about the dependencies.

To administer which scripts should be run in which runlevels, a special header block is inserted into each init script. An example of such a header block is given below:

### BEGIN INIT INFO
# Provides:          food
# Required-Start:    $syslog $remote_fs
# Required-Stop:     $syslog $remote_fs
# Default-Start:     3 5
# Default-Stop:      0 1 2 6
# Description:       the foo daemon serves as an example daemon
### END INIT INFO

This block states that the init script in which this comment is found handles the foo daemon (service). This daemon needs system logging and remote file systems to be active before it may start (Required-Start) and requires system logging and remote file systems to be active to stop (Required-Stop). The foo daemon will be started upon entering runlevels 3 and 5 (Default-Start) and stopped upon entering runlevels 0 1 2 and 6 (Default-Stop). The description speaks for itself.

Additionally, the following keywords may be found in the INIT INFO block:

Should-Start: start these first if you can. 
              If not available the service may run with reduced functionality.
Should-Stop: try to stop these services after this service has been stopped. 
X-xxx-xxx: these are vendor supplied options

As you can see, it is possible to use variables instead of literal function names to list the services which should be started/stopped. insserv uses the concept of System Facilities (SF) and associates dependencies to each one of them. These “system facilities” are defined in files in the directory /etc/insserv.conf.d/ or in a file named /etc/insserv.conf. These files define the labels and their meaning, for example:

#
# All local filesystems are mounted (done during boot phase)
#
$local_fs	boot.localfs

#
# Low level networking (ethernet card)
#
$network	network +pcmcia +hotplug

#
# Named is operational
#
$named		+named $network

#
# All remote filesystems are mounted (note in some cases /usr may
# be remote. Most applications that care will probably require
# both $local_fs and $remote_fs)
#
$remote_fs	$local_fs +nfs

#
# System logger is operational
#
$syslog		syslog

#
# The system time has been set correctly
#
$time		boot.clock +xntpd

The combination of startpar and insserv intends to provide parallel execution during boot time by considering the dependencies. insserv creates a file that may be used with the startpar to perform a parallel init script execution while considering the dependencies.

Servicenames that start with a plus '+' sign are optional, e.g. in our example above, nfs will be started if it is available. If not, it is silently ignored. The names of the services (e.g. nfs, network etc.) correspond to the init scripts as found in /etc/init.d.

To create the links from the scripts in /etc/init.d/ to the corresponding runlevel directories (/etc/init.d/rcN.d/) you can use the insserv command. It evaluates the headers and creates the necessary links for start and stop scripts in the appropriate runlevel directories. It also determines the order in which the various scripts should be run to satisfy their mutual dependencies. Most distributions also provide some graphical tool to create the links.

The chkconfig command

Another tool to manage the proper linking of start up (init) scripts is chckconfig. On some systems (e.g. SuSE/Novell) it serves as a front-end for insserv and uses the LSB standardized comment block to maintain its administration. On older systems it maintains its own special comment section, that has a much simpler and less flexible syntax. This older syntax consists of two lines, one of them is a description of the service, it starts with the keyword description:). The other line starts with the keyword chkconfig:, and lists the run levels for which to start the service and the priority (which determines in what order the scripts will be run while changing runlevels). For example:

# Init script for foo daemon
#
# description: food, the foo daemon
# chkconfig: 2345 55 25
#
#

This denotes that the foo daemon will start in runlevels 2, 3, 4 and 5, will have priority 55 in the queue of initscripts that are run during startup and priority 25 in the queue of initscripts that are run if the daemon needs to be stopped.

The chkconfig utility can be used to list which services will be started in which runlevels, to add or delete a service to or from a runlevel and to add or delete an entire service from the startup scripts.

Note

We are providing some examples here, but be warned: there are various versions of chkconfig around. Please read the manual pages for the chkconfig command on your distribution first.

chkconfig does not automatically disable or enable a service immediately, but simply changes the symbolic links. If the cron daemon is running and you are on a Red Hat based system which is running in runlevel 2, the command

 
# chkconfig --levels 2345 crond off

would change the administration but would not stop the cron daemon immediately. Also note that on a Red Hat system it is possible to specify more than one runlevel, as we did in our previous example. On Novell/SuSE systems, you may use:

 
# chkconfig food 2345

and to change this so it only will run in runlevel 1 simply use

 
# chkconfig food 1

# chkconfig --list 

will list the current status of services and the runlevels in which they are active. For example, the following two lines may be part of the output:

xdm                       0:off   1:off   2:off   3:off   4:off   5:on    6:off   
xfs                       0:off   1:off   2:off   3:off   4:off   5:off   6:off   

They indicate that the xfs service is not started in any runlevel and the xdm service only will be started while switching to runlevel 5.

To add a new service, let's say the foo daemon, we create a new init script and name it after the service, in this case we might use food. This script is consequently put into the /etc/init.d directory, we need to insert the proper header in that script (either the old chkconfig header, or the newer LSB compliant header) and run

# chkconfig --add food 

To remove the foo service from all runlevels, you may type:

# chkconfig --del food

Note, that the food script will remain in the /etc/init.d/ directory.

The startproc command

This command has identical functionality as the LSB command start_daemon. It starts a process if it is not already running. One of its arguments is the full path to an executable. startproc will check if there already is a process running from that executable. If not, it will start a new process by executing the executable. There are options to set the user- and groupname (-u, -g) under which the new proces should be run, the nice (-n) level and the -f option to force execution even if there seems to be another process running from the same executable.

By default, the command checks for a pidfile /var/run/<basename>.pid, but that name can be overridden by specifying the -p option. If such a pidfile exists, it is read and the command limits itself to verification of the process with that pid. If no matching process could be found, the process will be started. It is up to that process to create, delete or update the pidfile.

startproc will refuse to start up a process (unless forced by setting the -f flag) if it finds a matching zombie process in the process table.

The checkproc command

checkproc checks if a process is running. One of its arguments is the full path to an executable. checkproc will check if there already is a process running from that executable. If so it exits with exitcode 0. If not, it will return nothing and exit with exit code 1. If the -v flag has been specified, the command will report the pids of any matching running processes it finds.

By default, the command checks for a pidfile /var/run/<basename>.pid, but that name can be overridden by specifying the -p option. If such a pidfile exists, it is read and the command limits itself to verification of the process with that pid.

The killproc command

killproc is used to signal running process(es). This is mostly used to stop these processes. It allows the signal to send to be specified but defaults to SIGTERM. If SIGTERM is used and the process does not terminate within five seconds a SIGKILL is sent to it. The time between sending these signals can be tuned using the -t option. If a process has been terminated and a verified pid file was found, this pid file will be removed. killproc will try to prevent killing itself, its parents or grandparents.

The startpar command

For many years init scripts would run sequentially. Partially as a result of the architecture: there was only one init process that would start up a script, which then (by nature) would execute its statements sequentially. Another reason was that many init scripts depended on each other and therefore needed to be executed sequentially an in a fixed order. For example, before you could start the mailserver, the network connections needed to be up and running. And, since most older Unix (Linux) systems had limited amounts of memory, limited bus speeds and limited IO bandwidths, which could be consumed easily by just one init script, the sequential paradigm also prevented slugginess. It nevertheless took a rather long time for a system to change runlevels.

Nowadays, with much faster hardware, there are plenty of system resources available during boot. Even though some services still need to be started sequentially, some of them can be started in parallel, thus shortening the time for runlevel changes. This can be done by employing the startpar command.

startpar's task is to start runlevel scripts in parallel while serializing their output. The order in which these scripts should be executed and whether or not they are allowed to be executed in parallel is determined by the (LSB compliant) comment headers in the scripts. In order to know which programs should be executed first, the startpar command uses make-like dependency files, which were created by the insserv command. Such dependency files list the various services and their dependencies.

The -M option switches startpar into this make like behaviour. The option takes three different arguments: boot, start, and stop, which translates into reading .depend.boot, .depend.start or .depend.stop respectively (normally found in /etc/init.d/, and put there by an earlier run of insserv)

The degree of parallelism on one CPU can be set with the -p option. By default full parallelism will be employed. If you want to provide an argument to all of the scripts you may use the -a option.

Copyright Snow B.V. The Netherlands