Take Command: Init

来源:互联网 发布:it软件开发 编辑:程序博客网 时间:2024/05/22 03:21

Take Command: Init
(November 1998)

Reprinted with permission of the Linux Journnal

Init is the driving force that keeps our Linux box alive, and itis the one that can put it to death. This article is meant tosummarize why Init is so powerful and how you can instruct it tobehave differently from its default behaviour (yes, Init is powerful,but the superuser rules over Init).

by Alessandro Rubini

Which Init?

In Unix parlance, the word ``init'' doesn't identify a specificprogram, but rather a class of programs. The name ``init'' isgenerically used to call the first process that is executed at systemboot -- actually,the only process that is executed at systemboot. When the kernel is done with setting up the computer's hardware,it invokes init and gives up controlling the computer. From now on thekernel only processes system calls, without taking any decisional rolein system operation. After the kernel is done mounting the rootfilesystem, everything is controlled by init.

Currently, there are several choices as far as init is concerned: youcan use the now-classic program that comes in the SysVinit package byMiquel van Smoorenburg, or simpleinit by Peter Orbaek (found in thesource package of util-linux), or a simple shell script (like the oneshown in this article, which has a lot of functionality less than anyC-language implementation). If you set up embedded systems you caneven just run the target application like it was init. Insane peoplewho dislike multitasking could even port command.com to Linux and runit as the init process, although you won't ever be able to restrictyourself to 640k when running a Linux kernel.

No matter what is the program you choose, it needs to be accessed witha pathname of /sbin/init, /etc/init or /bin/init, because thesepathnames are compiled in the kernel. If neither of them can beexecuted than the system is severely broken, and the kernel will spawna root shell to allow interactive recovery (i.e., /bin/sh is used asan init process).

To achieve maximum flexibility, kernel developers offered a way toselect a different pathname for the init process. The kernel accepts acommand line option ofinit= exactly for that purpose. Kerneloptions can be passed interactively at boot time, or you can use theappend= directive in /etc/lilo.conf. Silo, Milo, Loadlin andother loaders allow specifying kernel options as well.

As you may imagine, the easiest way to get root access to a Linux boxis by typinginit=/bin/sh to the Lilo prompt. Note that thisis not a security holeper se, because the real security holehere is physical access to the console. If you are concerned about theinit= option, Lilo can prevent interaction using its ownpassword protection.

The task of init

Ok, so init is a generic naming, and almost anything can be usedas init. The question now is what is a real init supposed to do.Being the first (and only) process spawned by the kernel, the task ofinit consists in spawning every other process in the system. Thisusually includes the various daemons used in system operation as wellas any login session on the text console.Init is also expected to restart some of its child processes as soonas they exit. This typically applies to the login sessions running onthe text consoles: as soon as you logout the system should run another``getty'' to allow starting another session.Init should also collect dead processes and dispose of them. In theUnix abstraction of processes, a process can't be removed from thesystem table unless its death is reported to its parent (or anotherancestor in case its parent doesn't exist anymore). Whenever a processdies, by callingexit or otherwise, it remains around in thestate of a zombie process until someone collects it. Init, being theancestor of any other process, is expected to collect the exit statusof any orphaned zombie process -- note that every well-written programshould reap its own children, zombies only exist when some program ismisbehaving. If init wouldn't collect zombies, lazy programmers couldeasily consume system resources and hang the system by filling theprocess table.The last task of Init is handling system shutdown. The init programmust stop any process and unmount all the filesystems when thesuperuser tells that shutdown time has arrived. Theshutdownexecutable, actually, doens't do anything but tell init thatanything is over.As we have seen, the task of init is not too hard to implement, and ashell script could well perform most of the required tasks. Note thatevery decent shell collects its dead children, so this is not aproblem with shell scripts.What real init implementations add to the simple shell scriptapproach is a greater control over system activity, and thus a hugebenefit in overall flexibility.This article will now proceed by showing different implementations ofthe init concept, in ascending order of complexity.

