Wprowadzenie do procesów rozruchu i uruchamiania systemu Linux

Zrozumienie procesów rozruchu i uruchamiania systemu Linux jest ważne zarówno dla możliwości konfigurowania systemu Linux, jak i rozwiązywania problemów z uruchamianiem. Ten artykuł przedstawia sekwencję rozruchu przy użyciu programu rozruchowego GRUB2 oraz sekwencję startową wykonywaną przez system inicjalizacji systemd.

W rzeczywistości istnieją dwie sekwencje zdarzeń, które są wymagane do uruchomienia komputera z Linuksem i uczynienia go użytecznym: rozruch i start. Sekwencja rozruchu rozpoczyna się, gdy komputer jest włączony, i jest zakończona, gdy jądro jest inicjalizowane i systemd jest uruchamiany. Proces rozruchu następnie przejmuje i kończy zadanie doprowadzenia komputera z Linuksem do stanu operacyjnego.

Ogółem, proces rozruchu w Linuksie jest dość prosty do zrozumienia. Składa się z następujących kroków, które zostaną opisane bardziej szczegółowo w następnych sekcjach.

  • BIOS POST
  • Ładowacz systemu (GRUB2)
  • Inicjalizacja jądra
  • Uruchomienie systemd, rodzica wszystkich procesów.

Zauważ, że ten artykuł dotyczy GRUB2 i systemd, ponieważ są to aktualne programy ładujące i inicjujące dla większości głównych dystrybucji. Inne opcje oprogramowania były używane historycznie i nadal znajdują się w niektórych dystrybucjach.

Proces bootowania

Proces bootowania może być zainicjowany na jeden z kilku sposobów. Po pierwsze, jeśli zasilanie jest wyłączone, włączenie zasilania rozpocznie proces bootowania. Jeśli komputer jest już obsługiwany przez lokalnego użytkownika, w tym root’a lub nieuprzywilejowanego użytkownika, użytkownik może programowo zainicjować sekwencję startową za pomocą GUI lub wiersza poleceń, aby zainicjować restart. Restart spowoduje najpierw zamknięcie, a następnie ponowne uruchomienie komputera.

BIOS POST

Pierwszy krok procesu startowego Linuksa nie ma nic wspólnego z Linuksem. Jest to sprzętowa część procesu bootowania i jest taka sama dla każdego systemu operacyjnego. Gdy komputer jest zasilany po raz pierwszy uruchamia POST (Power On Self Test), który jest częścią BIOS (Basic I/O System).

Gdy IBM zaprojektował pierwszy komputer PC w 1981 roku, BIOS został zaprojektowany do inicjalizacji komponentów sprzętowych. POST jest częścią BIOS-u, której zadaniem jest zapewnienie, że sprzęt komputerowy działa poprawnie. Jeśli POST się nie powiedzie, komputer może nie nadawać się do użytku i dlatego proces rozruchu nie jest kontynuowany.

BIOS POST sprawdza podstawową operacyjność sprzętu, a następnie wydaje przerwanie BIOS-u, INT 13H, które lokalizuje sektory startowe na wszelkich podłączonych urządzeniach startowych. Pierwszy znaleziony sektor startowy zawierający poprawny rekord startowy jest ładowany do pamięci RAM, a sterowanie jest przekazywane do kodu, który został załadowany z sektora startowego.

Sektor startowy jest tak naprawdę pierwszym etapem programu ładującego. Istnieją trzy programy ładujące używane przez większość dystrybucji Linuksa, GRUB, GRUB2 i LILO. GRUB2 jest najnowszy i jest obecnie używany znacznie częściej niż inne starsze opcje.

GRUB2

GRUB2 to skrót od „GRand Unified Bootloader, wersja 2” i jest obecnie podstawowym programem ładującym dla większości obecnych dystrybucji Linuksa. GRUB2 jest programem, który sprawia, że komputer jest wystarczająco inteligentny, aby znaleźć jądro systemu operacyjnego i załadować je do pamięci. Ponieważ łatwiej jest napisać i powiedzieć GRUB niż GRUB2, mogę używać terminu GRUB w tym dokumencie, ale będę odnosił się do GRUB2, chyba że zaznaczono inaczej.

