|
1.
Poziomy działania
Uruchamianie systemu operacyjnego
Podczas uruchamiania systemu operacyjnego na ekranie pojawia się dużo, nie
zawsze zrozumiałego tekstu. Gdy przyjrzeć się dokładniej, można zauważyć, że
tekst ten jest za każdym razem taki sam. Cały ten proces nazywamy sekwencją
startową, która (w większym lub mniejszym stopniu) jest skonfigurowana
statycznie.
Najpierw bootloader ładuje obraz jądra systemu do pamięci i zleca
procesorowi jego wykonanie. W chwili kiedy jądro zostanie załadowane i
wykonane, uruchamiane są specyficzne zadania, związane ściśle z jądrem po czym
uruchamiany jest proces init.
Proces ten następnie upewnia się czy wszystkie systemy plików (zdefiniowane w
/etc/fstab) zostały poprawnie zamontowane i są gotowe do pracy.
Następnie uruchamiane są poszczególne skrypty umieszczone w katalogu
/etc/init.d, które mają za zadanie uruchomić kolejno wszystkie
usługi niezbędne do poprawnego działania systemu.
Na koniec, kiedy wszystkie skrypty zostaną wykonane, init aktywuje
terminale (w większości przypadków są to po prostu wirtualne konsole, między
którymi można się przełączać za pomocą kombinacji klawiszy Alt-F1,
Alt-F2 itd.) przy pomocy służącego do tego programu pod nazwą
agetty. Sprawdza on czy użytkownik może się zalogować na dany terminal,
uruchamiając login.
Skrypty Init
Skrypty umieszczone w katalogu /etc/init.d nie są uruchamiane
przez init w przypadkowej kolejności. Co więcej, nie są uruchamiane
wszystkie naraz lecz w określonej kolejności. Informacje na ten temat tej
kolejności pobierane są z katalogu /etc/runlevels.
Na samym początku init inicjuje te skrypty, do których dowiązania
symboliczne znajdują się w katalogu /etc/runlevels/boot.
Zazwyczaj uruchamiane są one w kolejności alfabetycznej. Wyjątek stanowią te,
które posiadają informacje o zależnościach. Mówią one o tym, że do
prawidłowego działania danej usługi musi wcześniej zostać uruchomiona inna.
Kiedy skrypty mające dowiązanie w /etc/runlevels/boot zostaną
uruchomione, init kontynuuje uruchamianie tych, do których dowiązania
znajdują się w katalogu /etc/runlevels/default. Podobnie jak w
poprzednim przypadku, uruchamiane są w kolejności alfabetycznej. Wyjątek
stanowią tylko sytuacje, gdy muszą zostać spełnione zależności niezbędne do
poprawnego przeprowadzenia procesu startowego.
Jak działa Init
Oczywiście o wszystkim nie decyduje sam init. Potrzebuje on stosownego
pliku konfiguracyjnego, który zawiera informacje o zadaniach jakie ma wykonać.
Ten plik to /etc/inittab.
Na początku tej części dokumentu jest wzmianka o tym, że init w
początkowej fazie działania sprawdza czy systemy plików zostały zamontowane
poprawnie. Definicja tego zadania w /etc/inittab wygląda
następująco:
Listing 1.1: Inicjacja systemu w /etc/inittab |
si::sysinit:/sbin/rc sysinit
|
Powyższa linia mówi procesowi init, że w celu inicjacji systemu ma
wykonać polecenie /sbin/rc sysinit. Tak naprawdę to właśnie skrypt
/sbin/rc zajmuje się inicjacją, a init jedynie zleca
zadania innym procesom.
Następnie init uruchamia wszystkie skrypty, do których dowiązania
symboliczne znajdują się we wspomnianym wcześniej katalogu
/etc/runlevels/boot. W pliku konfiguracyjnym jest to zdefiniowane
w następujący sposób:
Listing 1.1: Kontynuacja procesu uruchamiania systemu |
rc::bootwait:/sbin/rc boot
|
Ponownie skrypt rc wykonuje niezbędne zadania. Argument dla rc
(boot) jest taki sam jak nazwa podkatalogu w
/etc/runlevels, w którym znajdują się dowiązania do skryptów
wykonywanych w tej części procesu uruchamiania systemu.
Następnie init sprawdza plik /etc/inittab w celu odszukania
informacji, w który poziom działania (runlevel) ma "wejść" system:
Listing 1.1: Linia initdefault |
id:3:initdefault:
|
W tym przypadku jest to poziom (runlevel) o numerze 3. Dzięki tej
informacji init sprawdza co musi zostać uruchomione aby system zaczął
działać w trzecim poziomie (rulevelu 3).
Listing 1.1: Definicja poziomów działania |
l0:0:wait:/sbin/rc shutdown
l3:3:wait:/sbin/rc default
l4:4:wait:/sbin/rc default
l5:5:wait:/sbin/rc default
l6:6:wait:/sbin/rc reboot
|
W linii, która definiuje trzeci poziom, podobnie jak w przypadku poprzednich,
jest odwołanie do skryptu rc. Tym razem jest on uruchamiany z argumentem
default. Argument ten brzmi tak samo, jak nazwa jednego z podkatalogów w
/etc/runlevels.
Po tym jak rc zakończy swoją pracę, init decyduje o tym jakie,
oraz przy użyciu jakich poleceń, mają zostać aktywowane wirtualne konsole.
Listing 1.1: Definicja konsol wirtualnych |
c1:12345:respawn:/sbin/agetty 38400 tty1 linux
c2:12345:respawn:/sbin/agetty 38400 tty2 linux
c3:12345:respawn:/sbin/agetty 38400 tty3 linux
c4:12345:respawn:/sbin/agetty 38400 tty4 linux
c5:12345:respawn:/sbin/agetty 38400 tty5 linux
c6:12345:respawn:/sbin/agetty 38400 tty6 linux
|
Co to jest poziom działania (runlevel)?
Init używając notacji, w której każdy poziom działania ma swój numer
decyduje o tym, który z nich ma być w danej chwili aktywny. Poziom działania
(runlevel) to stan, w którym uruchomiony jest system operacyjny. Każdy z
poziomów charakteryzuje się pewnym zestawem skryptów, które muszą być wykonane
podczas wchodzenia lub wychodzenia z danego poziomu.
Gentoo posiada siedem zdefiniowanych poziomów: trzy wewnętrzne i cztery
definiowane przez użytkownika. Wewnętrzne nazywają się sysinit,
shutdown i reboot. Jak nietrudno się domyślić służą one kolejno
do inicjacji, wyłączania oraz ponownego uruchamiania systemu.
Poziomy definiowane przez użytkownika związane są z podkatalogami
/etc/runlevels: boot, default,
nonetwork i single. W poziomie boot
uruchamiane są wszystkie niezbędne usługi systemowe używane w pozostałych
poziomach. Pozostałe trzy różnią się rodzajem uruchamianych usług:
default służy do uruchamiania "standardowych" operacji,
nonetwork wykorzystywany jest w przypadkach kiedy do uruchomienia
danej usługi nie jest wymagane połączenie z siecią, zaś single
używany jest tylko wtedy, gdy system wymaga naprawy.
Praca ze skryptami Init
Skrypty uruchamiane przez proces rc nazywane są skryptami init
(ang. init scripts). Umieszczone są w katalogu /etc/init.d i mogą
być uruchamiane wraz z następującymi argumentami: start, stop,
restart, pause, zap, status, ineed,
iuse, needsme, usesme lub broken.
Aby uruchomić, zatrzymać lub przeładować dowolną usługę (wraz z powiązanymi z
nią innymi usługamii) należy użyć odpowiednio start, stop i
restart. Przykładowo:
Listing 1.1: Uruchamianie Postfixa |
# /etc/init.d/postfix start
|
Uwaga:
Wyłączane lub przeładowywane są tylko te usługi,, które tego wymagają. Inne,
które są powiązane z przeładowywaną usługą, jeśli nie ma takiej potrzeby, nie
są restartowane.
|
Aby wyłączyć daną usługę pozostawiając przy życiu usługi z nią powiązane należy
użyć argumentu pause:
Listing 1.1: Wyłączanie Postfixa, zachowując włączone powiązane z nim usługi |
# /etc/init.d/postfix pause
|
Aby zobaczyć jaki status ma aktualnie dana usługa (włączony, wyłączony...)
trzeba użyć argumentu status:
Listing 1.1: Informacje o statusie postfixa |
# /etc/init.d/postfix status
|
Jeśli powyższe polecenie zwróci informację, że Postfix jest uruchomiony, lecz
faktycznie będzie inaczej, należy użyć argumentu zap w celu
uaktualnienia informacji o statusie.
Listing 1.1: Uaktualnienie informacji o statusie postfixa |
# /etc/init.d/postfix zap
|
Do sprawdzenia jakie zależności posiada usługa trzeba użyć argumentu
iuse lub ineed. Dzięki ineed można uzyskać listę tych,
które są niezbędne do prawidłowego działania danej usługi. iuse z kolei
pokazuje te usługi, które mogą być używane lecz nie są
niezbędne do jego uruchomienia i poprawnego funkcjonowania.
Listing 1.1: Zapytanie o listę usług niezbędnych do działania Postfixa |
# /etc/init.d/postfix ineed
|
Podobnie można zapytać, które z usług w systemie wymagają danej usługi
(needsme) lub mogą lecz nie muszą go używać (usesme):
Listing 1.1: Zapytanie o listę usług, które wymagają Postfixa |
# /etc/init.d/postfix needsme
|
Ostatnią z możliwości jest użycie argumentu, który wyświetli listę brakujących
z listy wymaganych usług.
Listing 1.1: Zapytanie o listę brakujących usług powiązanych z Postfixem |
# /etc/init.d/postfix broken
|
1.
Praca z rc-update
Co to jest rc-update?
W celu ustalenia poprawnej kolejności uruchamiania usług system init w Gentoo
korzysta z drzewa zależności. Utrzymanie i zarządzanie takim drzewem bez użycia
dodatkowych narzędzi byłoby bardzo nudnym i stosunkowo trudnym zadaniem. Na
szczęście w Gentoo są już gotowe narzędzia, które znacznie ułatwiają
zarządzanie poziomami działania oraz skryptami init.
Przy pomocy rc-update można dodawać i usuwać skrypty init z poziomu
działania (runlevela). rc-update za każdym razem zleca skryptowi
depscan.sh odbudowanie na nowo wspomnianego drzewa zależności.
Dodawanie i usuwanie usług
Pierwsze usługi są dodawane do poziomów działania już podczas procesu
instalacyjnego. Wówczas można było nie skojarzyć czym jest na przykład poziom o
nazwie "default", teraz powinno to być jasne. W celu dodania lub usunięcia
usługi, rc-update wymaga podania między innymi argumentu określającego
akcję (co rc-update ma zrobić): add, del lub show.
Zatem w celu dodania lub usunięcia skryptu init, należy wykonać polecenie
rc-update wraz z argumentami add lub del, podając dalej
nazwę skryptu oraz poziomu. Na przykład:
Listing 1.1: Usuwanie Postfixa z poziomu default |
# rc-update del postfix default
|
Polecenie rc-update -v show pokazuje listę wszystkich dostępnych skryptów
wraz z informacją w którym z poziomów są one uruchamiane:
Listing 1.1: Informacje o dostępnych skryptach init |
# rc-update -v show
|
Można również wykonać polecenie rc-update show (bez -v) aby
zobaczyć jakie skrypty są uruchomione w jakim poziomie.
1.
Konfiguracja usług
Dlaczego dodatkowa konfiguracja jest potrzebna?
Skrypty init mogą być niekiedy dość skomplikowane. Dlatego część użytkowników
nie jest zbytnio zainteresowana ich edytowaniem i modyfikacją z uwagi na
możliwość popełnienia błędów. Jednak czasami możliwość zmiany konfiguracji
usługi jest bardzo ważna. Na przykład w momencie kiedy zaistnieje potrzeba
samodzielnego dodania jakiejś opcji.
Drugim powodem, dla którego ingerencja w skrypty init może okazać się pomocna
jest możliwość uaktualnienia skryptów bez obawy przed tym, że dokonane zmiany
nie zostaną zastosowane.
Katalog /etc/conf.d
Gentoo umożliwia bardzo prosty sposób konfiguracji poszczególnych usług. Każdy
skrypt init może być skonfigurowany za pomocą stosownego pliku w katalogu
/etc/conf.d. Na przykład skrypt apache2
(/etc/init.d/apache2) posiada swój własny plik konfiguracyjny,
/etc/conf.d/apache2, w którym można umieścić wszelkie opcje z
jakimi ma się uruchomić serwer Apache 2:
Listing 1.1: Zmienna zdefiniowana w pliku /etc/conf.d/apache2 |
APACHE2_OPTS="-D PHP5"
|
Plik konfiguracyjny zawiera zmienne (podobnie jak /etc/make.conf),
czyniąc konfigurację serwisów bardzo łatwą. Dostarcza nam to także więcej
informacji na temat zmiennych (jako komentarz).
1.
Pisanie skryptów Init
Czy muszę to robić?
Nie. Zwykle umiejętność pisania skryptów dla inita nie jest wymaganą
umiejętnością ponieważ wraz z dystrybucją Gentoo dostarczane są wszystkie
niezbędne skrypty, które pozwalają na uruchamianie wszystkich usług. Aczkolwiek
umiejętność ta może okazać się przydatna, kiedy zainstalowana zostanie w
systemie usługa, bez użycia do tego Portage. Wówczas będzie trzeba napisać
skrypt samodzielnie.
Nie można używać skryptów init, które nie są napisane specjalnie dla Gentoo:
skrypty init w Gentoo nie są kompatybilne ze skryptami z innych dystrybucji!
Szablon
Poniżej znajduje się szablon skryptu init.
Listing 1.1: Szablon skryptu init |
#!/sbin/runscript
depend() {
}
start() {
}
stop() {
}
restart() {
}
|
Każdy skrypt wymaga zdefiniowanej funkcji start(). Pozostałe
funkcje są opcjonalne.
Zależności
W tym miejscu można zdefiniować dwa rodzaje zależności: use i
need. Wspomniane wcześniej need są bardziej restrykcyjne niż
zależności zdefiniowane jako use. Należy wybrać i dodać tu stosowne
usługi, od których zależna będzie ta, dla której piszemy skrypt. Można też
zdefiniować zależności wirtualne.
Zależności wirtualne to takie zależności, w których nie określą się
ściśle konkretnej usługi. Przykładowo skrypt init wymaga działającego systemu
logowania, lecz nie jest jasno określone jakiego. W Gentoo dostępnych jest
kilka systemów logowania (metalogd, syslog-ng, sysklogd, ...). Zdefiniowanie
każdego z nich (zainstalowanie i uruchomienie wszystkich wymienionych wyżej
systemów logowania nie wydaje się być najlepszym pomysłem) nie było by dobrym
rozwiązaniem. Jak można się jednak przekonać wszystkie z tych usług są
akceptowane dzięki zależnościom wirtualnym.
Rzućmy okiem na informacje o zależnościach dla usługi Postfix.
Listing 1.1: Zależności Postfixa |
depend() {
need net
use logger dns
provide mta
}
|
Jak widać, postfix:
-
wymaga usługi net (jest to zależność wirtualna, która może być
spełniona przykładowo przez /etc/init.d/net.eth0).
-
współpracuje z usługą logger (jest to zależność wirtualna, którą
spełnia przykładowo /etc/init.d/syslog-ng).
-
współpracuje z usługą dns (zależność wirtualna, którą spełnia
przykładowo /etc/init.d/named).
-
zapewnia usługę mta (zależność wirtualna, którą spełniają wszystkie
serwery pocztowe).
Kontrola kolejności
Czasami nie potrzeba osobnego skryptu inicjującego. Chcemy jednak, aby usługa
była uruchamiana przed (lub po) uruchomieniem innej usługi,
jeśli ta jest dostępna w systemie i uruchamia się na tym samym
poziomie działania. Informacji na ten temat możesz dostarczyć skryptowi za
pomocą opcji before lub after.
Przyjrzyjmy się bliżej ustawieniom usługi Portmap:
Listing 1.1: Funkcja depend() usługi Portmap |
depend() {
need net
before inetd
before xinetd
}
|
Można użyć znaku "*" aby objąć wszystkie usługi w tym samym poziomie działania,
nie jest to jednak zalecana metoda.
Listing 1.1: Uruchamianie skryptu init jako pierwszego w poziomie działania |
depend() {
before *
}
|
Jeżeli nasza usługa musi posiadać prawo zapisywania na lokalnym dysku będzie
potrzebowała localmount. Jeżeli zapisuje cokolwiek w katalogu
/var/run, na przykład pliki pid, powinna zostać uruchomiona po
bootmisc:
Listing 1.1: Przykładowa funkcja depend() |
depend() {
need localmount
after bootmisc
}
|
Funkcje standardowe
Do tego aby funkcja depend() spełniała swoje zadanie, potrzebna jest
poprawna definicja funkcji start(). Funkcja ta zawiera polecenia
niezbędne do uruchomienia usługi. Wskazane jest użycie opcji ebegin i
eend, dzięki którym można poinformować użytkownika co się w danym
momencie dzieje:
Listing 1.1: Przykład funkcji start() |
start() {
ebegin "Uruchamiam moja_usługa"
start-stop-daemon --start --exec /path/to/my_service \
--pidfile /path/to/my_pidfile
eend $?
}
|
Zarówno --exec jak i --pidfile powinny zostać użyte w funkcjach
start i stop. Jeżeli usługa nie tworzy pliku pid, należy użyć parametru
--make-pidfile jeśli jest to możliwe. Należy jednak dla pewności
przetestować możliwość użycia tego parametru. Możemy również dodać parametr
--quite do opcji start-stop-daemon, jednak nie jest to działanie
zalecane.
Uwaga:
Należy się upewnić, że parametr --exec wywołuje usługę, a nie skrypt
bash, który po uruchomieniu tej usługi wyłącza sie. Tak zachowują się skrypty
init.
|
Jeżeli chcemy przejrzeć więcej przykładów funkcji start(), powinniśmy
przeczytać kody źródłowe skryptów init dostępne w naszym katalogu
/etc/init.d.
Pozostałe funkcje jakie można definiować to: stop() i restart().
Nie są one jednak konieczne! System init jest dostatecznie inteligentny aby
poradzić sobie z ich brakiem dzięki start-stop-daemon.
Mimo że nie musimy tworzyć funkcji stop() poniżej znajdziemy
przykład jak ją napisać:
Listing 1.1: Przykładowa funkcja stop() |
stop() {
ebegin "Stopping my_service"
start-stop-daemon --stop --exec /path/to/my_service \
--pidfile /path/to/my_pidfile
eend $?
}
|
Jeżeli nasza usługa uruchamia inny skrypt (na przykład, bash, pythona, perl), a
ten skrypt w czasie działania zmienia nazwę (na przykład foo.py na
foo), będziemy musieli dodać parametr --name do
start-stop-daemon. Musimy określić nazwę jaką będzie miał skrypt po
zmianie. W przykładzie usługa uruchamia skrypt foo.py, który później
zmienia nazwę ma foo:
Listing 1.1: Usługa, która uruchamia skrypt foo |
start() {
ebegin "Starting my_script"
start-stop-daemon --start --exec /path/to/my_script \
--pidfile /path/to/my_pidfile --name foo
eend $?
}
|
start-stop-daemon posiada znakomity manual opisujący dokładnie wszelkie
opcje:
Listing 1.1: Uruchamianie manuala dla start-stop-daemon |
$ man start-stop-daemon
|
Składnia skryptów startowych Gentoo opierają się na bashu przez co można w nich
używać instrukcji zgodnych z bashem.
Dodawanie niestandardowych opcji
Jeśli chcemy aby init posiadał więcej opcji niż te dotychczas omówione, należy
dodać nową opcję do zmiennej opts i stworzyć funkcję o takiej samej
nazwie. Na przykład, aby utworzyć opcję o nazwie restartdelay:
Listing 1.1: Dodanie opcji restartdelay |
opts="${opts} restartdelay"
restartdelay() {
stop()
sleep 3
start()
}
|
Zmienne konfiguracyjne dla usług
Aby skrypt uruchamiający daną usługę sięgał do plików konfiguracyjnych,
zlokalizowanych w /etc/conf.d nie trzeba praktycznie robić
niczego. W chwili kiedy skrypt zostanie uruchomiony, przetworzone zostaną
następujące pliki:
- /etc/conf.d/<nasz skrypt init>
- /etc/conf.d/basic
- /etc/rc.conf
Jeśli skrypt zawiera jakieś zależności wirtualne (np. takie jak net),
pliki związane z tymi zależnościami (w tym przypadku
/etc/conf.d/net) także zostaną przetworzone.
1.
Zmiana zachowania poziomu działania
Kto może mieć z tego korzyści?
Wielu użytkowników laptopów zna taką sytuację: dopiero po powrocie do domu chcą
uruchomić net.eth0, gdyż gdy są w drodze, to i tak nie ma sensu go
uruchamiać, bo i tak nie ma dostępu do sieci. W Gentoo można dowolnie
modyfikować zachowanie poziomów działania.
Możemy, na przykład utworzyć drugi "domyślny" poziom, który używa innych
skryptów startowych. Można wybierać, którego poziomu działania chce się używać
podczas startu systemu.
Używanie softlevela
Po pierwsze, trzeba utworzyć katalog poziomów działania dla swojego drugiego
"domyślnego" poziomu działania. Jako przykład stworzymy katalog
offline:
Listing 1.1: Tworzenie katalogu poziomu działania |
# mkdir /etc/runlevels/offline
|
Należy dodać potrzebne skrypty startowe do nowo utworzonego katalogu. Na
przykład, jeżeli chcemy mieć dokładną kopię aktualnego domyślnego
poziomu działania, wyłączając net.eth0:
Listing 1.1: Dodawanie potrzebnych skryptów startowych |
# cd /etc/runlevels/default
# for service in *; do rc-update add $service offline; done
# rc-update del net.eth0 offline
# rc-update show offline
acpid | offline
domainname | offline
local | offline
net.eth0 |
|
Nawet jeśli net.eth0 zostanie usunięte z poziomu uruchomieniowego
offline, udev będzie próbował uruchomić urządzenia, które wykryje, a
następnie uruchomi potrzebne usługi. Dlatego należy dodać każdą usługę sieciową,
której nie chcemy startować (sposób ten działa również w przypadku, innych
urządzeń wykrywanych przez udev), do pliku /etc/conf.d/rc, według
podanego poniżej wzoru.
Listing 1.1: Wyłączanie usług przy pomocy pliku /etc/conf.d/rc |
RC_COLDPLUG="yes"
RC_PLUG_SERVICES="!net.eth0"
|
Uwaga:
Aby dowiedzieć się więcej na ten temat, należy przeczytać komentarze znajdujące
się wewnątrz pliku /etc/conf.d/rc.
|
Teraz należy wyedytować pliki konfiguracyjne bootloadera i dodać wpis dla
poziomu działania offline. Dla przykładu w
/boot/grub/grub.conf:
Listing 1.1: Dodawanie wpisu dla poziomu działania offline |
title Gentoo Linux Tryb Offline
root (hd0,0)
kernel (hd0,0)/kernel-2.4.25 root=/dev/hda3 softlevel=offline
|
Teraz już jest wszystko ustawione. Jeżeli system zostanie uruchomiony i wybrana
zostanie dodana przed chwilą pozycja, zamiast domyślnego poziomu
działania będzie używany poziom offline.
Używanie bootlevela
Używanie bootlevela jest analogiczne do softlevela. Jedyną
różnicą jest definiowanie drugiego "rozruchowego" poziomu uruchamiania zamiast
drugiego "domyślnego" poziomu uruchamiania.
|