Linux 系統從啟動到驅動加載
文章目錄
- Linux 系統從啟動到驅動加載
- 一、硬件上電與 BIOS/UEFI 階段
- 1. 1 硬件上電初始化
- 1.2 BIOS/UEFI執行過程
- 1.3 Bootloader加載細節
- 二、Bootloader 階段
- 三、Linux 內核初始化
- 3.1 架構相關初始化(setup_arch)
- 3.2 核心子系統初始化
- 3.3 虛擬文件系統(VFS)初始化
- 3.4 內核線程創建
- 四、驅動子系統初始化
- 4.1 設備模型初始化(driver_init)
- 4.2 總線子系統注冊
- 4.3 早期驅動初始化(early platform drivers)
- 4.4 設備描述信息解析
- 五、驅動加載與設備枚舉
- 5.1 總線驅動掃描硬件設備
- 5.2 設備與驅動匹配
- 5.3 驅動probe初始化
- 5.4 創建設備節點
- 六、用戶空間初始化
- 6.1 init 進程啟動
- 6.2 udev 設備管理
- 6.3 驅動模塊加載
- 6.4 系統啟動完成
- 七、關鍵數據結構與函數
- 八、典型驅動加載流程示例
- 九、調試與故障排查
- 9.1 查看內核啟動日志(dmesg)
- 9.2 檢查 sysfs 設備信息(/sys/devices)
- 9.3 使用 udevadm 監控設備事件
- 9.4 動態加載/卸載驅動模塊(insmod/rmmod)
一、硬件上電與 BIOS/UEFI 階段
1. 1 硬件上電初始化
當計算機電源接通后:
- 電源供應器完成自檢,向主板發送Power Good信號
- CPU從復位狀態解除,寄存器被初始化為默認值(如x86架構的CS=0xF000,EIP=0xFFF0)
- 主板時鐘發生器開始工作,提供穩定的時鐘信號
1.2 BIOS/UEFI執行過程
BIOS/UEFI固件執行的主要任務:
-
POST(Power-On Self Test):
- 檢測關鍵硬件:CPU、內存、顯卡等
- 聲卡發出"滴"聲表示檢測通過(不同BIOS廠商的提示音編碼不同)
- 對檢測到的硬件生成設備列表(如通過ACPI表)
-
硬件初始化:
- 配置內存控制器和初始化RAM
- 初始化基本輸入輸出設備(鍵盤、顯示器)
- 設置中斷向量表(IVT)或高級中斷控制器(APIC)
-
引導設備選擇:
- 按照CMOS中存儲的啟動順序(如:硬盤→USB→網絡)
- 讀取每個設備的引導扇區(MBR或GPT分區表的引導記錄)
- 對于UEFI系統,會查找ESP分區中的/EFI/BOOT/BOOTx64.EFI文件
1.3 Bootloader加載細節
當BIOS/UEFI找到有效啟動設備后:
- 傳統BIOS會將MBR中的第一階段引導程序(512字節)加載到內存0x7C00處
- UEFI則直接加載EFI應用程序到內存
- 對于GRUB2:
- 第一階段(boot.img)僅負責加載core.img
- 第二階段(core.img)包含基本文件系統驅動,用于定位/boot/grub
- 最終加載grub.cfg配置文件并顯示啟動菜單
示例啟動順序:
- 檢測到SATA硬盤
- 讀取MBR中的引導代碼
- 加載GRUB的stage1 → stage1.5 → stage2
- 顯示GRUB菜單等待用戶選擇
二、Bootloader 階段
-
引導加載器啟動:
- 系統 BIOS/UEFI 完成硬件初始化后,將控制權交給存儲在 MBR 或 EFI 分區中的引導加載器(如 GRUB2)
- 常見的引導加載器包括:
- GRUB (Grand Unified Bootloader)
- Syslinux
- LILO (Linux Loader)
- systemd-boot (適用于 UEFI 系統)
-
內核加載過程:
- 引導加載器讀取其配置文件(如 GRUB 的 grub.cfg)
- 加載壓縮的內核鏡像(通常在 /boot 目錄下,命名為 vmlinuz-<版本號>)到內存
- 同時加載 initramfs(Initial RAM Filesystem)鏡像
- 典型文件示例:
/boot/vmlinuz-5.4.0-91-generic /boot/initrd.img-5.4.0-91-generic
-
參數傳遞與內核啟動:
- 引導加載器向內核傳遞啟動參數,常見參數包括:
root=
:指定根文件系統設備(如 root=/dev/sda1)init=
:指定初始化程序路徑(替代默認的 /sbin/init)quiet
:減少啟動時內核信息輸出splash
:顯示啟動畫面
- 內核解壓過程:
- 首先解壓縮 zImage 或 bzImage 格式的內核
- 然后加載必要的驅動和初始化臨時根文件系統
- 最終控制權轉移到內核的入口點 start_kernel() 函數(位于 init/main.c)
- 引導加載器向內核傳遞啟動參數,常見參數包括:
-
initramfs 的作用:
- 提供早期用戶空間環境
- 包含必要的驅動程序(如磁盤控制器、文件系統驅動)
- 掛載真正的根文件系統
- 在嵌入式系統中可能直接作為最終根文件系統使用
-
特殊場景處理:
- 加密根分區:需要 initramfs 包含解密工具
- RAID/LVM:需要加載相應的模塊
- 網絡啟動:需要包含網絡驅動程序
三、Linux 內核初始化
3.1 架構相關初始化(setup_arch)
setup_arch() 是架構特定的初始化函數,主要負責:
- 解析硬件信息(如通過設備樹獲取CPU類型、內存布局等)
- 初始化物理內存管理(如建立memblock內存分配器)
- 設置處理器特殊功能(如開啟MMU、緩存等)
- 架構相關的早期設備初始化(如SMP處理器初始化)
示例:在ARM架構中會初始化處理器異常向量表,x86架構則會檢測和初始化ACPI。
3.2 核心子系統初始化
包括以下關鍵系統的初始化:
- 頁表初始化:建立內核地址空間映射,包括:
- 內核代碼段映射
- 設備I/O空間映射
- 早期內存分配區域映射
- 中斷控制器初始化:
- 探測并初始化本地APIC/IOAPIC(x86)
- GIC初始化(ARM)
- 設置中斷描述符表(IDT)
- 定時器子系統:
- 初始化高精度定時器(hrtimer)
- 設置系統時鐘源(如HPET、TSC)
- 校準CPU頻率(loops_per_jiffy)
3.3 虛擬文件系統(VFS)初始化
VFS初始化流程:
- 注冊基礎文件系統類型(如proc、sysfs、tmpfs)
- 初始化dentry緩存和inode緩存
- 掛載rootfs作為初始文件系統
- 創建標準文件描述符(stdin/stdout/stderr)
- 掛載實際根文件系統(通過root=內核參數指定)
3.4 內核線程創建
初始化的關鍵內核線程包括:
- kthreadd(內核線程守護進程,pid=2)
- 負責創建和管理其他內核線程
- 通過kthread_create()請求創建新線程
- 其他早期線程:
- ksoftirqd(軟中斷處理線程)
- kworker(工作隊列線程)
- migration(CPU遷移線程)
- watchdog(看門狗監控線程)
這些初始化步驟完成后,內核將進入用戶空間初始化階段,啟動第一個用戶進程(通常是init或systemd)。
四、驅動子系統初始化
4.1 設備模型初始化(driver_init)
該階段主要完成內核設備模型的核心數據結構初始化,包括:
- kobject 子系統初始化,建立設備層次結構基礎
- 設備類和屬性文件系統的創建(/sys/class/)
- 內核對象引用計數機制的建立
- 設備號分配器的初始化(devtmpfs)
4.2 總線子系統注冊
分平臺總線類型進行初始化:
-
platform_bus_init:
- 注冊平臺總線類型(platform_bus_type)
- 初始化 platform_device 和 platform_driver 的匹配機制
- 創建/sys/bus/platform/目錄結構
- 典型應用場景:SoC內置設備(如GPIO控制器、時鐘模塊)
-
pci_init:
- PCI總線探測和枚舉
- PCI設備資源分配(IO空間、內存空間)
- 建立PCI設備樹結構
- PCI設備驅動匹配機制初始化
4.3 早期驅動初始化(early platform drivers)
在基本設備模型建立后立即加載的關鍵驅動:
- 內存控制器驅動
- 串口調試驅動(earlycon)
- 時鐘源驅動
- 中斷控制器驅動
這些驅動通過early_platform_init
機制注冊,具有以下特性: - 在常規驅動加載前完成初始化
- 使用簡化版的資源獲取接口
- 通常通過命令行參數指定(如
earlycon=uart8250,mmio,0xfe001000
)
4.4 設備描述信息解析
根據系統類型采用不同配置機制:
設備樹(DT)系統:
- 解析/boot/dtb文件或U-Boot傳遞的設備樹
- 展開設備樹節點為device_node結構
- 將設備樹節點轉換為platform_device
- 處理設備樹中的中斷映射、DMA范圍等特殊屬性
ACPI系統:
- 解析ACPI表(DSDT、SSDT等)
- 轉換ACPI設備為平臺設備
- 處理_PRT(中斷路由)、_CRS(資源分配)等方法
- 支持ACPI電源管理擴展功能
兩種系統最終都會生成統一的設備資源描述,包括:
- 內存映射區域
- 中斷號
- DMA通道
- 時鐘源
- 電源管理參數
五、驅動加載與設備枚舉
5.1 總線驅動掃描硬件設備
總線驅動負責識別和掃描連接的硬件設備。常見方式包括:
- PCI設備枚舉:通過讀取PCI配置空間(Configuration Space)獲取設備Vendor ID、Device ID等信息
- 設備樹解析(DT node parsing):在ARM架構中解析設備樹(Device Tree)的節點信息
- ACPI枚舉:x86架構通過ACPI表獲取設備信息
- USB總線枚舉:通過USB協議發現連接的設備
示例:PCI枚舉過程中會遍歷所有總線號碼(bus number),對每個設備/功能組合讀取其配置寄存器。
5.2 設備與驅動匹配
內核通過driver_match_device()
函數進行匹配,主要依據:
- 設備樹中的
compatible
屬性 - ACPI設備ID(HID/UID/CID)
- PCI設備的Vendor/Device ID
- 平臺設備的名稱匹配
匹配優先級通常為:設備樹匹配 > ACPI匹配 > ID表格匹配 > 名稱匹配。
5.3 驅動probe初始化
匹配成功后調用驅動的probe()
函數進行:
- 資源分配(內存、IRQ、DMA等)
- 硬件初始化(寄存器配置、固件加載)
- 設備特定設置(時鐘、電源管理)
- 子系統注冊(如輸入設備、網絡設備等)
典型錯誤處理包括:檢查資源可用性、逐步回滾失敗操作。
5.4 創建設備節點
通過devtmpfs自動創建設備文件:
- 內核調用
device_add()
將設備注冊到系統 - 根據設備類型(字符/塊設備)創建
/dev
節點 - 設置正確的設備號(major/minor)
- 應用層可通過udev/mdev規則進一步配置節點屬性
特殊設備可能還需要:
- sysfs屬性文件(
/sys/class/...
) - debugfs接口
- 用戶空間通知機制(uevent)
六、用戶空間初始化
6.1 init 進程啟動
內核最后階段會啟動第一個用戶空間進程 init(PID=1),根據系統配置選用不同的 init 系統:
- systemd(現代主流發行版):采用并行化的服務啟動方式,提供單元(unit)管理
- 讀取
/etc/systemd/system/
和/usr/lib/systemd/system/
下的單元文件 - 啟動基礎目標(target)如
multi-user.target
或graphical.target
- 讀取
- sysvinit(傳統系統):串行執行
/etc/init.d/
中的啟動腳本- 通過運行級別(runlevel)控制啟動階段
- 典型流程:先掛載文件系統,再啟動基礎服務
6.2 udev 設備管理
udev 守護進程負責動態設備管理:
- 監聽內核通過 netlink 發送的 uevent
- 處理設備熱插拔事件(如 USB 插入)
- 根據
/etc/udev/rules.d/
規則文件:- 創建設備節點(如
/dev/sda1
) - 設置設備權限和所有者
- 觸發關聯的硬件初始化腳本
- 創建設備節點(如
6.3 驅動模塊加載
通過 modprobe
機制加載用戶態驅動模塊:
- 讀取
/etc/modprobe.d/
配置文件 - 自動解決模塊依賴關系
- 典型應用場景:
- 加載文件系統驅動(如 ext4、ntfs)
- 加載特殊硬件驅動(如 NVIDIA 顯卡)
- 加載網絡協議棧模塊
6.4 系統啟動完成
完成所有初始化步驟后:
- 啟動登錄管理器(如 gdm、lightdm)進入圖形界面
- 或顯示文本登錄提示符(tty1-6)
- 記錄啟動日志到
/var/log/boot.log
- 系統進入多用戶模式,所有服務正常運行
七、關鍵數據結構與函數
Linux設備驅動模型中的兩個核心數據結構及其關系:
/*** struct device_driver - 驅動對象的基本表示* @name: 驅動名稱,用于sysfs顯示和模塊匹配* @bus: 指向驅動所屬的總線類型,如platform_bus_type* @probe: 驅動探測回調函數,當設備與驅動匹配時調用* * 示例:當注冊i2c_driver時,需要設置這些字段*/
struct device_driver {const char *name;struct bus_type *bus;int (*probe)(struct device *dev);
};/*** struct bus_type - 總線類型的抽象表示* @name: 總線名稱,如"platform"、"pci"等* @match: 總線匹配函數,用于判斷設備和驅動是否兼容* * 總線負責管理其下的設備和驅動,典型的匹配過程:* 1. 設備注冊時,總線遍歷所有驅動,調用match函數* 2. 驅動注冊時,總線遍歷所有設備,調用match函數* 3. 匹配成功則調用驅動的probe函數*/
struct bus_type {char *name;int (*match)(struct device *dev, struct device_driver *drv);
};
工作流程說明:
- 系統初始化時會注冊各種總線類型(platform/pci/i2c等)
- 驅動開發者編寫驅動時:
- 定義device_driver結構體實例
- 實現probe函數進行設備初始化
- 向總線注冊該驅動
- 當匹配發生時,總線層會:
- 通過match函數驗證設備和驅動的兼容性
- 調用驅動的probe函數初始化設備
- 建立sysfs中的設備-驅動關聯
八、典型驅動加載流程示例
以下是一個完整的 PCI 設備驅動加載流程示例,展示了從驅動注冊到設備初始化的關鍵步驟:
// 1. 定義設備ID表,用于匹配支持的PCI設備
static const struct pci_device_id my_pci_ids[] = {{ PCI_DEVICE(0x10AB, 0x1100), .driver_data = 0 }, // 廠商ID 0x10AB,設備ID 0x1100{ PCI_DEVICE(0x10AB, 0x1200), .driver_data = 0 }, // 另一個兼容設備{ 0, } // 結束標記
};// 2. 定義驅動操作函數
static int my_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{int retval;// 使能PCI設備retval = pci_enable_device(pdev);if (retval) {dev_err(&pdev->dev, "Failed to enable PCI device\n");return retval;}// 獲取設備資源(如內存區域、中斷號等)// ...具體設備初始化代碼...dev_info(&pdev->dev, "Device successfully initialized\n");return 0;
}static void my_remove(struct pci_dev *pdev)
{// 釋放資源,關閉設備// ...清理代碼...pci_disable_device(pdev);dev_info(&pdev->dev, "Device removed\n");
}// 3. 定義PCI驅動結構體
static struct pci_driver my_driver = {.name = "my_device", // 驅動名稱.id_table = my_pci_ids, // 設備匹配表.probe = my_probe, // 設備發現時的回調.remove = my_remove, // 設備移除時的回調// 可選:.suspend/.resume 等電源管理回調
};// 4. 注冊PCI驅動
module_pci_driver(my_driver);// 5. 模塊信息
MODULE_DEVICE_TABLE(pci, my_pci_ids);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Sample PCI Device Driver");
典型工作流程:
- 內核加載驅動模塊時調用
module_pci_driver
宏注冊驅動 - 內核遍歷PCI總線,將每個設備與
id_table
進行匹配 - 找到匹配設備后,調用
probe
函數初始化設備 - 設備使用期間可能觸發中斷/處理請求
- 設備移除或模塊卸載時調用
remove
函數清理資源
注意事項:
- 必須實現基本的probe/remove函數對
- 需要正確處理設備使能/禁用流程
- 建議添加適當的錯誤處理和資源管理
- 現代驅動通常還需要實現電源管理回調
九、調試與故障排查
9.1 查看內核啟動日志(dmesg)
dmesg
命令是排查驅動程序問題的首選工具,它顯示內核環形緩沖區中的消息。典型使用場景包括:
- 查看驅動加載時的初始化信息:
dmesg | grep <驅動模塊名>
- 跟蹤設備插拔事件:
dmesg -w
(實時監控) - 檢查錯誤信息:
dmesg --level=err,warn
- 清空緩沖區:
dmesg -c
(需要root權限)
9.2 檢查 sysfs 設備信息(/sys/devices)
sysfs文件系統提供了設備樹的詳細信息:
/sys/devices/
├── platform
│ └── <設備名>/
│ ├── driver -> ../../../bus/platform/drivers/<驅動名>
│ ├── modalias
│ └── power/
重要檢查項:
- 設備是否被正確識別:
ls /sys/bus/<總線類型>/devices
- 驅動綁定狀態:查看設備目錄下的
driver
符號鏈接 - 設備參數:如
/sys/class/gpio/gpio<N>/
下的各種屬性文件
9.3 使用 udevadm 監控設備事件
udev工具鏈的典型應用:
# 監控所有設備事件
udevadm monitor# 查看特定設備屬性
udevadm info -a -p /sys/class/net/eth0# 觸發設備重掃描
udevadm trigger
關鍵調試場景:
- 檢查規則匹配:
udevadm test /sys/class/<設備類>/<設備名>
- 驗證熱插拔事件處理
- 調試udev規則執行過程
9.4 動態加載/卸載驅動模塊(insmod/rmmod)
驅動模塊管理操作流程:
# 加載模塊(需.ko文件路徑)
sudo insmod module.ko [參數名=參數值]# 查看已加載模塊
lsmod | grep <模塊名># 卸載模塊(需確保無設備在使用)
sudo rmmod module# 更常用的modprobe工具
sudo modprobe module_name
sudo modprobe -r module_name
注意事項:
- 模塊參數可以通過
/sys/module/<模塊名>/parameters/
查看或修改 - 依賴關系處理:
modprobe
會自動解決依賴 - 查看模塊信息:
modinfo <模塊名>
- 常見錯誤:模塊版本不匹配、符號未導出、資源沖突等
研究學習不易,點贊易。
工作生活不易,收藏易,點收藏不迷茫 :)