Proces uruchamiania systemu Linux

Od włączenia zasilania do działającej przestrzeni użytkownika — oprogramowanie układowe (firmware), program ładujący (bootloader), inicjalizacja jądra i init. Koncentracja na x86-64 i UEFI, z uwagami dotyczącymi BIOS/MBR tam, gdzie to istotne.

01FIRMWARE
02BOOTLOADER
03OBRAZ JĄDRA
04INICJ. JĄDRA
05INITRAMFS
06INIT
01

Oprogramowanie układowe i włączanie zasilania

Gdy procesor wychodzi ze stanu resetu, zaczyna wykonywanie od stałego adresu fizycznego. W architekturze x86-64 jest to 0xFFFFFFF0 — 16-bajtowy "wektor resetu", który natychmiast przeskakuje do pamięci ROM oprogramowania układowego.

UEFI

Nowoczesne systemy używają UEFI. Wykonanie przechodzi przez trzy fazy przed wywołaniem programu ładującego system operacyjny:

SEC (Bezpieczeństwo) Działa całkowicie w pamięci podręcznej jako RAM (CAR). Weryfikuje obraz PEI, ustanawia minimalne zaufane środowisko.
PEI (Inicjalizacja Pre-EFI) Inicjuje pamięć DRAM, podstawy chipsetu i wszelki kod specyficzny dla układu. Tworzy listę bloków przekazywania (Hand-Off Block - HOB), która jest przekazywana dalej.
DXE (Wykonanie Sterownika) Pełne środowisko UEFI. Ładuje sterowniki z woluminu firmware'u, uruchamia PCIe, pamięć masową i USB. Tworzy Tablicę Systemową EFI (EFI System Table) widoczną dla późniejszych etapów.
BDS (Wybór Urządzenia Rozruchowego) Ocenia kolejność rozruchu w NVRAM, lokalizuje aplikację EFI (\EFI\BOOT\BOOTX64.EFI lub ścieżkę specyficzną dla dystrybucji) i przekazuje jej kontrolę.
Bezpieczny Rozruch (Secure Boot)
Przed uruchomieniem jakiegokolwiek pliku binarnego EFI, UEFI weryfikuje jego podpis z listą dozwolonych db. Shim (shimx64.efi) to standardowy pierwszy etap dla dystrybucji — niesie własny podpis akceptowany przez klucze Microsoftu, a następnie weryfikuje bootloader za pomocą własnej bazy danych MOK.

Starszy BIOS / MBR (informacyjnie)

BIOS ładuje pierwsze 512 bajtów dysku rozruchowego (MBR) pod adres fizyczny 0x7C00 i przeskakuje do niego. Kod MBR lokalizuje partycję rozruchową za pomocą tablicy partycji i ładuje program ładujący drugiego etapu. Wszystko to dzieje się w 16-bitowym trybie rzeczywistym bez abstrakcji sprzętowej.

02

Program ładujący (Bootloader)

Dominującym bootloaderem w systemach Linux x86-64 jest GRUB2. Plik binarny EFI grubx64.efi jest ładowany bezpośrednio przez BDS jako aplikacja EFI; działa w 64-bitowym trybie EFI z pełnym dostępem do Usług Rozruchowych UEFI.

Zadania GRUB2

Protokół rozruchowy systemu Linux

GRUB umieszcza strukturę struct boot_params pod znanym adresem i przekazuje ją do jądra. Zawiera ona mapę pamięci (tablica E820), wskaźnik do wiersza poleceń, adres i rozmiar initrd, informacje o trybie wideo oraz dane specyficzne dla EFI.

Układ boot_params (częściowy)
0x000 screen_info
0x040 apm_bios_info
0x090 hdr          ← setup_header (magiczna liczba 0xAA55, wersja protokołu itp.)
0x1E8 e820_entries
0x2D0 e820_table[128]
...   efi_info     ← Mapa pamięci EFI, wskaźnik do tablicy systemowej
systemd-boot / EFI stub
Alternatywa dla GRUB: samo jądro może być poprawną aplikacją EFI poprzez CONFIG_EFI_STUB. systemd-boot (lub dowolny program ładujący EFI) ładuje vmlinuz bezpośrednio jako plik binarny EFI, przekazując parametry przez EFI LoadOptions. Nie jest potrzebna oddzielna ścieżka kodu dla bootloadera.
03

Ładowanie i dekompresja jądra

Plikiem na dysku jest vmlinuz — samorozpakowujące się archiwum. Nazwa pochodzi od virtual memory Linux; sufiks z wskazuje na kompresję (historycznie gzip, obecnie często zstd lub lz4 w zależności od konfiguracji dystrybucji).

Struktura obrazu