GRUB został zaprojektowany tak, aby był kompatybilny ze specyfikacją multiboot, która pozwala GRUB-owi na uruchamianie wielu wersji Linuksa i innych wolnych systemów operacyjnych; może także łańcuchowo ładować płytę startową własnościowych systemów operacyjnych.

GRUB może także pozwolić użytkownikowi na wybór startu z kilku różnych jąder dla danej dystrybucji Linuksa. Daje to możliwość uruchomienia systemu z poprzedniej wersji jądra, jeśli zaktualizowane jądro ulegnie awarii lub jest niekompatybilne z ważnym oprogramowaniem. GRUB może być skonfigurowany za pomocą pliku /boot/grub/grub.conf.

GRUB1 jest obecnie uważany za starszy i został zastąpiony w większości nowoczesnych dystrybucji przez GRUB2, który jest przeróbką GRUB1. Dystrybucje oparte na Red Hat zaktualizowały się do GRUB2 w okolicach Fedory 15 i CentOS/RHEL 7. GRUB2 zapewnia taką samą funkcjonalność startową jak GRUB1, ale GRUB2 jest także środowiskiem pre-OS opartym na komendach mainframe i pozwala na większą elastyczność podczas fazy pre-boot. GRUB2 jest konfigurowany przez /boot/grub2/grub.cfg.

Podstawową funkcją GRUB-a jest załadowanie jądra Linuksa do pamięci i uruchomienie go. Obie wersje GRUBa działają zasadniczo w ten sam sposób i mają te same trzy etapy, ale będę używał GRUBa2 do omówienia sposobu w jaki GRUB wykonuje swoją pracę. Konfiguracja GRUBa lub GRUB2 i użycie komend GRUB2 jest poza zakresem tego artykułu.

Ale GRUB2 oficjalnie nie używa notacji etapów dla trzech etapów GRUB2, wygodnie jest odnosić się do nich w ten sposób, więc tak zrobię w tym artykule.

Faza 1

Jak wspomniano w sekcji BIOS POST, pod koniec POST, BIOS przeszukuje podłączone dyski w poszukiwaniu rekordu startowego, zwykle znajdującego się w Master Boot Record (MBR), ładuje do pamięci pierwszy, który znajdzie, a następnie rozpoczyna wykonywanie rekordu startowego. Kod startowy, czyli GRUB2 stage 1, jest bardzo mały, ponieważ musi zmieścić się w pierwszym 512-bajtowym sektorze na dysku twardym wraz z tablicą partycji. Całkowita ilość miejsca przeznaczona na kod startowy w klasycznym MBR wynosi 446 bajtów. Ten 446-bajtowy plik dla etapu 1 nosi nazwę boot.img i nie zawiera tablicy partycji, która jest dodawana do rekordu startowego osobno.

Ponieważ rekord startowy musi być tak mały, nie jest też zbyt inteligentny i nie rozumie struktur systemu plików. Dlatego jedynym celem etapu 1 jest zlokalizowanie i załadowanie etapu 1.5. Aby to osiągnąć, etap 1.5 GRUB-a musi być umieszczony w przestrzeni pomiędzy płytą startową a pierwszą partycją na dysku. Po załadowaniu etapu 1.5 GRUB-a do pamięci RAM, etap 1 przekazuje kontrolę etapowi 1.5.

Etap 1.5

Jak wspomniano powyżej, etap 1.5 GRUB-a musi być umieszczony w przestrzeni pomiędzy płytą startową a pierwszą partycją na dysku. Ta przestrzeń była historycznie niewykorzystana z powodów technicznych. Pierwsza partycja na dysku zaczyna się od sektora 63 i z MBR w sektorze 0, pozostawia to 62 512-bajtowe sektory – 31,744 bajtów – w których można przechowywać plik core.img, który jest etapem 1.5 GRUBa. Plik core.img ma rozmiar 25,389 bajtów, więc jest wystarczająco dużo miejsca pomiędzy MBR a pierwszą partycją dysku, na której można go przechowywać.

