Start systemu Slackware Linux – skrypty i daemony
Napisał: Patryk Krawaczyński
13/04/2010 w Administracja Brak komentarzy. (artykuł nr 247, ilość słów: 2768)
N
a początku warto powiedzieć, że proces rozruchu systemu operacyjnego Linux na różnych platformach sprzętowych jest taki sam. Różnice wynikające z architektury danego sprzętu nie mają znaczenia (od momentu ładowania systemu – nieznaczne różnice można zauważyć podczas startu samego komputera).
Zaczynając od włączenia zasilania Basic Input Output System (BIOS) wykonuje Power On Self Test (POST), następnie odczytywany jest Master Boot Record (MBR) zawierający program rozruchowy np. Lilo lub inny (np. GRUB). Program rozruchowy zawiera menu proponujące użytkownikowi wybór oraz ustanowienie parametrów startu systemu, czyli po prostu wybór jądra – ponieważ można powiedzieć, że jądro systemu to Linux. Jest ono rdzeniem całej tej otoczki, którą nazywamy oprogramowaniem systemowym. Po wyborze jądra (jego wersji) – następuje jego załadowanie do pamięci – sprawdzane są zainstalowane urządzenia sprzętowe (rezultaty wyświetlane są na ekranie monitora), montowany jest główny system plików wraz z drzewem systemowym:
./ - główny system plików | +-/bin - programy uznane za niezbędne. +-/boot - obraz jądra i statyczne pliki boot loadera. +-/sbin - binarne pliki systemu (tylko dla administratora). +-/lib - biblioteki programowe i moduły jądra. +-/etc - pliki konfiguracyjne hosta, sieci, X11, poczty itp. +-/home - tutaj żyją użytkownicy (włączając administratora). +-/lost+found - odzyskane pliki (przez e2fsck). +-/mnt - miejsce na montowanie innych systemów plików. +|- cdrom |- flash |- floppy |- ... +-/srv - katalog dla oprogramowania serwerowego. +-/sys - od jądra 2.6 - interfejs zmiany jego parametrów. +-/tmp - tutaj są przechowywane i kasowane tymaczasowe pliki. +-/opt - oprogramowanie dodatkowe nie wchodzące w skład systemu. +-/proc - wirtualny system plików (informacje na temat procesów, jądra, urządzeń). +-/root - tutaj pracuje administrator. +-/usr - więcej oprogramowania, bibliotek i dokumentacji. +|- X11R6 - oprogramowanie X Window. |- bin - więcej oprogramowania uznanego za niezbędne. |- dict - słowniki systemowe. |- doc - faqi, jak-to-zrobić, dokumentacje programów. |- etc - pliki konfiguracyjne programów. |- games - gry, gry, gry! |- include - pliki nagłówkowe wykorzystywane do programowania w C. |- info - informacje GNU. |- lib - więcej bibliotek systemowych. |- libexec |- local - oprogramowanie pozasystemowe nie załączone na CD-ROMie. | +|- bin | |- doc | |- etc | |- games | |- info | |- lib | |- man | |- sbin | |- src - źródła programów. | |- ... |- man - strony manualne. | +|- man1..9n |- sbin - więcej systemowych plików binarnych administratora. |- share - |- src - źródła samego Linuksa - czyli jądro. | +|- linux -> linux-2.6.31337 | |- linux-2.6.31337 |- tmp -> ../var/tmp +-/var - logi systemowe, poczta i inne ważne pliki. +|- cache - zcacheowane dane aplikacji. |- lock - pliki blokujące współdzielone zasoby. |- log - logi systemowe. |- mail - poczta systemowa. |- run - aktualnie działające programy i ich identyfikatory procesów. |- spool - dane oczekujące / w trakcie przetwarzania. |- yp - dane przewidziane dla serwisów NIS. +-...
Następnie zostaje uruchomiony pierwszy proces (ma on identyfikator procesu PID o wartości 1), który nazywa się /sbin/init (lub gdy go nie ma szukane są /etc/init ewentualnie /bin/bash). Bezpośrednio po uruchomieniu ładuje on plik /etc/inittab (plik konfiguracyjny demona init), po czym wykonuje akcje, które w tym pliku zostały opisane (np. odpala procesy getty). Linux może pracować w kilku trybach pracy oznaczonych numerami od 0 do 6. W dokumentacji są określone jako runlevels (poziomy pracy systemu). Dzięki nim możemy pogrupować programy działające rezydentnie w systemie (aby je uruchamiać / zatrzymywać całymi grupami). To dzięki poziomom pracy możemy również zamknąć system. Znaczenie poszczególnych poziomów jest następujące:
- 0* (halt) – tryb zamknięcia systemu,
- 1 (single user mode) – tryb jednoużytkowy, działają tylko serwisy niezbędne do pracy systemu,
- 2 – nieużywany – lecz tak samo skonfigurowany jak poziom 3,
- 3 (multiuser mode) – tryb wieloużytkowy, działają wszystkie serwisy sieciowe, które są zainstalowane,
- 4 (session managers) – uruchomione zostanie środowisko graficzne X11,
- 5 – nieużywany – lecz tak samo skonfigurowany jak poziom 3,
- 6* (reboot) – restart systemu.
Warto wspomnieć, że Slackware używa stylu botowania systemu pochodzącego z BSD. Inne dystrybcje zazwyczaj korzystają z systemu System V stworzonego przez Miquel’a van Smoorenburg’a. Styl botowania BSD jest systemem, gdzie wszystkie startowe / kończące skrypty znajdują się w jednym katalogu /etc/rc.d
. Dla każdego poziomu pracy jest odnośnik do odpowiedniego uruchamiającego dany poziom (/etc/rc.d/rc.1, /etc/rc.d/rc.2 itd.) podczas, gdy System V posiada dla każdego poziomu oddzielny katalog, w którym znajdują się ponumerowane skrypty. W przypadku stylu BSD, każdy z tych skryptów jest i posiada w sobie odnośniki do odpowiednich demonów oraz programów, a także innych, kolejnych skryptów, które posiadają podobną zawartość. Poziomy, które zostały wyróżnione gwiazdkami są poziomami specjalnymi, ponieważ odpowiedzialne są za restart i prawidłowe zamknięcie systemu. Jeśli chcemy przełączać się pomiędzy danymi trybami pracy systemu to służy do tego komenda init:
init 1
Spowoduje ona uruchomienie trybu pojedynczego użytkownika, zaś:
init 3
Spowoduje powrót do standardowego trybu pracy. Wynika to z tego, że plik /etc/inittab zawiera informacje o tym, jak system ma reagować w odpowiedzi na określone zdarzenia (np. zmiana trybu pracy, wciśnięcie kombinacji klawiszy ALT+CTRL+DEL). Dalsza analiza tego pliku pozwoli nam poznać, które skrypty za co są odpowiedzialne oraz jak zbudowana jest prawidłowa sekwencja danego trybu systemu. Plik ten składa się z ciągu linii o następującej budowie:
id:runlevels:action:process
Gdzie:
- Pole “ID” – jest unikalnym łańcuchem 1 do 4 znaków (identyfikatorem), opisującym całą linię.
- Pole “RUNLEVELS” – określa tryby pracy, w których opisywana akcja ma działać.
- Pole “ACTION” – określa warunki niezbędne do wykonania komendy opisanej przez process.
- Pole “PROCESS” – jest najczęściej ścieżką dostępu, która uruchamia dany program lub skrypt przez init.
Pliku /etc/inittab z reguły się nie modyfikuje, chociaż można go wykorzystać do zmian niektórych ustawień, np. standardowy poziom startu systemu czy ilość konsol tekstowych. Kiedy system wprowadzany jest w dany poziom pracy, init przegląda wszystkie linie dotyczące danego poziomu. Tak samo postępuje podczas uruchomienia standardowego trybu pracy systemu, tylko z tym wyjątkiem, że szukana jest opcja “initdefault” w polu “ACTION”. Pole “ACTION” może przyjmować następujące wartości:
- 1) initdefault – poziom do jakiego zostanie doprowadzony system po zakończeniu procedur startowych (standardowo 3). Jeśli taki wpis nie zostanie odnaleziony init podczas startu systemu zada pytania o poziom rozruchu systemu. Dlatego linia:
id:3:initdefault:
określa docelowy tryb pracy systemu (osiągany podczas normalnego startu systemu). Jeśli chcemy wykorzystać więcej trybów, należy zmienić numer trybu w tej linii. Należy pamiętać, aby nie wstawić tam numerów 0 lub 6. - 2) once – proces zostanie wykonany tyko raz.
- 3) boot – proces zostanie wykonany w trakcie startu systemu.
- 4) sysinit – proces zostanie uruchomiony podczas startu systemu. Zostanie on uruchomiony przed wszystkimi pozostałymi (o niższym priorytecie np. boot) wpisami uruchamiającymi inne procesy. Przykładem tutaj może być linia:
si:S:sysinit:/etc/rc.d/rc.S
, która uruchomi skrypt rc.S na początku samego startu systemu – odpowiedzialny jest on za m.in. za aktywację partycji wymiany, ustawienie czasu, sprawdzenie integralności systemu plików. - 5) wait – proces zostanie uruchomiony jednorazowo, gdy wskazany poziom pracy systemu zostanie osiągnięty. Init będzie podczas jego pracy oczekiwał na jego całkowite zakończenie. Po jego zakończeniu proces nie zostanie ponownie uruchomiony. Przykładem może tu być linia:
rc:2345:wait:/etc/rc.d/rc.M
– uruchamiająca skrypt wprawiający system w rozruch trybu wielu użytkowników. Dopiero po jego zakończeniu będą odpalane kolejne linie w pliku /etc/inittab. - 6) respawn – proces zostanie uruchomiony jednorazowo, gdy wskazany poziom pracy systemu zostanie osiągnięty. Init będzie podczas jego pracy oczekiwał na jego całkowite zakończenie. Po jego zakończeniu proces zostanie ponownie uruchomiony. Czynność ta będzie powtarzana za każdym zakończeniem danego procesu. Przykładem może tu być linia:
c1:1235:respawn:/sbin/agetty 38400 tty1 linux
– uruchamia ona program agetty otwierający port lokalnej konsoli terminala (tty) wyświetlając znak zachęty logowania. Po wylogowaniu proces ten zostanie ponownie uruchomiony. - 7) ctrlaltdel – proces zostanie uruchomiony wtedy, gdy init otrzyma sygnał SIGINT. Sygnał ten oznacza, że ktoś przy konsoli systemowej nacisnął “trzech króli” (Ctrl+Alt+Del często używanych w systemie Windows). Wykonując tę kombinację uruchamia on odpowiedni skrypt przypisany dla tej właśnie kombinacji. Przykładem może tu być linia:
ca:ctrlaltdel:/sbin/shutdown -t5 -r now
– uruchamiająca komendę, która poddaje system przeładowaniu.
Innymi dość rzadko spotykanymi (chyba, że posiadamy system UPS) opcjami są:
- bootwait – init nie oczekuje na zakończenie danego procesu,
- powerwait – proces zostanie rozpoczęty, gdy init otrzyma sygnał: SIGPWR – mówiący o zaistniałych problemach z zasilaniem,
- power – ta sama sytuacja tylko, że init nie oczekuje na zakończenie procesu,
- powerokwait – proces zostanie rozpoczęty, gdy init otrzyma sygnał SIGPWR, a plik konfiguracyjny /etc/powerstatus będzie zawierał wpis: OK.
I na tym kończy się zadanie init. Na starcie systemu uruchamia on odpowiednie skrypty, niektóre jednorazowo, a inne wiele razy. Dalej w akcję wchodzą skrypty uruchamiające odpowiednie demony oraz programy diagnozujące system – włączenie pamięci wirtualnej, zamontowanie systemu plików, wyczyszczenie katalogów z logami, inicjalizacja urządzeń Plug & Play, załadowanie modułów jądra, konfiguracja urządzeń PCMCIA, ustawienia portów szeregowych, uruchomienie skryptu stylu System V (jeśli zostanie znaleziony). Poniżej przedstawiono małą mapę ładowania systemu na podstawie systemu Slackware, która pozwoli nam na płynne przejście od init do skryptów po poszczególne daemony:
*-jądro systemu (kernel) | init-+-rc.S-+ | |-rc.modules | |-rc.serial | |-rc.sysvinit | +-rc.M-+ | |-rc.pcmcia | |-rc.inet1 | |-rc.hotplug | |-rc.inet2 | |- ... | +-rc.K +-rc.0 +-rc.6
Oczywiście rozbijając ten schemat drzewa na bardziej drobne składniki możemy otrzymać szczegółową mapę systemu. Lecz to nie jest naszym celem. Przypatrując się zaprezentowanemu schematowi widzimy po zagałęzieniach, że kończą się one na podskryptach posiadających postać rc.*. Podskrypty te uruchamiają lub zatrzymują programy rezydentne, działające w systemie – w Linuksie określa się je mianem daemonów. Jak już wspomniałem skrypty uruchamiające poszczególne daemony znajdują się w katalogu /etc/rc.d/. Nazwa każdego skryptu odpowiada nazwie uruchamianego serwisu, np. komenda /etc/rc.d/rc.httpd start – uruchomi demona http, zaś /etc/rc.d/rc.httpd stop – spowoduje jego zatrzymanie. Możemy się temu przyjrzeć dokładniej rozwijając jedno rozgałęzienie do końca jego “owocu”:
*-jądro | init-+-rc.M-+ |-rc.inet2 | + | |-rc.sshd | |- /usr/sbin/sshd |-rc.httpd |- /usr/sbin/apachectl
Wynikiem tak wielu rozgałęzień jest zawsze poszczególna usługa lub ich grupa. W tym przypadku został uruchomiony daemon Secure Shell (SSH). Inne daemony np. httpd są uruchamiane na wcześniejszych gałęziach. Dlaczego takich przykładowych rozgałęzień nie posiadają rc.K, rc.0 i rc.6? Oczywiście, że je posiadają, lecz powyższe mapy ilustrują start systemu, a te trzy skrypty odpowiedzialne są za kończenie pracy wszystkich demonów (rc.K jest skryptem poziomu pojedynczego użytkownika, który pozostawia tylko daemony niezbędne do pracy systemu). Gdybyśmy opisywali zamykanie systemu rozgałęzienia te z pewnością by się tam znalazły. Wnikając w poszczególną budowę tych skryptów możemy zauważyć, że są one głównie zbudowane z warunków sprawdzających istnienie dalszych gałęzi oraz w przypadku pozytywnego wyniku uruchamianiu dalszych podskryptów i demonów. Dla przykładu oto warunek pochodzący ze skryptu rc.M sprawdzający istnienie podskryptu rc.inet2:
# Start networking daemons: if [ -x /etc/rc.d/rc.inet2 ]; then . /etc/rc.d/rc.inet2 else # Start the system logger. Normally this is started by # rc.inet2 because /usr might be mounted via NFS. if [ -x /etc/rc.d/rc.syslog ]; then . /etc/rc.d/rc.syslog start fi fi
Jeśli istnieje plik /etc/rc.d/rc.inet2
– odpowiedzialny za uruchomienie wielu daemonów (tak więc rc.inet2 możemy nazwać daemonem daemonów sieciowych – choć brzmi to dziwnie) to zostanie on uruchomiony. W przeciwnym wypadku zostanie uruchomiony tylko daemon raportowania komunikatów systemowych syslogd. Wpis dotyczący syslogd także znajduje się w rc.inet2, który w normalnych warunkach uruchamia go, lecz gdyby z jakiś powodów rc.inet2 nie było na swoim miejscu wpis ten stanowi swego rodzaju zabezpieczenie przed awaryjnym włączeniem raportowania zdarzeń w systemie. Przechodząc jedno piętro niżej wycinamy warunek z rc.inet2 uruchamiający daemona rc.sshd:
# Start the OpenSSH SSH daemon: if [ -x /etc/rc.d/rc.sshd ]; then echo "Starting OpenSSH SSH daemon: /usr/sbin/sshd" /etc/rc.d/rc.sshd start fi
Przypadek analogicznie podobny do poprzedniego, z tą różnicą, że został dodany komunikat wyświetlany podczas startu systemu informujący nas o pomyślnym załadowaniu usługi. Czas więc przejść do samego pojedynczego daemona rc.* oraz zapoznaniem się z jego budową:
demon_start() { /usr/sbin/demon } demon_stop() { killall demon } demon_restart() { demon_stop sleep 1 demon_start } case "$1" in 'start') demon_start ;; 'stop') demon_stop ;; 'restart') demon_restart ;; *) echo "usage $0 start|stop|restart" esac
Słowo daemon może być zastąpione dowolnym programem, którego dany system zarządzania daemonami się tyczy. System ten składa się z trzech sekcji: start, stop i restart – są to trzy typowe operacje wykonywane na danych usługach w systemie (dodatkowymi mogą być: status czy reload). Możemy je uruchamiać, zatrzymywać i restartować w celu odczytania nowych konfiguracji. W sekcji start zazwyczaj jest ścieżka dostępu do danego daemona, po danej ścieżce mogą występować parametry z jakimi ma zostać uruchomiony dany daemon w celu optymalizacji jego pracy. Sekcja stop posiada instrukcje “zabijające” wszelkie procesy związane z tą usługą w celu jej całkowitego zatrzymania. Natomiast sekcja restart zwyczajowo wykorzystuje wcześniejsze sekcje powodując zatrzymanie i uruchomienie danego daemona. Dlatego jeśli byśmy chcieli zatrzymać np. daemona syslogd wystarczy, że wydamy komendę:
/etc/rc.d/rc.syslog stop
Tak samo postępujemy w przypadku jego uruchomienia czy restartu. Należy jednak pamiętać, że nie wszystkimi daemonami możemy tak zarządzać. Należy wiedzieć, który daemon jest ostatnim w gałęzi składowym systemem zarządzającym daną usługą. Przecież po wydaniu polecenia /etc/rc.d/rc.M stop otrzymamy komunikaty błędu, ponieważ rc.M jest daemonem wielu uruchomień składników systemowych, które dzielą się na kolejne podskrypty i programy. Najczęściej daemonami, którymi możemy zarządzać na trzy wyżej wymienione sposoby są usługi i serwery linuksowe np. httpd, sshd, ftpd, pop3d, imapd, telnetd (końcówka “d” jest skrótem od daemon). Ich nazwa w odzwierciedleniu daemona jest taka sama, lecz poprzedzona frazą “rc.” np. rc.http czy rc.sshd. By w pełni zrozumieć cały mechanizm startu Linuksa Slackware i przybliżyć sobie pojęcia daemonów najlepiej krok po kroku samemu przeanalizować poszczególne skrypty startowe.
Bonus:Jak już wiemy init jest programem, który ładuje system i konfiguruje terminale, ale także świetnie się nadaje do stworzenia własnego daemona. Wystarczy, że do pliku /etc/inittab dodamy dowolny wiersz, który określi, który daemon ma być przez program init obserwowany:
xx:13:respawn:/usr/local/sbin/demon
Jak widzimy wiersz ten w pliku inittab składa się z dowolnego (ale unikatowego) identyfikacyjnego ciągu znaków, za którym występują poziomy uruchomieniowe programów, a następnie znane już nam słowo kluczowe respawn, na samym końcu zaś – pełna ścieżka do programu, który ma być naszym daemonem. W powyższym przykładzie, dopóki “daemon” nie posiada konfiguracji powodującej pracy na pierwszym planie systemu, program init będzie tworzył jego kolejne kopie po każdym jego zakończeniu pracy. Po wprowadzeniu tych zmian do pliku inittab konieczne jest zrestartowanie programu init np. przez sygnał HUP. W tym celu wydajemy polecenie:
kill -HUP 1
Warto dodać, że jeśli nasz program będzie bardzo szybko kończył swoją pracę tym samym powodując zbyt szybkie tworzenie swoich egzemplarzy, wówczas program init na chwilę wstrzyma jego działanie, aby zapobiec całkowitemu skonsumowaniu zasobów systemowych.
Od Slackware w wersji 7.0 został zaimplementowany skrypt pozwalający na kompatybilność Slackware ze stylem uruchamiania stosowanym w System V. Od wersji 12.0 odpowiada za tą funkcję pakiet o nazwie sysvinit (zawierający programy zgodne z System V) oraz sysvinit-functions (zwierający odpowiednią strukturę katalogów oraz dowiązań symbolicznych do stylu BSD). Jak wspomniałem wcześniej, styl ten polega na przydzieleniu, każdemu poziomowi rozruchu własnego katalogu oraz umieszczeniu w nim poszczególnych daemonów, które mają być w danym poziomie uruchamiane. Styl ten daje dość przejrzyste zarządzanie poszczególnymi usługami. Jeśli nie potrafimy się poruszać się po drzewie daemonów stylu BSD, możemy na własne potrzeby stworzyć odpowiednie katalogi (od wersji 12.0 zrobi to za nas w/w pakiet) i w nich poumieszczać dane skrypty. W tym celu należy zapoznać się z budową pliku /etc/rc.d/rc.sysvinit oraz rozmieszczeniem stosowanym np. w systemie opartym na dystrybucji RedHat / Debian.
Więcej informacji: man init, pliki konfiguracji /etc/inittab i /etc/rc.d/