Gentoo Logo

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() {
  (Informacje o zależnościach)
}

start() {
  (Komendy niezbędne do uruchomienia usługi)
}

stop() {
  (Komendy niezbędne do jej wyłączenia)
}

restart() {
  (Polecenia służące do restartu usługi)
}

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    # czekaj 3 sekundy przed ponownym uruchomieniem
  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

(Kopiowanie wszystkich usług z domyślnego runlevela do runlevela offline
# cd /etc/runlevels/default
# for service in *; do rc-update add $service offline; done
(Usuwanie niechcianej usługi z runlevela offline
# rc-update del net.eth0 offline
(Wyświetlenie wszystkich aktywnych usług dla runlevela offline
# rc-update show offline
(Częściowe wyjście)
                    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"
(Następnie wpisujemy usługi, których nie chcemy uruchamiać automatycznie)
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.

Zaktualizowano 11 października 2007

Oryginalna wersja tego dokumentu została po raz ostatni zaktualizowana 17 września 2011. Jeśli chcesz pomóc w aktualizacji tego dokumentu do najnowszej wersji, skontaktuj się z Łukaszem Damentko, koordynatorem polskiego projektu tłumaczeń dokumentacji Gentoo.

Podsumowanie: Gentoo używa specjalnego formatu skryptów startowych, które pozwalają na budowanie zależności oraz zarządzanie wirtualnymi skryptami startowymi. Rozdział ten pokaże jak je tworzyć i jak nimi zarządzać.

Donate to support our development efforts.

Copyright 2001-2012 Gentoo Foundation, Inc. Questions, Comments? Contact us.