Ze względu na większą ilość kodu, który może być umieszczony w etapie 1.5, może on mieć wystarczająco dużo kodu, aby zawierać kilka popularnych sterowników systemów plików, takich jak standardowy EXT i inne linuksowe systemy plików, FAT i NTFS. GRUB2 core.img jest znacznie bardziej złożony i zdolny niż starszy GRUB1 stage 1.5. Oznacza to, że etap 2 GRUB2 może być umieszczony na standardowym systemie plików EXT, ale nie może być umieszczony na woluminie logicznym. Tak więc standardową lokalizacją dla plików etapu 2 jest system plików /boot, a konkretnie /boot/grub2.

Zauważ, że katalog /boot musi znajdować się na systemie plików, który jest obsługiwany przez GRUB. Nie wszystkie systemy plików są. Zadaniem etapu 1.5 jest rozpoczęcie wykonywania sterowników systemu plików niezbędnych do zlokalizowania plików etapu 2 w systemie plików /boot i załadowania potrzebnych sterowników.

Etap 2

Wszystkie pliki dla GRUB-a etapu 2 znajdują się w katalogu /boot/grub2 i kilku podkatalogach. GRUB2 nie posiada pliku obrazu jak etapy 1 i 2. Zamiast tego składa się głównie z modułów jądra runtime, które są ładowane w razie potrzeby z katalogu /boot/grub2/i386-pc.

Funkcją GRUB2 etap 2 jest zlokalizowanie i załadowanie jądra Linux do pamięci RAM i przekazanie kontroli nad komputerem do jądra. Jądro i związane z nim pliki znajdują się w katalogu /boot. Pliki jądra są identyfikowalne, ponieważ ich nazwy zaczynają się od vmlinuz. Możesz wyświetlić zawartość katalogu /boot, aby zobaczyć aktualnie zainstalowane jądra w systemie.

GRUB2, podobnie jak GRUB1, obsługuje uruchamianie z jednego z wybranych jąder Linuksa. Menedżer pakietów Red Hat, DNF, wspiera przechowywanie wielu wersji jądra, tak że jeśli wystąpi problem z najnowszą, starsza wersja jądra może zostać uruchomiona. Domyślnie GRUB dostarcza przedstartowe menu zainstalowanych jąder, włączając w to opcję ratunkową i, jeśli jest skonfigurowana, opcję odzyskiwania.

Etap 2 GRUB2 ładuje wybrane jądro do pamięci i przekazuje kontrolę nad komputerem do jądra.

Jądro

Wszystkie jądra są w samorozpakowującym się, skompresowanym formacie, aby zaoszczędzić miejsce. Jądra znajdują się w katalogu /boot, razem z początkowym obrazem dysku RAM i mapami urządzeń na dyskach twardych.

Po załadowaniu wybranego jądra do pamięci i rozpoczęciu wykonywania, musi ono najpierw wyodrębnić się ze skompresowanej wersji pliku, zanim będzie mogło wykonać jakąkolwiek użyteczną pracę. Gdy jądro się wyodrębni, ładuje systemd, który jest zamiennikiem starego programu inicjującego SysV, i przekazuje mu kontrolę.

To jest koniec procesu rozruchu. W tym momencie, jądro Linux i systemd są uruchomione, ale nie są w stanie wykonywać żadnych produktywnych zadań dla użytkownika końcowego, ponieważ nic innego nie jest uruchomione.

Proces rozruchu

Proces rozruchu następuje po procesie rozruchu i doprowadza komputer z Linuksem do stanu operacyjnego, w którym jest on użyteczny do produktywnej pracy.

systemd

systemd jest matką wszystkich procesów i jest odpowiedzialny za doprowadzenie hosta Linuksa do stanu, w którym może być wykonywana produktywna praca. Niektóre z jego funkcji, które są o wiele bardziej rozbudowane niż stary program init, to zarządzanie wieloma aspektami działającego hosta Linuksa, w tym montowanie systemów plików, uruchamianie i zarządzanie usługami systemowymi wymaganymi do posiadania produktywnego hosta Linuksa. Wszystkie zadania systemd, które nie są związane z sekwencją startową są poza zakresem tego artykułu.