Nagłówek bzImage Pierwsze ~512 bajtów. Zawiera setup_header z magiczną liczbą 0x53726448 ("HdrS"), wersję protokołu, wskazówki dotyczące adresu ładowania oraz przesunięcie/rozmiar ładunku (payload).
Kod konfiguracyjny trybu rzeczywistego Historycznie działał w trybie 16-bitowym; w przypadku protokołu rozruchowego ≥ 2.02 i nowoczesnego bootloadera, jest on całkowicie omijany — bootloader używa bezpośrednio 32/64-bitowych punktów wejścia.
Skompresowany ładunek Rzeczywisty plik binarny jądra, skompresowany. Poprzedzony niewielkim kodem źródłowym (stubem) dekompresora.

Dekompresja

Kontrola przechodzi do stuba dekompresora w arch/x86/boot/compressed/head_64.S. Tworzy on minimalną tablicę stron, przełącza w tryb long mode, jeśli jeszcze w nim nie jest, dekompresuje obraz jądra do pamięci (zwykle pod adres 0x1000000) i przeskakuje do nieskompresowanego wejścia jądra — startup_64 w arch/x86/kernel/head_64.S.

KASLR
Dzięki CONFIG_RANDOMIZE_BASE, dekompresor wywołuje oprogramowanie układowe, aby uzyskać losowe przesunięcie fizyczne przed umieszczeniem jądra. Końcowy adres ładowania jest zapisywany i używany do naprawienia wszelkich absolutnych odniesień przed skokiem do właściwego jądra.
04

Wczesna inicjalizacja jądra

Po dojściu do startup_64, jądro wykonuje własny kod bez systemu operacyjnego pod spodem. Stan sprzętu jest nadal w dużej mierze taki, jak pozostawiło go oprogramowanie układowe / bootloader.

startup_64 → start_kernel

startup_64 Konfiguruje wczesne tablice stron (mapowania tożsamości + mapowania jądra). Czyści sekcję BSS. Wywołuje x86_64_start_kernel().
x86_64_start_kernel Naprawia rejestr CR3, aby wskazywał na prawdziwy PGD jądra. Przechodzi do wywołania start_kernel() w init/main.c.
start_kernel() Główny punkt wejścia w języku C. Wykonuje setki wywołań inicjujących w określonej kolejności. Nigdy nie powraca.

Kluczowa sekwencja inicjalizacji wewnątrz start_kernel()

Uruchamianie SMP

Gdy główny procesor rozruchowy (BSP - Bootstrap Processor) zakończy start_kernel(), wysyła sekwencje INIT-SIPI-SIPI przez LAPIC, aby obudzić procesory aplikacyjne (AP - Application Processors). Każdy AP wykonuje start_secondary(), inicjuje własny stan dla każdego CPU i dołącza do kolejki uruchamiania planisty. W nowoczesnych jądrach proces ten jest kontrolowany przez mechanizm hotplug dla CPU.

Weryfikacja topologii CPU w czasie działania
# logiczne CPU i mapowanie
$ lscpu --extended

# domeny planisty (topologia NUMA/cache widziana przez jądro)
$ cat /sys/kernel/debug/sched/domains/cpu0/domain0/name
05

initramfs

Jądro nie może zamontować prawdziwego głównego systemu plików bez sterowników, które same w sobie mogą być modułami jądra przechowywanymi w tym systemie plików (kontroler dysku, LUKS, LVM, sieć). initramfs rozwiązuje ten problem jajka i kury.

Czym jest

Skompresowanym (gzip/lz4/zstd) archiwum CPIO przekazywanym do jądra przez bootloader za pomocą pól initrd_start / initrd_size w boot_params. Jądro rozpakowuje je do instancji tmpfs i montuje jako początkowy katalog główny (/).

initrd a initramfs
Starszy initrd był obrazem urządzenia blokowego montowanym jako ramdisk (wymagał sterownika systemu plików i stałego rozmiaru). initramfs to archiwum CPIO rozpakowywane bezpośrednio do tmpfs — tańsze, bez stałego rozmiaru, bez dodatkowej warstwy systemu plików. Nowoczesne jądra używają initramfs; termin "initrd" jest potocznie używany dla obu tych rozwiązań.

Zawartość i wykonanie

Archiwum zawiera minimalny główny system plików wystarczający do zlokalizowania i zamontowania prawdziwego katalogu głównego (root):

Przejście do prawdziwego katalogu głównego

Po zamontowaniu prawdziwego urządzenia głównego (zazwyczaj w /sysroot), proces init initramfs wywołuje switch_root /sysroot /sbin/init — sekwencję wywołań systemowych, która przenosi montowanie, wykonuje chroot oraz uruchamia przez exec końcowy plik binarny init. Przestrzeń tmpfs środowiska initramfs zostaje zwolniona.

Badanie lub rozpakowywanie initramfs
# lista zawartości
$ lsinitrd /boot/initramfs-$(uname -r).img   # dracut/Fedora
$ lsinitcpio /boot/initramfs-linux.img        # mkinitcpio/Arch

