Skip to content

Linux Kernel Boot Process

Page này mô tả quá trình boot từ góc nhìn của kernel: kernel nhận gì từ bootloader, tự khởi tạo thế nào và bàn giao cho user space ra sao. Cách dùng và cấu hình bootloader (U-Boot...) và các bước cụ thể theo từng board nằm ở phần Linux Embedded và Projects, page này chỉ link tới.

Tổng quan chuỗi boot

BootROM -> (SPL / FSBL) -> Bootloader (U-Boot) -> Linux kernel -> init -> user space
  • BootROM: mã cố định trong SoC, nạp tầng boot đầu tiên từ flash/SD/QSPI...
  • Bootloader (có thể nhiều tầng, ví dụ SPL/FSBL rồi U-Boot): khởi tạo DRAM và phần cứng tối thiểu, nạp kernel, device-tree (DTB), initramfs vào RAM rồi nhảy vào kernel.
  • Kernel: khởi tạo CPU, bộ nhớ, các subsystem, driver; mount root filesystem; chạy init.
  • init: tiến trình đầu tiên (PID 1), khởi chạy các dịch vụ user space.

Chi tiết các tầng đầu: Bootloader trong hệ nhúng Linux và U-Boot on Zynq.

Giao diện bootloader và kernel

Bootloader cần chuẩn bị trong RAM và truyền cho kernel:

Thành phần Mô tả
Kernel image Image (ARM64), zImage/uImage (ARM32), bzImage (x86), hoặc đóng gói trong FIT image
Device-tree blob (DTB) Mô tả phần cứng cho kernel, xem Device Model
initramfs (tùy chọn) Root filesystem tạm nằm trong RAM
Kernel command line Chuỗi tham số, xem Kernel Command Line

Khi nhảy vào kernel, địa chỉ DTB được truyền qua thanh ghi theo quy ước của từng kiến trúc (ví dụ ARM64: x0 chứa địa chỉ DTB; ARM32: r2).

Kernel khởi động

  1. Early arch setup: code assembly trong arch/<arch>/kernel/head.S bật MMU, dựng page table ban đầu, rồi nhảy vào code C.
  2. start_kernel() (init/main.c): khởi tạo lần lượt bộ nhớ, scheduler, IRQ, timer, console, parse command line, ...
  3. rest_init(): tạo hai thread kernel là kernel_init (sau này trở thành PID 1) và kthreadd (PID 2, cha của các kernel thread).
  4. kernel_init(): chạy các initcall, mount root filesystem, rồi exec chương trình init.

Initcall

Các hàm khởi tạo của built-in driver/subsystem được đăng ký bằng họ macro *_initcall và chạy theo thứ tự level:

Level Macro Ví dụ dùng
early early_initcall Khởi tạo rất sớm
0 pure_initcall
1 core_initcall Core subsystem
2 postcore_initcall
3 arch_initcall Code đặc thù kiến trúc/board
4 subsys_initcall Đăng ký subsystem, bus
5 fs_initcall File system
6 device_initcall module_init() của built-in driver
7 late_initcall Chạy sau cùng

Với built-in module, module_init(fn) được định nghĩa thành device_initcall (xem Kernel Module). Vì vậy bus/subsystem (subsys_initcall) luôn sẵn sàng trước khi driver đăng ký, và device-tree được kernel dùng để tạo các platform device trước khi probe() được gọi.

Để xem thời gian từng initcall, thêm initcall_debug vào command line.

Mount root filesystem và chạy init

  • Có initramfs: kernel giải nén initramfs vào RAM và chạy /init trong đó; script này tìm và mount root filesystem thật rồi switch_root.
  • Không có initramfs: kernel mount trực tiếp thiết bị trong root= (kèm rootfstype=, rootwait nếu thiết bị xuất hiện chậm, ví dụ SD/MMC).
  • Sau khi mount, kernel chạy chương trình init: thử init= trong command line, sau đó /sbin/init, /etc/init, /bin/init, /bin/sh. Nếu không có cái nào chạy được, kernel báo Kernel panic - not syncing: No working init found.
  • Phổ biến: systemd, BusyBox init, SysV init.

Debug quá trình boot

  • earlycon / earlyprintk: in log trước khi console thật được khởi tạo.
  • initcall_debug: in thời gian và kết quả từng initcall.
  • dmesg, tham số printk.time=1, loglevel=.
  • dyndbg=: bật pr_debug ngay lúc boot, xem Dynamic Debug.
  • Panic ngay khi mount root: kiểm tra root=, driver của thiết bị lưu trữ đã built-in chưa (nếu không dùng initramfs), và DTB có đúng không.