Po pierwsze, systemd montuje systemy plików zdefiniowane przez /etc/fstab, włączając w to wszelkie pliki wymiany lub partycje. W tym momencie może uzyskać dostęp do plików konfiguracyjnych znajdujących się w /etc, włączając w to swoje własne. Używa swojego pliku konfiguracyjnego, /etc/systemd/system/default.target, do określenia stanu lub celu, w którym powinien uruchomić się host. Plik default.target jest tylko dowiązaniem symbolicznym do prawdziwego pliku docelowego. Dla desktopowej stacji roboczej, będzie to zazwyczaj plik graphical.target, który jest odpowiednikiem runlevel 5 w starym SystemV init. Dla serwera, domyślnie będzie to raczej multi-user.target, który jest jak runlevel 3 w SystemV. Emergency.target jest podobny do trybu pojedynczego użytkownika.

Zauważ, że cele i usługi są jednostkami systemd.

Tabela 1, poniżej, jest porównaniem celów systemd z poziomami startowymi starego SystemV. Aliasy docelowe systemd są dostarczane przez systemd dla kompatybilności wstecznej. Aliasy docelowe pozwalają skryptom i wielu sysadminom, takim jak ja, używać poleceń SystemV, takich jak init 3, do zmiany poziomów startowych. Of course, the SystemV commands are forwarded to systemd for interpretation and execution.

SystemV Runlevel systemd target systemd target aliases Description
halt.target Halts the system without powering it down.
0 poweroff.target runlevel0.target Halts the system and turns the power off.
S emergency.target Single user mode. No services are running; filesystems are not mounted. This is the most basic level of operation with only an emergency shell running on the main console for the user to interact with the system.
1 rescue.target runlevel1.target A base system including mounting the filesystems with only the most basic services running and a rescue shell on the main console.
2 runlevel2.target Multiuser, without NFS but all other non-GUI services running.
3 multi-user.target runlevel3.target All services running but command line interface (CLI) only.
4 runlevel4.target Unused.
5 graphical.target runlevel5.target multi-user with a GUI.
6 reboot.target runlevel6.target Reboot
default.target This target is always aliased with a symbolic link to either multi-user.target or graphical.target. systemd always uses the default.target to start the system. Domyślny.target nigdy nie powinien być aliasowany do halt.target, poweroff.target, lub reboot.target.

Tabela 1: Porównanie poziomów uruchomieniowych SystemV z celami systemd i niektórymi aliasami celów.

Każdy cel ma zestaw zależności opisanych w jego pliku konfiguracyjnym. systemd uruchamia wymagane zależności. Zależności te są usługami wymaganymi do uruchomienia hosta linuksowego na określonym poziomie funkcjonalności. Kiedy wszystkie zależności wymienione w plikach konfiguracyjnych celu są załadowane i uruchomione, system działa na tym poziomie docelowym.

Systemd patrzy również na starsze katalogi inicjujące SystemV aby sprawdzić czy istnieją tam jakieś pliki startowe. Jeśli tak, systemd użył ich jako plików konfiguracyjnych do uruchomienia usług opisanych przez te pliki. Przestarzała usługa sieciowa jest dobrym przykładem jednej z tych, które wciąż używają plików startowych SystemV w Fedorze.

Niżej znajduje się rysunek 1, skopiowany bezpośrednio ze strony man bootup. Pokazuje ogólną sekwencję zdarzeń podczas uruchamiania systemd i podstawowe wymagania porządkujące, aby zapewnić pomyślne uruchomienie.

Cele sysinit.target i basic.target mogą być uważane za punkty kontrolne w procesie uruchamiania. Chociaż jednym z celów systemd jest równoległe uruchamianie usług systemowych, nadal istnieją pewne usługi i cele funkcjonalne, które muszą zostać uruchomione przed uruchomieniem innych usług i celów. Te punkty kontrolne nie mogą być przekroczone dopóki wszystkie usługi i cele wymagane przez ten punkt kontrolny nie zostaną spełnione.

Więc sysinit.target jest osiągnięty kiedy wszystkie jednostki od których zależy są ukończone. Wszystkie te jednostki, montowanie systemów plików, ustawianie plików wymiany, uruchamianie udev, ustawianie nasion generatora losowego, inicjowanie usług niskiego poziomu i ustawianie usług kryptograficznych, jeśli jeden lub więcej systemów plików jest zaszyfrowanych, muszą być ukończone, ale w ramach sysinit.target te zadania mogą być wykonywane równolegle.

