2004-02-27 14:33

The Linux Booting Process Unveiled

Biblical version:

In the beginning, there was GRUB (or maybe LILO) and GRUB loaded the kernel, and kernel begat init, and init begat rc, and rc begat network and httpd and getty, and getty begat login, and login begat shell and so on.


In this article we will explore how most Linux distributions boot, in some detail. I haven't seem many full explanations of this, and it's not terribly complex, although it is somewhat long.

I will use a Red Hat 9 system to explain it, but most distributions should be almost the same... except Slackware and its derivatives.

While almost everyone uses what's called a SystemV init setup, Slackware and derivatives use a BSD init ( like FreeBSD, OpenBSD and *BSD, of course :-), which is different.

Step 1: The Boot Manager

The boot manager is a small program that resides mostly on the MBR [1] and presents you with a menu letting you choose what operating system (if you have more than one) you want to boot.

Nowadays the most common one is GRUB, with LILO a second popular alternative. [2]

I will not get into much detail about them, both are well documented, and most of what you want to know about them is how to make them boot windows, or multiple linux kernels, and that's not what this article is about.

In the regular, plain-old-booting-linux business, all the boot loader does is:

  • Load the kernel into memory
  • Optionally load a ramdisk called initrd containing stuff like disk drivers
  • Pass the kernel arguments, of which we are only interested in runlevel and init
  • Start execution of the kernel.

So, what are those two arguments I mentioned, runlevel and init? We'll get to runlevel eventually, but init we'll see soon.

Step 2: init

The init argument the boot loader can pass to the kernel is the name of a program. Usually, none is given, and the default, /sbin/init is used.

You could, for example, pass init=/bin/sh to the kernel, and then a plain shell would be used instead.

What does the kernel do with init? It starts it. It's the only program the kernel itself starts, everything else is started by init.

The regular Linux init will then read a file called /etc/inittab to see what it has to do. The format of that file is somewhat involved and archaic, but it's not too complex.

To understand it, you have to know what a runlevel is. A runlevel is a state for the system. Usually, you have runlevels 0,1,2,3,4,5,6 and maybe S (Debian has it, Red Hat doesn't).

For example, runlevel 1 (or S) usually means a single shell running, as few processes as possible, maybe no login, maybe just asking for root's password. While runlevel 5 may mean 6 logins in text mode, a graphical login, and a web server running. As I said, two different states of the system.

The system starts, when init loads in an undefined state (sometimes called N), and then will switch to one runlevel or another depending on what the runlevel argument from the bootloader to the kernel was, and the contents of /etc/inittab.

For example, if the bootloader passed runlevel as 5, init will try to switch to that state. If no runlevel argument was passed, it will use its default, which is in /etc/inittab

Normally the only reason for the bootloader to pass an argument is if you want it to boot in an unusual state, for example, a single-user mode for maintenance (runlevel 1), or with a replacement init because of disk corruption (init=/bin/sh).

So, let's look at that file...

Step 2.1: /etc/inittab

All lines starting with # are comments

The other lines are like this:

1:2345:respawn:/sbin/mingetty tty1

They have 4 fields, separated with colons, which mean (taken from the inittab(5) man page).

This has no real meaning, but should be different for each line, can be one to four characters
For example, 2345 means this line applies to runlevels 2,3,4 and 5. In some actions (see below) this field is ignored, in some it means something else :-P
what this line does
a command to be executed. Some actions require no command

When it's booting, to decide the desired runlevel (again, if it's not passed as an argument), init will look for a line with the initdefault action.

For example:


That means: go to runlevel 5. So, if you wanted to change the default runlevel, that's what you change.

But what does it mean to go to one runlevel? Well, each runlevel runs a different configuration of software. One runlevel may have a webserver running, and another not have it. One runlevel may show you a graphical login screen, or not, or give you 6 text terminals, or one.

Think of it like those old multiboot menus on DOS which read different autoexec.bat and config.sys files.

The meaning of the runlevels may be different on different distributions, here's what it says for Red Hat:

# The runlevels used by RHS are:
#   0 - halt (Do NOT set initdefault to this)
#   1 - Single user mode
#   2 - Multiuser, without NFS (The same as 3, if you do not have networking)
#   3 - Full multiuser mode
#   4 - unused
#   5 - X11
#   6 - reboot (Do NOT set initdefault to this)

So, for example, if you switched to runlevel 6, it would reboot. You can switch runlevels at any moment using the telinit command, but for the purposes of booting and this article, you switch only once, to the default runlevel, and you're done.

So, what happens after you know you are going to runlevel 5?

If you are booting, you check all lines with actions sysinit boot and bootwait, in that order, and run what the command field says.

In Red Hat's default inittab, that's just this:


So, it will run a script called /etc/rc.d/rc.sysinit, which does stuff like loading a terminal font, check disks, mount stuff... basic system habitability drudge work.