# ręczne rozpakowanie (dostosuj do typu kompresji)
$ mkdir /tmp/ir && cd /tmp/ir
$ file /boot/initramfs-$(uname -r).img        # sprawdzenie kompresji
$ zstdcat /boot/initramfs-$(uname -r).img | cpio -idm
06

System Init

PID 1 w ostatecznym rootfs to system init. Jest on przodkiem wszystkich procesów przestrzeni użytkownika i nigdy nie może zostać zakończony. W zdecydowanej większości obecnych dystrybucji Linuksa jest to systemd.

Sekwencja rozruchowa systemd

default.target Zazwyczaj połączony dowiązaniem symbolicznym z graphical.target lub multi-user.target. Definiuje zbiór jednostek, które muszą być aktywne.
Graf jednostek systemd buduje graf zależności ze wszystkich plików jednostek w /lib/systemd/system/ oraz /etc/systemd/system/. Krawędzie to Requires=, Wants=, Before=, After=.
Aktywacja równoległa Jednostki o spełnionych zależnościach są aktywowane współbieżnie. Aktywacja przez gniazdo D-Bus oraz jednostki urządzeń (.device poprzez udev) pozwalają na opóźnione uruchamianie (lazy-start) usług.
Wczesne cele sysinit.targetbasic.targetnetwork.targetmulti-user.target / graphical.target. Każdy z nich stanowi punkt synchronizacji.

Kluczowe wczesne jednostki

Badanie wydajności rozruchu

Diagnostyka
# Ogólne zestawienie czasu rozruchu
$ systemd-analyze

# Czasy dla poszczególnych jednostek (od najwolniejszej)
$ systemd-analyze blame

# Ścieżka krytyczna przez graf zależności
$ systemd-analyze critical-chain

# Pełny graf zależności jednostek (renderowany przez dot)
$ systemd-analyze dot | dot -Tsvg > boot.svg

# Bufor kołowy jądra z tego rozruchu
$ journalctl -k -b 0

# Wszystkie komunikaty z fazy initramfs
$ journalctl -b 0 -o short-monotonic | head -200
Alternatywne systemy init
SysVinit (sekwencyjny, skrypty powłoki, wciąż spotykany w niektórych systemach minimalnych/wbudowanych), OpenRC (oparty na zależnościach i powłoce, domyślny w Gentoo i Alpine), runit (używany w Void Linux — oparty na nadzorze, brak plików jednostek). Jądro dba tylko o to, aby PID 1 pozostał przy życiu i przejmował osierocone procesy; cała reszta to tylko kwestia przyjętej polityki.

Krótkie zestawienie (Quick Reference)

Kluczowe pliki i ścieżki

/boot/vmlinuz-*Skompresowany obraz jądra ładowany przez bootloader.
/boot/initramfs-*.imgArchiwum CPIO initramfs (nazewnictwo dracut; initramfs-linux.img na Arch).
/boot/grub/grub.cfgWygenerowana konfiguracja GRUB2. Nie edytuj bezpośrednio; wygeneruj ponownie za pomocą grub-mkconfig.
/proc/cmdlineWiersz poleceń jądra przekazywany przez bootloader.
/proc/1/exeDowiązanie symboliczne do pliku binarnego init (potwierdza, czym jest PID 1).
/sys/firmware/efiObecne tylko przy rozruchu UEFI. Zawiera zmienne EFI oraz informacje o usługach środowiska wykonawczego.
/sys/firmware/acpiTablice ACPI udostępniane przez jądro. acpidump służy do ich ekstrakcji.
/sys/kernel/boot_paramsSurowa struktura boot_params widziana przez jądro (dostępna z CONFIG_BOOT_CONFIG).
/etc/systemd/system/Lokalne pliki jednostek i nadpisania (wyższy priorytet niż /lib/systemd/system/).
/etc/fstabTabela montowania systemu plików używana przez systemd-fstab-generator do tworzenia jednostek montowania.

Przydatne parametry jądra

root=Główne urządzenie (Root). Akceptuje /dev/sdXN, UUID=, PARTUUID=, LABEL=.
rootflags=Opcje montowania dla głównego systemu plików.
init=Zastąpienie PID 1. Np. init=/bin/bash dla powłoki awaryjnej.
rd.breakPrzejście do powłoki wewnątrz initramfs przed wykonaniem switch_root (dracut).
systemd.unit=Rozruch do określonego celu, np. rescue.target lub emergency.target.
nomodesetWyłącza KMS; jądro pozostaje w trybie bufora ramki (framebuffer) VESA/EFI. Przydatne przy debugowaniu sterowników GPU.
iommu=offWyłącza IOMMU. Istotne przy debugowaniu problemów z DMA.
loglevel=Poziom szczegółowości (verbosity) wczesnych komunikatów printk (0–7). Domyślnie zazwyczaj 4 (KERN_WARNING).
earlyprintk=Kieruje wczesne dane wyjściowe jądra na port szeregowy lub konsolę EFI, zanim uruchomiony zostanie bufor ramki.