SuSE Linux: Od verze 7.0
V první části této série se budeme věnovat tzv. boot konceptu, tedy detailně si popíšeme proces od startu programu init až po přihlášení uživatele. Nebudeme se zmiňovat o tom, co se děje předtím, tj. nebudeme popisovat BIOS, LILO ani inicializaci jádra, rovnou začneme initem resp. ukážeme si místo, kde je init jádrem spuštěn a pak už budeme podrobně popisovat, co se děje.
init(8)
.
Při zavádění jádra je nutné zjistit, na jakém typu procesoru je jádro
zaváděno. Tato činnost je popsána v souboru arch/i386/kernel/head.S
.
Možná vás zmate, že tento soubor má příponu .S
a nikoli .c
a tedy není v jazyce C. O Linuxu velmi mnoho lidí tvrdí, že je napsán kompletně v C,
ale není to pravda. Zejména architekturálně závislé části musí být napsány v assembleru
a soubor arch/i386/kernel/head.S
je jednou z nich.
Téměř na konci tohoto souboru nalezneme i následující kód:
#ifdef CONFIG_SMP movb ready, %cl cmpb $1,%cl je 1f # the first CPU calls start_kernel # all other CPUs call initialize_secondary call SYMBOL_NAME(initialize_secondary) jmp L6 1: #endif call SYMBOL_NAME(start_kernel) L6: jmp L6 # main should never return here, but # just in case, we know what happens.I když je tento kód v assembleru, je poměrně jednoduché jej pochopit. Pokud je při konfiguraci linuxového jádra povolena podpora více procesorů (CONFIG_SMP), jsou ostatní procesory kromě prvního (který má číslo 0) inicializovány. První procesor vždy provede volání funkce start_kernel, která již je napsána v jazyce C a nachází se v souboru
init/main.c
linuxového jádra.
Po všech nutných inicializacích uvnitř této funkce je spuštěn proces init:
kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL); unlock_kernel(); current->need_resched = 1; cpu_idle();Ihned poté kernel přechází do stavu idle, který je realizován funkcí cpu_idle(), která je definována v souboru
arch/i386/kernel/process.c
:
/* * The idle thread. There's no useful work to be * done, so just try to conserve power and have a * low exit latency (ie sit in a loop waiting for * somebody to say that they'd like to reschedule) */ void cpu_idle (void) { /* endless idle loop with no priority at all */ init_idle(); current->nice = 20; current->counter = -100; while (1) { void (*idle)(void) = pm_idle; if (!idle) idle = default_idle; while (!current->need_resched) idle(); schedule(); check_pgt_cache(); } }Výše zmíněné volání kernel_thread(init, ...) tedy volá funkci init(), která je definována v souboru
init/main.c
. Zde je uvolněna paměť, která byla
použita při inicializačním procesu jádra, je otevřeno zařízení
/dev/console
:
if (open("/dev/console", O_RDWR, 0) < 0) printk("Warning: unable to open an initial console.\n");Odtud také plyne velmi populární hláška Warning: unable to open an initial console., která se vypíše pokud linuxové jádro nemůže otevřít soubor
/dev/console
. Po těchto činnostech se již konečně dostáváme k spuštění
vlastního procesu init:
/* * We try each of these until one succeeds. * * The Bourne shell can be used instead of init if we are * trying to recover a really broken machine. */ if (execute_command) execve(execute_command,argv_init,envp_init); execve("/sbin/init",argv_init,envp_init); execve("/etc/init",argv_init,envp_init); execve("/bin/init",argv_init,envp_init); execve("/bin/sh",argv_init,envp_init); panic("No init found. Try passing init= option to kernel.");Pokud uživatel zadá v příkazové řádce jádra parametr init= s nějakým řetězcem, je tento řetězec obsažen v proměnné execute_command (viz funkce parse_options v souboru
init/main.c
) a bude spuštěn program tohoto jména
místo /sbin/init
, který by byl spuštěn standardně.
Pokud neexistuje ani /sbin/init
, zkusí kernel spustit /etc/init
případně i /bin/init
. Pokud ani jeden z nich neexistuje, pokusí se
kernel spustit ještě příkazový interpret /bin/sh
. Pokud ani ten neexistuje,
uživatel má smůlu a uvidí na své konzoli hlášku:
No init found. Try passing init=option to kernel..
/sbin/init
a bude tedy spuštěn.
Proces init, který bude mít identifikační číslo procesu rovno jedné, což je
možné ilustrovat výpisem příkazu ps:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root
1 0.0 0.0 344 96 ? S 04:27 0:05 init [2]
Proces init je otcem všech ostatních procesů. Můžeme se o tom přesvědčit
pomocí programu pstree:
init-+-apmd |-atd |-cardmgr |-cron |-dhcpcd |-gpm |-kbgndwm |-kcmlaptop |-kflushd |-kfm-+-emacs---ispell |...Konfigurace procesu init je uložena v tzv. init-tabulce, v souboru
/etc/inittab
(viz man 5 inittab). Tento soubor je textový a je možné jej
velmi jednoduše editovat pomocí libovolného textového editoru. Řádky
začínající znakem # nebo prázdné řádky jsou ignorovány. Ostatní řádky mají
následující strukturu:
id:runlevels:action:processKaždý řádek tedy obsahuje čtyři pole (některá mohou být prázdná) oddělená znakem dvojtečka.
První políčko identifikuje řádek v rámci souboru inittab. Druhé políčko obsahuje seznam tzv. úrovní běhu (runlevel), kterých se řádek týká. Třetí políčko definuje způsob, jakým má být akce provedena. Poslední políčko definuje proces, který má být spuštěn.
Nebudeme se pouštět do větších detailů, neboť manuálová stránka souboru
inittab je velmi dobře napsána. Soustředíme se na soubor inittab a vůbec
inicializační proces distribuce SuSE Linux. Popíšeme si tedy strukturu
souboru /etc/inittab
v této distribuci a význam jednotlivých řádků.
Jedním z prvních řádků je definice standardní úrovně běhu systému.
# default runlevel id:2:initdefault:V distribuci SuSE Linux je standardní úrovní běhu úroveň 2 (pokud systémový administrátor zvolil textové přihlášení) resp. úroveň běhu 3 (pokud zvolil grafické přihlášení). U jiných distribucí může být rozložení úrovní běhu jiné - např. distribuce Red Hat Linux používá jako standardní úroveň běhu úroveň číslo 3 (resp. 5 pro grafické přihlášení).
Rozložení jednotlivých úrovní běhu je v distribuci SuSE Linux následující:
- úroveň běhu 0 znamená zastavení (halt) systému - úroveň běhu S znamená jednouživatelský režim (single-user) - úroveň běhu 1 znamená multiuživatelský režim bez podpory sítě - úroveň běhu 2 znamená multiuživatelský režim včetně podpory sítě - úroveň běhu 3 znamená multiuživatelský režim včetně podpory sítě a grafického přihlášení pomocí xdm nebo kdm - úroveň běhu 6 znamená reboot systémuOstatní úrovně běhu (4 a 5) nejsou v distribuci SuSE Linux použity.
Další řádky souboru inittab obsahují spuštění skriptu, který je startován vždy při zavedení systému:
# check system on startup # first script to be executed if not booting in emergency (-b) mode si:I:bootwait:/sbin/init.d/boot
/sbin/init.d
. V distribuci SuSE Linux jsou všechny
inicializační skripty umístěny v tomto adresáři.
Na začátku skriptu boot jsou načteny dva soubory z nichž ten druhý je v
distribuci SuSE Linux stěžejním, ale nepředbíhejme. První načtený soubor
(/etc/rc.status
) obsahuje funkce a potřebnou infrastrukturu pro startovací
skripty a zejména podporu pro barevné grafické odlišení úspěšnosti
provedení jednotlivých částí inicializace. Ihned poté je připojen souborový
systém /proc avšak zatím pouze bez zápisu do souboru /etc/mtab
,
protože ten je obsažen na kořenovém souborovém systému, který je stále připojen pouze
pro čtení:
echo -n "Mounting /proc device" mount -n -t proc proc /procNa příkladě klávesové kombinace SysRq si ukážeme, jak distribuce SuSE Linux používá konfigurační soubor
/etc/rc.config
:
# # check if sysrq should be enabled # if test -e /proc/sys/kernel/sysrq ; then if test "$ENABLE_SYSRQ" = yes ; then echo "1" > /proc/sys/kernel/sysrq else echo "0" > /proc/sys/kernel/sysrq fi fiTento kód zajistí, že pokud linuxové jádro, které je právě nabootováno, podporuje SysRq (pokud nevíte, co to je, přečtěte si soubor
Documentation/sysrq.txt
ve zdrojových textech linuxového jádra)
a pokud je nastavena proměnná ENABLE_SYSRQ na hodnotu "yes", je tato funkce jádra
povolena. Pokud je proměnná nastavena jinak (např. na hodnotu "no"), je
tato funkce zakázána. Možná vás teď napadne, kde se tato proměnná
nastavuje. A to už jsme zpět u souboru /etc/rc.config
, který mimo jiné
nastavuje i tuto proměnnou:
# # If you say yes here, you will have some control over the system even # if the system crashes for example during kernel debugging. # Please consult /usr/src/linux/Documentation/sysrq.txt # for further information. # ENABLE_SYSRQ="no"Uživatel tedy může libovolným textovým editorem změnit hodnotu této proměnné a při příštím restartu systému již tato funkce bude zapnuta.
Pokračujme dále v rozboru startovacího skriptu boot. Po zapnutí/vypnutí funkce SysRq je dále kontrolována funkce kláves Stop-A (L1-A), která na počítačích Sun SPARC umožní vstoupit do PROM monitoru. Dále zapínáme podporu Multiple Device, aktivujeme Volume groups (pokud používáme Logical Volume Manager).
Dále aktivujeme všechna odkládací zařízení zaznamenaná v souboru
/etc/fstab
:
# # fsck can need a huge amount of memory, so make sure, it is there. # echo "Activating swap-devices in /etc/fstab..." swapon -a &> /dev/nullNyní zkontrolujeme souborový systém, jestli nebyl nekorektně odpojen. Pokud byl nekorektně odpojen, je zkontrolován pomocí programu fsck. Poté se začínají dít zajímavější věci. Vyčistíme pozůstatky z předchozího běhu systému:
# # clean up # rm -f /etc/mtab* /etc/nologin /nologin /fastbootObnovíme soubor závislostí mezi moduly pomocí programu depmod a připojíme lokální souborové systémy uvedené v tabulce
/etc/fstab
:
# # Mount local filesystems in '/etc/fstab' (and create an entry # for / and /proc). # echo "Mounting local file systems..." mount -fv -t proc proc /proc rc_status mount -av -t nonfs,noproc rc_status echo "Mounting local file systems"Zde je připojen souborový systém /proc a je pro něj proveden záznam do tabulky
/etc/mtab
, protože při předchozím připojení nebylo možné provést
zápis do tohoto souboru.
Dále jsou (pokud je to nutné) obnoveny soubory se stavovými informacemi ohledně diskových kvót a obnovena vyrovnávací paměť ld.so, nastavena správná časová zóna a upraveny hodiny. Nyní se začíná inicializovat síťový subsystém - rozhraní loopback, hostname a případně i jméno domény v NIS.
Poté je provedeno konečné vyčištění systému:
#
# clean up
#
rm -f /var/lock/* /var/lock/*/* /tmp/.X*lock \
/var/run/* /var/run/*/* /var/spool/uucp/LCK* 2>/dev/null
rm -f /var/run/utmp
echo -n > /var/run/utmp
chmod 664 /var/run/utmp
chown root
.tty /var/run/utmp
Nyní jsou aktivovány další skripty, které je nutné spustit v každé úrovní
běhu - jsou to skripty v adresáři /sbin/init.d/boot.d
, které začínají
znakem S:
# # start bootup client scripts. # if test -d /sbin/init.d/boot.d/ ; then for i in /sbin/init.d/boot.d/S*; do test -f $i || continue if test -x "$i" ; then # Active boot scripts, should have their own messages $i start else # Old boot scripts, may not have any message echo Running $i /bin/sh $i b rc_status -v1 -r fi done fiStarší startovací skripty nebyly spustitelné a startovaly se s parametrem b. Novější startovací skripty se startují stejně jako ostatní skripty s parametrem start a samy vypisují informace o tom, co dělají. Mezi těmito skripty jsou např. inicializační skripty klávesnice, sériových portů, USB portů nebo např. i inicializační skript balíku sysstat (resp. programu sar).
Pokud existuje soubor /etc/isapnp.conf
a proměnná START_ISAPNP je nastavena
na hodnotu "yes", jsou také inicializována PNP zařízení:
# # setup PNP if config file is present # if test -r /etc/isapnp.conf -a -x /sbin/isapnp -a "$START_ISAPNP" = yes ; then echo "Initializing PnP devices" /sbin/isapnp /etc/isapnp.conf rc_status -v1 -r fiNa závěr inicializační fáze skriptu boot jsou ještě spuštěny příkazy, které si uživatel (resp. správce systému) má možnost sám nadefinovat v souboru
/sbin/init.d/boot.local
:
# # start user defined bootup script. # if test -f /sbin/init.d/boot.local ; then echo "Running /sbin/init.d/boot.local" /bin/sh /sbin/init.d/boot.local rc_status -v1 -r fiSoubor
boot.local
standardně neobsahuje žádné příkazy.
Skript boot dále inicializuje logovací mechanismy, zprávy o stavu zavádění
systému uloží do souboru /var/log/boot.msg
a na virtuální
konzolu číslo 10 začne logovat všechny zprávy linuxového jádra.
Pokud máme v jádře přeloženu podporu pro další binární formáty (CONFIG_BINFMT_MISC) a máme nainstalován dosemu, je zapnuta podpora spouštění DOSových programů:
# # enable DEXE binary format # if test -d /proc/sys/fs/binfmt_misc -a -x /usr/bin/dosexec; then echo -n "Registering DEXE for binfmt" echo :DEXE:M::\\x0eDEXE::/usr/bin/dosexec: > \ /proc/sys/fs/binfmt_misc/register rc_status -v1 -r fiSkript boot dále povolí/zakáže dynamic IP patch, SYN flood protection a IP forwarding a nahraje modul memstat pro potřeby programu xosview.
V úplném závěru skriptu boot
se testuje, zda se jedná o první start
systému. Pokud ano, je spuštěn program YaST k dokončení instalace.
init 3nastartuje úroveň běhu číslo 3, tudíž multiuživatelskou úroveň s podporou sítě a grafického přihlášení (xdm).
Jak se odlišují tyto úrovně a kde je definováno, které služby do které
úrovně patří? V souboru /etc/inittab
je uvedena definice jednotlivých
úrovní běhu:
# /sbin/init.d/rc takes care of runlevel handling # # runlevel 0 is halt # runlevel S is single-user # runlevel 1 is multi-user without network # runlevel 2 is multi-user with network # runlevel 3 is multi-user with network and xdm # runlevel 6 is reboot l0:0:wait:/sbin/init.d/rc 0 l1:1:wait:/sbin/init.d/rc 1 l2:2:wait:/sbin/init.d/rc 2 l3:3:wait:/sbin/init.d/rc 3 #l4:4:wait:/sbin/init.d/rc 4 #l5:5:wait:/sbin/init.d/rc 5 l6:6:wait:/sbin/init.d/rc 6O všechny úrovně běhu se tedy stará skript
/sbin/init.d/rc
, který si nyní
popíšeme.
Skript rc zkontroluje, které služby má zastavit. Jsou to služby, které jsou spuštěny v úrovni běhu $PREVLEVEL a nejsou spuštěny v nové úrovni běhu. Pokud má služba/proces běžet i v nové úrovni běhu, není samozřejmě zastavena a poté opět spuštěna. Poté jsou spuštěny služby, které mají běžet v nové úrovni běhu, ale ještě neběží (některé služby mohou běžet ve všech úrovních běhu).
Na samotném konci skriptu je systémový administrátor informován o tom, zda a které služby byly úspěšně spuštěny.
Startovací skripty jednotlivých služeb jsou fyzicky umístěny v adresáři
/sbin/init.d
- např. skript sshd slouží pro obsluhu startování a
zastavování serveru OpenSSH:
-rwxr--r-- 1Tento skript je potom nalinkován do adresáře příslušejícího dané úrovni běhu. V našem případě např. pro úroveň běhu 2:root
root
1690 Aug 6 08:13 /sbin/init.d/sshd
lrwxrwxrwx 1Pokud má skriptroot
root
7 Sep 11 18:42 /sbin/init.d/rc2.d/K20sshd -> ../sshd lrwxrwxrwx 1root
root
7 Sep 11 18:42 /sbin/init.d/rc2.d/S20sshd -> ../sshd
/sbin/init.d/rc
např. nastartovat službu sshd ve třetí
úrovni běhu, spustí příkaz:
/sbin/init.d/rc3.d/S20sshd startPokud ji má naopak zastavit, spouští příkaz:
/sbin/init.d/rc3.d/K20sshd stopFunguje to i přesto, že skripty S20sshd a K20sshd ukazují na stejný skript (
/sbin/init.d/sshd
). Tento skript si totiž kontroluje, zda je volán s
argumentem start nebo stop.
/sbin/init.d/sshd
:
#! /bin/sh # Copyright (c) 1999, 2000 SuSE GmbH Nuernberg, Germany. All rights reserved. # # Author: Thorsten KukukNa úplném začátku skriptu je načten konfigurační soubor, 1999 # . /etc/rc.config # Determine the base and follow a runlevel link name. base=${0##*/} link=${base#*[SK][0-9][0-9]} # Force execution if not called by a runlevel directory. test $link = $base && START_SSHD=yes test "$START_SSHD" = yes || exit 0 # The echo return value for success (defined in /etc/rc.config). return=$rc_done case "$1" in start) if ! test -f /etc/ssh/ssh_host_key ; then echo Generating /etc/ssh/ssh_host_key. ssh-keygen -b 1024 -f /etc/ssh/ssh_host_key -N '' fi if ! test -f /etc/ssh/ssh_host_dsa_key ; then echo Generating /etc/ssh/ssh_host_dsa_key. ssh-keygen -d -b 1024 -f /etc/ssh/ssh_host_dsa_key -N '' fi echo -n "Starting SSH daemon:" startproc /usr/sbin/sshd $SSHD_OPTS || return=$rc_failed echo -e "$return" ;; stop) echo -n "Shutting down SSH daemon:" killproc -TERM /usr/sbin/sshd || return=$rc_failed echo -e "$return" ;; restart) $0 stop && $0 start || return=$rc_failed ;; reload) echo -n "Reload service sshd" killproc -f /var/run/sshd.pid -HUP /usr/sbin/sshd || return=$rc_failed echo -e "$return" ;; status) echo -n "Checking for service sshd: " checkproc /usr/sbin/sshd && echo OK || echo No process ;; probe) test /etc/ssh/sshd_config -nt /var/run/sshd.pid && echo reload ;; *) echo "Usage: $0 {start|stop|status|restart|reload|probe}" exit 1 esac # Inform the caller not only verbosely and set an exit status. test "$return" = "$rc_done" || exit 1 exit 0
/etc/rc.config, který obsahuje definici proměnných START_SSHD a SSHD_OPTS:
# # Start the ssh daemon ? (yes/no) # START_SSHD="yes" # # Options for sshd # SSHD_OPTS=""Pokud je proměnná START_SSHD různá od "yes", je spouštění skriptu ukončeno. Toto je ta správná, korektní metoda pro zastavení poskytování jednotlivých služeb. Program YaST určený pro správu systému potom obsahuje podporu pro nastavování těchto proměnných jinak než pomocí textového editoru.
Potom se skript rozhoduje podle prvního argumentu, co vlastně bude dělat. Pokud je prvním argumentem řetězec start, je daná služba nastartována. Některé služby stejně jako v našem případě provádějí při startu další činnosti. Sshd potřebuje ke svému startu a provozu vygenerované klíče, které jsou (pokud ještě neexistují) vygenerovány. Pokud je prvním argumentem řetězec stop, je služba zastavena. Pokud je argumentem řetězec reload, je démonu zajišťujícímu danou službu poslán signál, aby načetl znovu konfiguraci (může se lišit u jednotlivých služeb).
Startovací skripty dále podporují i kontrolu stavu služby pomocí argumentu status. Pokud služba běží, je odpovědí
Checking for service sshd: OKjinak
Checking for service sshd: No processNaše služba sshd navíc podporuje argument probe, kdy se testuje, zda byl konfigurační soubor démona sshd změněn od jeho posledního startu a pokud ano, je tento konfigurační soubor znovu načten.
Pro elegantnější práci jsou v distribuci SuSE Linux i symbolické odkazy na
startovací skripty v adresáři /usr/sbin
případně v /sbin
:
lrwxrwxrwx 1Link rcsshd tedy ukazuje na příslušný startovací skript. Můžeme tedy jednoduše službu zastavit pomocí jednoduchého příkazu:root
root
22 Sep 11 18:42 /usr/sbin/rcsshd -> ../../sbin/init.d/sshd
rcsshd stopa nemusíme psát plnou cestu ke skriptu:
/sbin/init.d/sshd stopDále je v distribuci pro přehlednost a kompatibilitu s dalšími distribucemi několik symbolických odkazů, které zjednodušují práci distributorům dalšího programového vybavení:
lrwxrwxrwx 1Tento symbolický odkaz umožní instalaci startovacích služeb stejně jako je tomu i u jiných distribucí. Potom můžeme již zmiňované ssh spustit i např. takto:root
root
14 Sep 11 18:20 /etc/rc.d -> ../sbin/init.d
/etc/rc.d/init.d/sshd startAle je otázkou, zda je to rychlejší a elegantnější než prosté rcsshd start.
Pokud někdy v budoucnu budete chtít vytvořit vlastní inicializační skript,
podívejte se zejména na soubor /sbin/init.d/skeleton
, který je
šablonou pro nové soubory. Velmi důležitým je také skript rctab(8)
,
který slouží k hlubší správě služeb jednotlivých úrovní běhu. Další informace k systému
zavádění SuSE Linuxu naleznete také v manuálové stránce init.d(7)
.
/etc/inittab
:
1:123:respawn:/sbin/mingetty --noclear tty1 2:123:respawn:/sbin/mingetty tty2 3:123:respawn:/sbin/mingetty tty3 4:123:respawn:/sbin/mingetty tty4 5:123:respawn:/sbin/mingetty tty5 6:123:respawn:/sbin/mingetty tty6Standardně je spuštěno šest virtuálních konzolí, z nichž první nesmaže hlášky ze startovacího procesu, protože by pak systémový administrátor nemusel stihnout všechny přečíst. Pokud budete chtít více konzolí, stačí přidat příslušné řádky.
Na každé virtuální konzoli běží program mingetty, který po zadání uživatelského jména spustí program login a uživateli je tak umožněno se přihlásit.
# what to do when CTRL-ALT-DEL is pressed ca::ctrlaltdel:/sbin/shutdown -h -t 4 nowObsah tohoto řádku lze systémově ovlivnit pomocí nastavení proměnné CONSOLE_SHUTDOWN na jednu z hodnot ignore (klávesová kombinace bude ignorována), halt (shutdown systému), reboot (restart systému). Soubor inittab bude poté upraven pomocí skriptu SuSEconfig. Administrativní rozhraní YaST dokonce umožňuje tuto změnu provést pomocí uživatelského rozhraní (a samozřejmě umožňuje spravovat i další proměnné).
Balík kbd přibližně od verze 0.90 umožňuje nadefinovat ještě další
speciální kombinaci, která vyvolá tzv. Keyboard Request. Ve standardní
klávesové mapě (/etc/defkeymap.map
) je tento požadavek nadefinován takto:
keycode 103 = Up alt keycode 103 = KeyboardSignalTedy jinými slovy, pokud na konzole SuSE Linuxu stisknete klávesu s šipkou nahoru a současně s ní klávesu Alt, bude vyvolána akce kbrequest, kterou je možno nakonfigurovat v souboru
/etc/inittab
:
# special keyboard request (Alt-UpArrow) # look into the kbd-0.90 docs for this kb::kbrequest:/bin/echo "Keyboard Request -- edit /etc/inittab to let this work."Standardně je tedy pouze vypsán text informující o možnosti konfigurace této klávesové kombinace.
V příští části se budeme věnovat centrálnímu skriptu SuSEconfig a
konfiguračnímu souboru /etc/rc.config
.