Then it will get all lines with action once and wait that have the desired runlevel in the runlevel field, and will run its commands, and will wait until the wait lines commands are finished.

In Red Hat, for runlevel 5:

l5:5:wait:/etc/rc.d/rc 5

What this particular script does is start all services configured for runlevel 5. That's what you see when it says things like "configuring the frobnozz [OK]" on boot. Now, let's see the details...

Step 3: Services

In fact, services start before the end of what's described in Step 2.1, so this is no longer logically organized. Complain to Congress, not to me ;-)

When you install a decently packaged software that needs to run without being manually started by a user (think webserver), it should have provided you with a control script for itself, and placed it in the standard place: /etc/init.d/ or /etc/rc.d/init.d depending on distribution. Most have both and they are the same.

There you will find many scripts. For example, I have one called /etc/init.d/network which, amazingly enough, controls the network.

For example, if I do /etc/init.d/network stop [3] it brings down the network. If I did /etc/init.d/network start it would bring it back up.

Some services support more or less commands, but all support stop, start and restart. You can learn what they handle by calling it without arguments:

[[email protected] root]# /etc/init.d/network
Usage: /etc/init.d/network {start|stop|restart|reload|status}
[[email protected] root]# /etc/init.d/httpd
Usage: /etc/init.d/httpd {start|stop|restart|condrestart|reload|status|fullstatus|graceful|

For each runlevel, there's a list of services that should be started, and a list of services that should be stopped.

On entering runlevel 5, for example, you may want to stop service httpd but start service smb, or whatever.

I heavily recommend you use a system management tool, like Red Hat's redhat-config-services or Debian's rcconf to handle this, they are simple and work just fine.

But, if you want to do it by hand, or just want to know how that configuration is stored, read on :-)

For each runlevel N, there is a folder, called /etc/rc.d/rcN.d.

Here's part of my folder for runlevel 5:

K05saslauthd -> ../init.d/saslauthd
K10lircd -> ../init.d/lircd
K10lircmd -> ../init.d/lircmd
K15httpd -> ../init.d/httpd
K15nessusd -> ../init.d/nessusd
S00microcode_ctl -> ../init.d/microcode_ctl
S05kudzu -> ../init.d/kudzu
S08iptables -> ../init.d/iptables
S10network -> ../init.d/network
S12syslog -> ../init.d/syslog
S13irqbalance -> ../init.d/irqbalance

The things that start with K are to be stopped. Those which start with S are to be started. The numbers are to give them an order to be killed or started.

So, we start by taking all the K stuff. For each, we check if the service is running. If it is, it's stopped.

Then we go over all the S stuff, if it is not running, it's started.

The stopping or starting is simply done by calling, for example

/etc/rc.d/rc5.d/S10network start

Since S10network is a symbolic link to /etc/rc.d/init.d/network, it's just the same as what we used before to start the network service.

In Red Hat, there's one particular service called local, usually started in last place (like S99local), which runs /etc/rc.d/rc.local.

If you have something you want to run on boot, but don't want to bother creating a service script (or don't know how), you can just tack it on the end in rc.local and have it work.

However, be careful! You can't login into the system [4] until rc.lcoal finishes, so don't put there any commands that take too long. Or if you do, put them in the background by adding a & at the end of the command.

On other distributions this will be different. For example, Debian has a folder where you throw scripts, instead of a single file.

After all that is done, all services are started, we get back to inittab.

Step 4: More inittab fun

Now init will get all lines with action respawn for the desired runlevel and start their processes. respawn commands are restarted when they end, so they will be running pretty much all the time as long as you are in this runlevel. [5]

In Red Hat for runlevel 5:

1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6
x:5:respawn:/etc/X11/prefdm -nodaemon

The lines with id 1 through 6 run a program in the terminals you reach using ALT-F1 through ALT-F6, which asks your username. Yes, those are what you use to login in text mode.

The line with id x gives you a graphical login. Notice that this is not in runlevels 1,2,3 or 4, so those have no graphical login.

You could have more or less text terminals or graphic logins editing this. Notice that to have two graphical logins you need other configurations, too.

And voilá, you are booted and ready to login.

[1] Master boot Record, a small piece at the beginning of your disk.
[2] LILO has some limitations, but it's simpler. However, GRUB is both more powerful and easier to fix when in trouble.
[3] I have to use the full path to execute it because it's not in one of the regular directories where programs are stored. Some distros, including Red Hat, include a handy command called service that lets you do this instead: service network stop with the same effect.
[4] At least locally, you may enter through ssh if the service is up
[5] Other pieces of inittab inclulde what should happen when you press ctr-alt-delete (ctrlaltdel action) or how to react to power failure to your UPS (powerok, powerwait, powerfail) or have functionality I have never seen in use (ondemand), but that's beyond the scope of this article.


Comments powered by Disqus

Contents © 2000-2019 Roberto Alsina