The sysinit.uruchamia wszystkie niskopoziomowe usługi i jednostki wymagane, aby system był marginalnie funkcjonalny i które są wymagane, aby umożliwić przejście do podstawowego.target.

local-fs-pre.target
|
v
(various mounts and (various swap (various cryptsetup
fsck services...) devices...) devices...) (various low-level (various low-level
| | | services: udevd, API VFS mounts:
v v v tmpfiles, random mqueue, configfs,
local-fs.target swap.target cryptsetup.target seed, sysctl, ...) debugfs, ...)
| | | | |
\__________________|_________________ | ___________________|____________________/
\|/
v
sysinit.target
|
____________________________________/|\________________________________________
/ | | | \
| | | | |
v v | v v
(various (various | (various rescue.service
timers...) paths...) | sockets...) |
| | | | v
v v | v rescue.target
timers.target paths.target | sockets.target
| | | |
v \_________________ | ___________________/
\|/
v
basic.target
|
____________________________________/| emergency.service
/ | | |
| | | v
v v v emergency.target
display- (various system (various system
manager.service services services)
| required for |
| graphical UIs) v
| | multi-user.target
| | |
\_________________ | _________________/
\|/
v
graphical.target

Figura 1: Mapa startowa systemd.

Po wypełnieniu sysinit.target, systemd następnie uruchamia basic.target, uruchamiając wszystkie jednostki wymagane do jego wypełnienia. Cel podstawowy zapewnia pewną dodatkową funkcjonalność poprzez uruchamianie jednostek, które są wymagane dla następnego celu. Obejmują one ustawianie takich rzeczy jak ścieżki do różnych katalogów wykonywalnych, gniazda komunikacyjne i timery.

Na koniec, cele poziomu użytkownika, multi-user.target lub graphical.target mogą zostać zainicjowane. Zauważ, że cel multi-user.target musi zostać osiągnięty, zanim zostaną spełnione zależności celu graficznego.

Podkreślone cele na Rysunku 1, są zwykłymi celami startowymi. Gdy jeden z tych celów zostanie osiągnięty, rozruch jest zakończony. Jeśli multi-user.target jest domyślny, to powinieneś zobaczyć logowanie w trybie tekstowym na konsoli. Jeśli graphical.target jest domyślny, to powinieneś zobaczyć graficzne logowanie; konkretny ekran logowania GUI, który zobaczysz będzie zależał od domyślnego menedżera wyświetlania, którego używasz.

Problemy

Miałem ostatnio potrzebę zmiany domyślnego jądra rozruchowego na komputerze z Linuksem, który używał GRUB2. Odkryłem, że niektóre z poleceń nie działały poprawnie dla mnie, lub że nie używałem ich poprawnie. Nie jestem jeszcze pewien, co było w tym przypadku, i muszę zrobić więcej badań.

Komenda grub2-set-default nie ustawiła poprawnie indeksu domyślnego jądra dla mnie w pliku /etc/default/grub tak, że pożądane alternatywne jądro nie wystartowało. Więc ręcznie zmieniłem /etc/default/grub GRUB_DEFAULT=saved na GRUB_DEFAULT=2, gdzie 2 jest indeksem zainstalowanego jądra, które chciałem uruchomić. Następnie wykonałem polecenie grub2-mkconfig > /boot/grub2/grub.cfg aby utworzyć nowy plik konfiguracyjny gruba. To obejście zadziałało zgodnie z oczekiwaniami i uruchomiło alternatywne jądro.

Wnioski

GRUB2 i system inicjujący systemd są kluczowymi komponentami w fazach rozruchu i startu większości nowoczesnych dystrybucji Linuksa. Pomimo faktu, że wokół systemd narosło wiele kontrowersji, te dwa komponenty współpracują ze sobą płynnie, aby najpierw załadować jądro, a następnie uruchomić wszystkie usługi systemowe wymagane do stworzenia funkcjonalnego systemu Linux.

Ale uważam, że zarówno GRUB2 jak i systemd są bardziej złożone niż ich poprzednicy, są one również tak samo łatwe do nauczenia się i zarządzania. Strony man zawierają wiele informacji o systemd, a freedesktop.org ma kompletny zestaw stron man systemd online. Odnieś się do zasobów, poniżej, aby uzyskać więcej linków.