Using /bin/sh as a minimal choice

As suggested above, the shell can be used as an init program.Using a bare shell, in theinit=/bin/sh way, only opens aroot shell in a completely unconfigured system. This section shows howa shell script can perform all of the tasks you need to have a minimalrunning system. This kind of tiny init can be used in embedded systemor similar reduced environments, where you must squeeze out everysingle byte out of the system. Note that the most radical approach toembedded systems is directly running the target application as theinit process; this results in a closed system (no way for theadministrator to interact should problems arise), but it sometimessuites the setup. The typical example of non-init-driven Linux systemis the installation environment of most modern distributions, where/sbin/init is a symbolic link to the installation program.

    #!/bin/sh    # avoid typing full pathnames    export PATH=/usr/bin:/bin:/sbin:/usr/sbin    # remount root read-write, and mount all    mount -n -o remount,rw /    mount -a    swapon -a    # system log    syslogd    klogd    # start your lan    modprobe eth0 2> /dev/null    ifconfig eth0 192.168.0.1    route add 192.168.0.0 eth0    route add default gw 192.168.0.254    # start lan services    inetd    sendmail -bd -q30m    # Anything else: crond, named, ...    # And run one getty with a sane path    export PATH=/usr/bin:/bin    /sbin/mingetty tty1   Listing 1
To make a long story short, Listing 1 shows a script that can performacceptably as init. The script is very short and incomplete; inparticular, note that it only runs one getty, which isn't restartedwhen it terminates. Be careful if you try to use this script, as eachLinux distribution chooses its own flavour of getty. Try grepgetty /etc/inittab to know what you have and how to call it.The script shown has another misfeature: it doesn't deal with systemshutdown. Adding shutdown support, however, is pretty easy; just bringeverything down after the interactive shell terminates. Adding thetext shown in Listing2 at the end of Listing1 does the trick.
    # killa anything you started    killall inetd    killall sendmail    killall klogd    killall syslogd    # kill anything else    kill -TERM -1    sleep 2    kill -KILL -1    # release the disks    swapoff -a    umount -a    mount -n -o remount,ro /    echo "The system is halted"    exit   Listing 2
Whenever you boot with a plain init=/bin/sh, you should atleast remount the root filesystem before you'll be able to doanything; you should also remember toumount -a before pressingctrl-alt-del, because the shell doesn't intercept the three-fingersalute.

Simpleinit, from util-linux

The util-linux package includes a C version of an initprogram. It's quite featured and can work well for most personalsystems, although it doesn't offer the huge amount of configurabilityoffered by the SysVinit package, which is the default on moderndistributions.The role of simpleinit (which should be called init to work properly,as suggested above) is very similar to the shell script just shown,with the added capability of managing single-user mode and iterativeinvocation of console sessions. It also correctly processes shutdownrequests.Simpleinit is interesting to look at, and well documented too, so youmight just enjoy reading the documentation; I suggest using the sourcedistribution of util-linux to get up to date information.The implementation of simpleinit is actually simple, like its namesuggests. The program executes a shell script (/etc/rc) and parses aconfiguration file to know what processes need to be respawned. Theconfiguration file is called /etc/inittab, like the one used by thefull-featured init; note however that its format is different.If you plan to install simpleinit in your system (which most likelyalready includes SysVinit) you must proceed with great care, and beprepared to reboot with a kernel argument of ``init=/bin/sh'' torecover from instable situations.

The Real Thing: SysVinit

