Linux 启动过程流程图
Linux启动过程详解
以下是Linux启动过程的详细树状图,从硬件上电开始,展示了内核初始化、设备驱动加载、用户空间启动以及应用程序与系统交互的完整流程。图中包含了关键函数调用、数据结构初始化和进程创建链。
[硬件上电]
└───> [BIOS/UEFI初始化]├─── POST自检│ ├─── 检测CPU、内存、显卡、硬盘等硬件│ ├─── 初始化基本输入输出系统│ └─── 确定启动设备顺序│└─── 引导加载程序(GRUB)├─── 从MBR/GPT读取引导代码├─── 加载GRUB核心模块│ ├─── 解析grub.cfg配置文件│ ├─── 显示启动菜单│ └─── 支持参数调整(如single模式)│└─── 加载内核和initrd├─── 读取/boot/vmlinuz (压缩内核映像)├─── 读取/boot/initrd (初始RAM磁盘)└─── 传递启动参数(如root=、ro、console=)│└───> [内核启动 - arch/x86/boot]├─── _start() [entry.S]│ ├─── 实模式初始化(16位)│ │ ├─── 设置堆栈指针│ │ ├─── 初始化段寄存器│ │ └─── 检测A20地址线│ ││ ├─── 切换到保护模式(32位)│ │ ├─── 加载GDT(全局描述符表)│ │ ├─── 设置CR0寄存器│ │ └─── 长跳转刷新CS寄存器│ ││ └─── 初始化保护模式环境│ ├─── 设置临时页表│ ├─── 加载IDT(中断描述符表)│ └─── 跳转到start_kernel()│└─── start_kernel() [init/main.c]├─── setup_arch() [架构相关初始化]│ ├─── 解析启动参数│ ├─── 初始化内存映射│ ├─── 检测CPU特性│ └─── 设置TLB和缓存│├─── setup_per_cpu_areas() [初始化CPU局部存储]│├─── mm_init() [内存管理初始化]│ ├─── memblock_init() [初始化内存块管理]│ ├─── setup_bootmem() [初始化引导内存分配器]│ ├─── page_alloc_init() [初始化页分配器]│ └─── slab_init() [初始化SLAB分配器]│├─── sched_init() [调度器初始化]│ ├─── 初始化调度类│ ├─── 创建idle进程│ ├─── 初始化运行队列│ └─── 设置调度策略│├─── trap_init() [异常和中断初始化]│ ├─── 设置IDT表项│ ├─── 注册异常处理函数│ └─── 初始化系统调用表│├─── init_IRQ() [中断控制器初始化]│ ├─── 配置中断控制器(如IOAPIC)│ ├─── 注册中断处理函数│ └─── 初始化中断向量│├─── time_init() [时钟初始化]│ ├─── 初始化系统时钟│ ├─── 设置定时器中断│ └─── 初始化jiffies│├─── console_init() [控制台初始化]│ ├─── 注册控制台驱动│ └─── 初始化打印函数│├─── vfs_caches_init() [虚拟文件系统缓存初始化]│ ├─── 初始化dentry缓存│ └─── 初始化inode缓存│└─── rest_init() [剩余初始化]├─── 创建内核线程│ ├─── kernel_init() [PID 1]│ ├─── ksoftirqd() [软中断处理]│ ├─── kworker() [工作队列处理]│ ├─── migration() [进程迁移]│ └─── watchdog() [看门狗定时器]│└───> [内核驱动初始化]├─── drivers_init()│ ├─── 初始化驱动核心框架│ ├─── 注册总线类型│ │ ├─── platform总线│ │ ├─── PCI总线│ │ ├─── USB总线│ │ ├─── I2C总线│ │ └─── SPI总线│ ││ └─── 初始化子系统│ ├─── 块设备子系统│ ├─── 字符设备子系统│ ├─── 网络子系统│ ├─── 输入子系统│ └─── 电源管理子系统│├─── 总线扫描与驱动匹配│ ├─── PCI总线扫描│ │ ├─── pci_early_init() [早期PCI初始化]│ │ ├─── pci_subsys_init() [PCI子系统初始化]│ │ ├─── pci_scan_bus() [扫描PCI总线]│ │ ├─── pci_device_probe() [探测PCI设备]│ │ └─── pci_register_driver() [注册PCI驱动]│ ││ ├─── USB总线扫描│ │ ├─── usb_init() [USB子系统初始化]│ │ ├─── usb_hub_init() [USB集线器初始化]│ │ ├─── usb_register_device_driver() [注册USB设备驱动]│ │ └─── hub_probe() [探测USB集线器]│ ││ └─── 其他总线扫描│├─── 驱动加载与初始化│ ├─── 内置驱动初始化│ │ ├─── 调用驱动的init函数│ │ ├─── 注册设备操作方法│ │ └─── 创建设备结构│ ││ ├─── 模块驱动加载│ │ ├─── request_module() [请求加载模块]│ │ ├─── load_module() [加载模块]│ │ ├─── module_init() [模块初始化]│ │ └─── init_module() [初始化函数入口]│ ││ └─── 驱动注册示例│ ├─── 字符设备驱动注册│ │ ├─── alloc_chrdev_region() [分配设备号]│ │ ├─── cdev_init() [初始化字符设备]│ │ ├─── cdev_add() [添加字符设备]│ │ └─── class_create() [创建设备类]│ ││ ├─── 块设备驱动注册│ │ ├─── register_blkdev() [注册块设备]│ │ ├─── alloc_disk() [分配磁盘结构]│ │ ├─── set_disk_operations() [设置磁盘操作]│ │ └─── add_disk() [添加磁盘]│ ││ └─── 网络设备驱动注册│ ├─── alloc_netdev() [分配网络设备]│ ├─── ether_setup() [以太网设备设置]│ ├─── register_netdev() [注册网络设备]│ └─── netdev_init() [网络设备初始化]│└─── 设备节点创建├─── class_create() [创建设备类]├─── device_create() [创建设备实例]└─── 在/dev目录下创建设备文件├─── 字符设备文件(如/dev/ttyS0)├─── 块设备文件(如/dev/sda)├─── 网络设备文件(如/dev/net/tun)└─── 其他设备文件(如/dev/input/event0)│└───> [initramfs处理]├─── 挂载initramfs├─── 执行早期脚本│ ├─── 加载必要驱动│ ├─── 初始化存储设备│ └─── 准备根文件系统│└─── 切换到真正的根文件系统│└───> [kernel_init() 继续]├─── 启动用户空间init进程│ ├─── 执行/sbin/init (systemd)│ │ ├─── 解析配置文件│ │ ├─── 启动基本服务│ │ └─── 进入默认target│ ││ └───> [systemd 启动流程]│ ├─── 启动sysinit.target│ │ ├─── systemd-udevd [设备管理]│ │ │ ├─── 监听内核uevent事件│ │ │ ├─── 根据规则创建设备节点│ │ │ └─── 配置设备属性│ │ ││ │ ├─── systemd-journald [日志服务]│ │ ├─── systemd-hwdb [硬件数据库]│ │ └─── systemd-sysctl [系统参数设置]│ ││ ├─── 启动basic.target│ │ ├─── systemd-logind [登录管理]│ │ ├─── systemd-user-sessions [用户会话]│ │ ├─── systemd-timesyncd [时间同步]│ │ └─── systemd-resolved [DNS解析]│ ││ └─── 启动default.target (graphical.target)│ ├─── systemd-display-manager [显示管理器]│ │ ├─── gdm.service [GNOME显示管理器]│ │ ├─── lightdm.service [轻量级显示管理器]│ │ └─── 创建Xorg/Wayland会话│ ││ └─── 用户登录│ ├─── 启动桌面环境│ │ ├─── GNOME│ │ ├─── KDE│ │ └─── Xfce等│ ││ └─── 执行用户应用程序│ ├─── shell (bash/zsh)│ ├─── 图形应用│ └─── 系统服务│ ├─── NetworkManager│ ├─── cron│ └─── sshd等│ ││ └───> [应用程序与系统交互]│ ├─── 系统调用│ │ ├─── 通过int 0x80或syscall指令触发│ │ ├─── 进入内核态执行服务例程│ │ └─── 返回用户态│ ││ ├─── 常见系统调用分类│ │ ├─── 文件操作(open, read, write, close)│ │ ├─── 进程控制(fork, exec, wait)│ │ ├─── 信号处理(kill, signal)│ │ ├─── 内存管理(brk, mmap)│ │ └─── 网络通信(socket, bind, connect)│ ││ ├─── 内核服务│ │ ├─── 文件系统│ │ │ ├─── VFS(虚拟文件系统)│ │ │ ├─── 具体文件系统(ext4, xfs)│ │ │ └─── 文件缓存机制│ │ ││ │ ├─── 进程管理│ │ │ ├─── 进程调度│ │ │ ├─── 进程间通信(IPC)│ │ │ └─── 信号机制│ │ ││ │ ├─── 内存管理│ │ │ ├─── 虚拟内存│ │ │ ├─── 物理内存分配│ │ │ └─── 页面置换算法│ │ ││ │ └─── 网络协议栈│ │ ├─── 链路层(以太网, WiFi)│ │ ├─── 网络层(IP, ICMP)│ │ ├─── 传输层(TCP, UDP)│ │ └─── 应用层(HTTP, FTP)│ ││ ├─── 设备驱动交互│ │ ├─── 通过/dev/ 目录下的设备文件访问│ │ ├─── 文件操作映射到驱动函数│ │ │ ├─── open() → 驱动的open方法│ │ │ ├─── read() → 驱动的read方法│ │ │ └─── ioctl() → 驱动的ioctl方法│ │ ││ │ └─── 中断处理机制│ │ ├─── 硬件触发中断│ │ ├─── 内核中断处理程序│ │ └─── 驱动的中断回调│ ││ └─── 硬件操作│ ├─── 内存映射IO(MMIO)│ ├─── 端口IO│ ├─── DMA操作│ └─── 中断控制
Linux 系统启动后的整体框架树状图
[Linux系统整体框架]
├─── [内核空间 (Kernel Space)]
│ ├─── 核心子系统
│ │ ├─── 进程调度子系统
│ │ │ ├─── 进程/线程管理
│ │ │ │ ├─── struct task_struct (进程描述符)
│ │ │ │ ├─── 进程状态管理
│ │ │ │ ├─── 进程上下文切换
│ │ │ │ └─── 进程优先级调度
│ │ │ │
│ │ │ ├─── 调度算法
│ │ │ │ ├─── CFS (完全公平调度器)
│ │ │ │ ├─── RT (实时调度器)
│ │ │ │ └─── IDLE (空闲调度器)
│ │ │ │
│ │ │ └─── 进程间通信(IPC)
│ │ │ ├─── 信号(Signal)
│ │ │ ├─── 管道(Pipe)
│ │ │ ├─── 消息队列(Message Queue)
│ │ │ ├─── 共享内存(Shared Memory)
│ │ │ └─── 套接字(Socket)
│ │ │
│ │ ├─── 内存管理子系统
│ │ │ ├─── 虚拟内存管理
│ │ │ │ ├─── 页表机制
│ │ │ │ ├─── 虚拟地址空间管理
│ │ │ │ └─── 页面置换算法(如LRU)
│ │ │ │
│ │ │ ├─── 物理内存管理
│ │ │ │ ├─── 内存区域(Zone)划分
│ │ │ │ ├─── 伙伴系统(Buddy System)
│ │ │ │ └─── SLAB/SLOB/SLUB分配器
│ │ │ │
│ │ │ └─── 内存映射
│ │ │ ├─── 文件映射
│ │ │ ├─── 匿名映射
│ │ │ └─── 设备内存映射
│ │ │
│ │ ├─── 文件系统子系统
│ │ │ ├─── VFS (虚拟文件系统)
│ │ │ │ ├─── struct inode (索引节点)
│ │ │ │ ├─── struct dentry (目录项)
│ │ │ │ ├─── struct file (文件对象)
│ │ │ │ └─── struct super_block (超级块)
│ │ │ │
│ │ │ ├─── 具体文件系统实现
│ │ │ │ ├─── ext4
│ │ │ │ ├─── xfs
│ │ │ │ ├─── btrfs
│ │ │ │ ├─── tmpfs
│ │ │ │ └─── NFS (网络文件系统)
│ │ │ │
│ │ │ └─── 文件缓存机制
│ │ │ ├─── 页缓存(Page Cache)
│ │ │ ├─── dentry缓存
│ │ │ └─── inode缓存
│ │ │
│ │ ├─── 网络协议栈
│ │ │ ├─── 链路层
│ │ │ │ ├─── 以太网驱动
│ │ │ │ ├─── WiFi驱动
│ │ │ │ └─── 帧处理
│ │ │ │
│ │ │ ├─── 网络层
│ │ │ │ ├─── IP协议
│ │ │ │ ├─── ICMP协议
│ │ │ │ ├─── 路由选择
│ │ │ │ └─── IP转发
│ │ │ │
│ │ │ ├─── 传输层
│ │ │ │ ├─── TCP协议
│ │ │ │ ├─── UDP协议
│ │ │ │ └─── 套接字API
│ │ │ │
│ │ │ └─── 应用层
│ │ │ ├─── HTTP协议
│ │ │ ├─── FTP协议
│ │ │ ├─── DNS协议
│ │ │ └─── 其他网络服务
│ │ │
│ │ └─── 设备驱动框架
│ │ ├─── 总线驱动
│ │ │ ├─── PCI总线
│ │ │ ├─── USB总线
│ │ │ ├─── I2C总线
│ │ │ └─── SPI总线
│ │ │
│ │ ├─── 设备类型驱动
│ │ │ ├─── 字符设备驱动
│ │ │ │ ├─── 串口驱动
│ │ │ │ ├─── 键盘驱动
│ │ │ │ └─── 鼠标驱动
│ │ │ │
│ │ │ ├─── 块设备驱动
│ │ │ │ ├─── 硬盘驱动
│ │ │ │ ├─── SSD驱动
│ │ │ │ └─── 光驱驱动
│ │ │ │
│ │ │ └─── 网络设备驱动
│ │ │ ├─── 网卡驱动
│ │ │ ├─── 无线网卡驱动
│ │ │ └─── 网络协议驱动
│ │ │
│ │ └─── 驱动管理机制
│ │ ├─── 驱动注册/注销
│ │ ├─── 设备探测
│ │ ├─── 驱动与设备匹配
│ │ └─── 设备文件创建
│ │
│ ├─── 系统调用接口
│ │ ├─── 系统调用表
│ │ ├─── 系统调用处理程序
│ │ └─── 用户空间访问方法
│ │ ├─── int 0x80 (32位)
│ │ ├─── syscall (64位)
│ │ └─── vDSO (虚拟动态共享对象)
│ │
│ └─── 中断与异常处理
│ ├─── IDT (中断描述符表)
│ ├─── 硬件中断处理
│ ├─── 软件异常处理
│ └─── 中断优先级管理
│
├─── [用户空间 (User Space)]
│ ├─── 系统服务
│ │ ├─── 初始化系统
│ │ │ ├─── systemd (PID 1)
│ │ │ ├─── 服务管理
│ │ │ ├─── 目标(target)管理
│ │ │ └─── 系统状态监控
│ │ │
│ │ ├─── 设备管理
│ │ │ ├─── udev (设备管理器)
│ │ │ ├─── 动态设备识别
│ │ │ └─── 设备节点创建
│ │ │
│ │ ├─── 日志管理
│ │ │ ├─── systemd-journald
│ │ │ ├─── rsyslog
│ │ │ └─── 日志存储与查询
│ │ │
│ │ ├─── 网络服务
│ │ │ ├─── NetworkManager
│ │ │ ├─── dhcpcd
│ │ │ ├─── sshd
│ │ │ └─── httpd (如Apache/Nginx)
│ │ │
│ │ ├─── 图形服务
│ │ │ ├─── Xorg/X11
│ │ │ ├─── Wayland
│ │ │ └─── 显示管理器(gdm/lightdm)
│ │ │
│ │ └─── 其他系统服务
│ │ ├─── cron (任务调度)
│ │ ├─── dbus (进程间通信总线)
│ │ ├─── avahi (零配置网络)
│ │ └─── cups (打印系统)
│ │
│ ├─── 应用环境
│ │ ├─── shell环境
│ │ │ ├─── bash
│ │ │ ├─── zsh
│ │ │ ├─── fish
│ │ │ └─── 命令行工具
│ │ │
│ │ ├─── 图形环境
│ │ │ ├─── 桌面环境
│ │ │ │ ├─── GNOME
│ │ │ │ ├─── KDE Plasma
│ │ │ │ ├─── Xfce
│ │ │ │ └─── LXDE
│ │ │ │
│ │ │ └─── 窗口管理器
│ │ │ ├─── i3
│ │ │ ├─── awesome
│ │ │ └─── Openbox
│ │ │
│ │ └─── 开发环境
│ │ ├─── 编译器(gcc, clang)
│ │ ├─── 解释器(Python, Ruby)
│ │ ├─── 构建工具(make, cmake)
│ │ └─── 版本控制(git)
│ │
│ └─── 库函数
│ ├─── C库
│ │ ├─── glibc (GNU C Library)
│ │ ├─── musl libc
│ │ └─── 系统调用封装
│ │
│ ├─── 图形库
│ │ ├─── GTK
│ │ ├─── Qt
│ │ ├─── OpenGL
│ │ └─── Vulkan
│ │
│ ├─── 网络库
│ │ ├─── libcurl
│ │ ├─── openssl
│ │ └─── libpcap
│ │
│ └─── 其他库
│ ├─── libxml2
│ ├─── sqlite3
│ ├─── ffmpeg
│ └─── SDL (多媒体)
│
└─── [交互机制]├─── 系统调用│ ├─── 用户空间 → 内核空间│ ├─── 提供特权操作接口│ └─── 通过统一API访问内核服务│├─── 进程间通信(IPC)│ ├─── 管道(Pipe)│ ├─── 消息队列(Message Queue)│ ├─── 共享内存(Shared Memory)│ └─── 套接字(Socket)│├─── 设备文件│ ├─── /dev/ 目录下的设备节点│ ├─── 字符设备文件(如/dev/ttyS0)│ ├─── 块设备文件(如/dev/sda)│ └─── 通过文件操作访问硬件│└─── 信号机制├─── 进程间异步通信├─── 常用信号(SIGINT, SIGTERM, SIGKILL)└─── 信号处理函数注册
关键数据结构与调用关系
-
内核关键数据结构
struct task_struct
:进程描述符,包含进程状态、优先级、内存信息等struct mm_struct
:内存管理结构,管理进程的虚拟地址空间struct file
:文件对象,表示打开的文件struct inode
:索引节点,存储文件元数据struct dentry
:目录项,实现文件路径查找struct device
:设备结构,表示系统中的物理或虚拟设备struct device_driver
:设备驱动,实现设备的操作方法
-
关键调用关系
request_module()
:动态加载内核模块register_chrdev()
:注册字符设备驱动register_blkdev()
:注册块设备驱动class_create()
:创建设备类device_create()
:创建设备节点sysfs_create_file()
:在sysfs中创建设备属性文件
1. 硬件上电与BIOS/UEFI阶段
1.1 硬件初始化
- 电源开启:按下电源按钮后,电源供应单元(PSU)开始向主板和其他组件供电。
- 硬件自检(POST):BIOS/UEFI固件执行加电自检,检测基本硬件(CPU、内存、硬盘、显卡等)。
- BIOS/UEFI初始化:初始化系统设备,设置中断向量表,读取CMOS/UEFI配置。
1.2 引导加载程序(Bootloader)加载
- MBR/GPT检测:BIOS/UEFI从启动设备(通常是硬盘)读取主引导记录(MBR)或GPT分区表。
- Bootloader加载:MBR中的引导代码加载GRUB/LILO等引导加载程序。
- GRUB菜单:显示可用内核选项,允许用户选择启动参数(如单用户模式、安全模式)。
2. 内核加载与初始化
2.1 内核加载
- 内核映像加载:GRUB从/boot分区加载内核映像(vmlinuz)和初始RAM磁盘(initrd/initramfs)。
- 控制权移交:BIOS/UEFI将控制权交给Linux内核。
2.2 内核初始化
- 早期初始化:内核解压并初始化内存管理、CPU调度等核心子系统。
- 设备检测:扫描并识别硬件设备,加载必要的驱动程序。
- 内存初始化:设置页表,初始化物理内存管理。
- 中断系统设置:配置中断控制器和中断处理程序。
2.3 驱动程序加载
- 内置驱动:加载编译进内核的驱动程序。
- 模块化驱动:通过initrd/initramfs加载必要的模块(如存储控制器驱动)。
- 根文件系统挂载:挂载根文件系统(通常是ext4、XFS等)。
3. 用户空间初始化
3.1 initramfs/initrd处理
- 临时根文件系统:initramfs提供临时根文件系统,包含早期启动所需的工具和驱动。
- 根文件系统准备:挂载真正的根文件系统,执行必要的文件系统检查。
3.2 init进程启动
- PID 1:内核启动第一个用户空间进程
init
(或systemd、upstart)。 - 运行级别设置:传统SysVinit根据
/etc/inittab
确定运行级别(0-6),现代系统使用targets替代。
3.3 系统服务启动
- SysVinit:按顺序执行
/etc/rc.d/rc*.d
目录下的脚本。 - systemd:并行启动独立单元(unit),通过依赖关系管理服务顺序。
- 关键服务:启动网络、日志、用户认证等基础服务。
4. 登录与用户会话
4.1 登录服务启动
- getty进程:启动终端登录服务(如tty1-tty6)。
- 图形界面:启动显示管理器(如GDM、LightDM),加载桌面环境(如GNOME、KDE)。
4.2 用户会话创建
- 用户认证:验证用户身份,创建shell会话。
- 环境设置:加载用户配置文件(
.bashrc
、.profile
等)。
5. 应用程序与系统交互
5.1 系统调用
- 用户态→内核态:应用通过系统调用(如
open()
、read()
)请求内核服务。 - 上下文切换:CPU从用户模式切换到内核模式执行特权操作。
5.2 设备驱动交互
- 设备文件:应用通过
/dev/
目录下的设备文件访问硬件(如/dev/sda
、/dev/ttyS0
)。 - 驱动接口:内核驱动提供统一的文件操作接口(read/write/ioctl)。
5.3 进程管理
- fork/exec:应用通过
fork()
创建子进程,通过exec()
加载新程序。 - 信号处理:进程间通过信号(如SIGTERM、SIGKILL)进行通信。
6. 系统启动后的整体框架
6.1 内核空间
- 核心子系统:内存管理、进程调度、文件系统、网络协议栈、设备驱动。
- 系统调用接口:提供用户空间访问内核功能的统一接口。
6.2 用户空间
- 系统服务:systemd、dbus、NetworkManager等守护进程。
- 应用环境:shell、图形界面、开发工具链、用户应用程序。
- 库函数:C库(glibc)、图形库(GTK、Qt)等提供应用编程接口。
7. 关键函数与程序加载过程
7.1 内核启动关键函数
start_kernel()
:内核入口点,初始化核心子系统。setup_arch()
:初始化硬件架构相关设置。mm_init()
:初始化内存管理系统。sched_init()
:初始化进程调度器。console_init()
:初始化控制台。rest_init()
:启动内核线程,最终调用kernel_init()
。
7.2 initramfs关键步骤
init
脚本执行:挂载根文件系统前的准备工作。- 模块加载:通过
modprobe
加载必要的驱动模块。 - 根文件系统挂载:将真正的根文件系统从磁盘/网络挂载。
7.3 systemd启动流程
systemd
初始化:解析配置文件,启动基本.target。- 依赖关系解析:分析并启动所有依赖的服务单元。
- 并行启动:无依赖关系的服务并行启动。
- 默认target激活:通常是graphical.target或multi-user.target。
8. 总结
Linux启动过程是一个从硬件到软件、从内核到用户空间的多层次初始化过程。理解这一过程有助于系统故障排查、性能优化和定制化配置。现代Linux发行版(如Ubuntu、Fedora)主要采用systemd作为初始化系统,相比传统SysVinit提供了更快的启动速度和更灵活的服务管理。
总结
这个树状图清晰展示了Linux系统从硬件上电到用户应用程序运行的完整启动过程,包括:
- 硬件初始化:BIOS/UEFI执行POST自检,加载引导程序
- 内核启动:从实模式到保护模式,初始化核心子系统
- 设备驱动加载:注册各类设备驱动,识别硬件
- 用户空间初始化:systemd启动各种系统服务和显示管理器
- 用户会话与应用程序:用户登录后运行shell和应用程序
- 系统交互:应用程序通过系统调用与内核和硬件通信
理解这个流程对于系统管理员和开发者非常重要,例如:
- 系统故障排查:可以根据启动流程定位问题出现的阶段
- 性能优化:了解各组件启动顺序,优化关键服务的启动时间
- 内核开发:掌握驱动注册和初始化的时机与方式