Most Linux distributions come with the version of init written byMiquel van Smoorenburg, this version is similar the approach taken bySystem-V (five) Unix.The main idea here is that the user of a computer system can wish tooperate his box in one of several different ways (not just single-userand multi-user). Although this feature is not usually exploited, it'snot so crazy as you might imagine. When the computer is shared by twoor more people in the family, different setups can be needed; anetwork server and a standalone playstation can happily coexist in thesame computer as different runlevels. And although I'm the only userof my laptop, I sometimes want a network server (through PLIP) andsometimes a netless environment, to save resources when I'm working onthe train.Each operating mode is called ``runlevel'', and you can choose therunlevel to use either at boot or at runtime. The main configurationfile for init is called /etc/inittab, which defines what to do atboot, when entering a runlevel or when switching from one runlevel toanother. It also tells how handle the three-finger salute and how todeal with power fails, although you'll need a power-daemon and an UPSto benefit from this feature.The inittab file is organized by lines, where each line is made up ofseveral colon-separated fields:``id:runlevel:action:command''Theinittab(5) man page is well written and comprehensivelike a man page should be, but I feel worth repeating here one of itsexamples: a stripped-down /etc/inittab that implements thesame features and misfeatures of the shell script shown above:

    id:1:initdefault:    rc::bootwait:/etc/rc    1:1:respawn:/sbin/getty 9600 tty1    

This simple inittab tells init that the default runlevel is ``1'',that at system boot it must execute /etc/rc waiting for itscompletion, and that when in runlevel 1 it must respawn forever thecommand ``/sbin/getty 9600 tty1''. As you may suspect, you'renot expected to test this out, because it doesn't handle the shutdownprocedure.Before proceeding further, however, I must fill a pair of gapsI left behinf. Let'd reply to the questions you keep asking:

  • ``How can I boot into a different runlevel than thedefault?'' That's easy, just add the runlevel on the kernel command line;for example tell ``Linux 2'' to your Lilo prompt, if ``Linux'' is thename of your kernel.
  • ``How can I switch from a runlevel to another one?''That's easy, either; as root call ``telinit 5'' totell the init process to switch to runlevel 5. Different numbersare different runlevels.

Configuring Init

Naturally, the typical /etc/inittab file is much more featured thanthe three-liner shown above. Although ``bootwait'' and ``respawn''are the most important actions, in order to deal with several issuesrelated to system management several other actions exist, but I won't detail them here.Note that SysVinit can deal with ctrl-alt-del whereas the versions ofinit shown earlier didn't catch the three-finger salute (i.e., themachine would reboot if you press the key sequence). Who is interestedin how this is done can check sys_reboot in/usr/src/linux/kernel/sys.c (if you look in the code you'll note theuse of a magic number of 672274793: can you imagine why Linus chosethis very number? I think I know what it is, but you'll enjoy findingit by yourself).So, let's see how a fairly complete /etc/inittab can take care ofeverything that's needed to handle the needs of a system's lifetime,including different runlevels. Although the magic of the game is always on show in /etc/inittab, youcan choose between several different approaches to systemconfiguration, the simplest being the three-liner shown above. In myopinion, two approaches are worth discussing in some detail: I'll callthem ``the Slackware way'' and ``the Debian way'' from two renownLinux distributions that chose to follow them.

The Slackware way

Although it's quite some time I don't install Slackware, thedocumentation included in SysVinit-2.74 tells that it still works thesame, less featured but much faster than the Debian way describedlater. My personal 486 box runs a Slackware-like /etc/inittab just forthe speed benefit.The core of an /etc/inittab as used by a Slackare system is shown inListing 3.

    # Default runlevel.    id:5:initdefault:    # System initialization (runs when system boots).    si:S:sysinit:/etc/rc.d/rc.S    # Script to run when going single user (runlevel 1).    su:1S:wait:/etc/rc.d/rc.K    # Script to run when going multi user.    rc:2345:wait:/etc/rc.d/rc.M    # What to do at the "Three Finger Salute".    ca::ctrlaltdel:/sbin/shutdown -t5 -rf now    # Runlevel 0 halts the system.    l0:0:wait:/etc/rc.d/rc.0    # Runlevel 6 reboots the system.    l6:6:wait:/etc/rc.d/rc.6    # Runlevel 1,2,3,5 have text login    c1:1235:respawn:/sbin/agetty 38400 tty1 linux    # Runlevel 4 is X only    x1:4:wait:/etc/rc.d/rc.4    # But run a getty on /dev/tty4 just in case...    c4:4:respawn:/sbin/agetty 38400 tty1 linux   Listing 3
You should not rightahead that the runlevels 0, 1 and 6 have apredefined meaning. This is hardwired into the init command (orbetter, into the shutdown command, part of the same package).Whenever you want to halt or reboot the system, init is told to switchto runlevel 0 or 6, thus executing /etc/rc.d/rc.0 or /etc/rc.d/rc.6.This works flawlessly because whenever init switches to a differentrunlevel it stops respawning any task that is not defined for the newrunlevel; actually, it even kills the running copy of the task (inthis case, the active /sbin/agetty).Configuring this setup is pretty simple, as the role of thedifferent files is pretty clear:
  • /etc/rc.d/rc.S is run at system boot independentyof the runlevel. Add here anything you want to execute right ahead.
  • /etc/rc.d/rc.M is run after rc.S is over, only when thesystem is going to runlevels 2-5. If you boot in runlevel 1 (singleuser) this is not executed. Add here anything you onlyrun when multiuser.
  • /etc/rc.d/rc.K deals with killing processes whengoing from multi-user to single-user. If you added anything in rc.Myou'll probably want to stop it from rc.K.
  • /etc/rc.d/rc.0 and /etc/rc.d/rc.6 shutdown andreboot the computer, respectively.
  • /etc/rc.d/rc.4 is only executed when runlevel 4is entered. The file runs the ``xdm'' process, to allow graphiclogin. Note that no getty is run on /dev/tty1 when in runlevel 4(but you can change this if you want).
This kind of setup is easy to understand, and you can differentiatebetween runlevels 2, 3 and 5 by adding proper ``wait'' (execute oncewaiting for termination) and ``respawn'' (execute forever) entries.By the way, if you ever guessed what ``rc'' means, it's the short formfor ``run command''. I have been editing my ``.cshrc'' and ``.twmrc''for years before being told what this arcane ``rc'' suffix is --there's something in the Unix world that is only handed on by oraltradition. I hope I'm now saving someone from years of unneededdarkness -- and I hope I won't be punished for writing it down.

The Debian way

Although simple, the Slackware way to setup /etc/inittab doesn't scalewell when you add new software packages to the system.Let's imagine, for example, that someone distribute ansshpackage (not unlikely, as ssh can't be distributed in officialdisks due to the insane US rules about crypto). The programsshd is a standalone server that must be invoked at system boot;this means that the package should patch /etc/rc.d/rc.M or one of thescripts it invokes to add ssh support. This is clearly a problem in aworld where packages are typically archives of files; add to this thatyou can't assume that rc.local is always unchanged from the stockdistribution, so even a post-install script that patches the file willmiserably fail most of the times.You should also consider that adding a new server program is only partof the job; the server must also be stopped in rc.K, rc.0 and rc.6. Asyou see, things are getting pretty tricky.The solution to this problem is both clean and elaborate. The idea isthat each package that includes a server must provide the system witha script to start and stop the service; each runlevel than will startor stop the services that are associated to that very runlevel.Associating a service and a runlevel can be as easy as creating filesin a runlevel-specific directory. This setup is common to Debian andRed Hat, and possibly other distributions that I never ran.The core of the /etc/inittab used by Debian-1.3 is shown in Listing 4.
    # The default runlevel.    id:2:initdefault:    # This is run first    si::sysinit:/etc/init.d/boot    # What to do in single-user mode.    ~~:S:wait:/sbin/sulogin    # Enter each runlevel    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    # getty    1:2345:respawn:/sbin/getty 38400 tty1   Listing 3
The Red Hat setup featuring exactly he same structure for systeminitialization but uses different pathnames; you'll be able to map onestructure over the other. Let's list the role of the different files:
  • /etc/init.d/boot Is the exact counterpart of rc.S. Ittypically checks local filesystems and mounts them, but the real thingis much more featured than that.
  • /sbin/sulogin Allows root to log in a single-userworkstation. Only shown in lising 4 because single-user modeis so important for system maintainance.
  • /etc/init.d/rc Is a script that runs any start/stopscript that belongs to the runlevel being entered.
The last item, the ``rc'' program, is the main character of thisenvironment: it's task consists in scanning the directory/etc/rc$runlevel.d invoking any script that appears in thedirectory.A stripped down version of ``rc'' would look like the following:
    #!/bin/sh    level=$1    cd /etc/rc.d/rc$level.d    for i in K*; do    $i stop    done    for i in S*; do$i start    done

What does it mean? It means that /etc/rc2.d (for example) includesfiles calledK* and S*; the former identify servicesthat must be stopped, and the latter identify services that must bestarted. Ok, but I didn't tell whence do K* and S* come from. This is the smartpart of it all: every software package that needs to be run for somerunlevel adds itself to all the /etc/rc?.d directories, either as a``start'' entry or as a ``kill'' (stop) entry. To avoid codeduplication, the package installs a script in /etc/init.d and severalsymbolic links from the various /etc/rc?.d.To show a real-life example, lets's see what is included in two``rc'' directories of debian:

    rc1.d:    K11cron         K20sendmail    K12kerneld      K25netstd_nfs    K15netstd_init  K30netstd_misc    K18netbase      K89atd    K20gpm          K90sysklogd    K20lpd          S20single    K20ppp    rc2.d:    S10sysklogd     S20sendmail    S12kerneld      S25netstd_nfs    S15netstd_init  S30netstd_misc    S18netbase      S89atd    S20gpm          S89cron    S20lpd          S99rmnologin    S20ppp

This shows how entering runlevel 1 (single-user) kills all theservices and start a ``single'' script; entering runlevel 2 (thedefault level) starts all the services. The number that appears nearthe K or the S is used to order the birth of death of the variousservices, as the shell expands wildcards appearing in /etc/init.d/rcin ascii order. Inovking anls -l command confirms that allof these files are symlinks, like the following:

    rc2.d/S10sysklogd -> ../init.d/sysklogd    rc1.d/K90sysklogd -> ../init.d/sysklogd

To summarize, adding a new software package in this environmentmeans adding a file in /etc/init.d and the proper symbolic link fromeach of the /etc/rc?.d directories. To make different runlevels behavedifferently (2, 3, 4 and 5 are configured in the same way by default),just remove or add symlinks in the proper /etc/rc?.d directories.If this scares you as too difficult, not all is lost. If you use RedHat (or Slackware), you can think of /etc/rc.d/rc.local like it wasautoexec.bat -- if you are old enough to remember about the pre-Linuxage. If you run Debian, you could create /etc/rc2.d/S95local and useit as your own rc.local; note however that Debian is very clean aboutsystem setup and I would have better not cast in print such aheresy. You know, powerful and trivial seldom match; you have beenwarned.

Debian-2.0 (hamm)

As I write this article, Debian 2.0 is being released to the public,and I suspect it will be of wide use when you read it.Although the structure of system initialization is the same, it'sinteresting to note that the developers managed to make it faster.Instead of running the files in /etc/rc2.d, the script /etc/init.d/rccan now run them in the same process, without spawning another shell;whether to execute them or source them is controlled by the filename:executables whoe name ends in.sh are sourced, the other onesare executed. The trick is shown in the following few lines, and thespeed benefit is non-negligible:

    case ``$i'' in        *.sh)            # Source shell script for speed.            (            trap - INT QUIT TSTP            set start; . $i            ) ;;        *)            # No sh extension, so fork subprocess.            $i start ;;   esac
Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.

0 0
原创粉丝点击