Linux 系统从开机到用户程序 main() 执行,理论上和实际应用中存在一定差异。下面将两者结合分析。


一、开机到内核初始化(Boot → init)

理论:

  1. BIOS/UEFI

    • 硬件自检(CPU、内存、外设)。

    • 加载 Bootloader(如 GRUB)。

  2. Bootloader

    • 解析内核映像,加载到内存。

    • 设置启动参数(kernel command line)。

    • 跳转内核入口 start_kernel()

  3. 内核初始化

    • start_kernel() 初始化:

      • 内存管理(页表、堆栈、malloc 内核空间)

      • CPU、时钟、中断

      • 调度器初始化

    • 创建第一个用户空间进程 init(PID=1)。

实际映射:

  • 系统上电 → Bootloader → 内核。

  • 内核完成初始化后,将控制交给 init

  • 我的系统中,这个阶段由 /sbin/init 或自定义启动脚本负责。


二、init → 启动脚本 → 模块启动

理论:

  • init 是用户空间的第一个进程。

  • 根据系统配置(SysVinit: /etc/inittab,Systemd: unit 文件)启动用户程序。

  • 启动过程中可能加载内核模块(insmodinit_module())。

实际:

  • 我的脚本中 init_module() 是用户空间的 程序启动封装,作用类似 init 脚本中启动服务。

  • 功能:

    1. 检查程序是否存在 (/usr/bin/$1)。

    2. 检查配置文件 (bootparam.sys) 是否禁用。

    3. 将启动记录写入 watchdog 文件。

    4. 对特定程序进行特殊处理(如 srosuinet 加 ASAN,srosdropbear 指定端口)。

    5. 背景启动或前台启动。

    6. 启动间隔 200ms 防止资源冲突。


三、execve → main

理论:

  • execve() 是内核系统调用,用于加载用户程序:

    • 解析 ELF 文件,分配内存空间。

    • 初始化栈和环境变量。

    • 指令指针指向 _start

  • _start 由 C 运行时(CRT)初始化:

    • 设置 argc/argv、环境变量。

    • 调用 main()

  • 用户程序真正开始执行。

实际:

  • 我的脚本中:

    $1 & # 或 $1

    • $1 是程序名,shell 会通过系统调用 execve() 启动程序。

    • 特殊程序可以附加参数或环境变量(如 ASAN)。

    • 启动后程序进入自己的 main(),执行业务逻辑。


四、模块管理与 Watchdog

  • 理论上,内核模块通过 init_module() 加载到内核空间。

  • 实际上,我的 init_module() 更像 用户空间模块管理器

    • WATCHDOG_EXEC_FILE 记录启动列表。

    • 可用于 watchdog 监控或重启。

    • 提供开关控制(通过 bootparam.sys)。


五、结合总结

阶段 理论机制 脚本实践
开机 → 内核 BIOS/UEFI → Bootloader → 内核初始化 硬件上电 → Bootloader → 内核启动
用户空间启动 init 启动脚本 → 系统服务 /init 或自定义 shell 脚本 → 调用 init_module() 启动程序
模块加载 内核模块 init_module() 用户程序封装启动:检查文件、配置 → $1 & / $1
execve → main 系统调用加载 ELF → _start → CRT → main Shell $1 调用 execve → 程序 _start → main
监控与控制 内核模块管理 Watchdog 文件 + 配置文件控制

关键理解:

  • 理论上每个阶段都是操作系统控制的内核/用户空间机制。

  • 实际系统中,我的 shell 脚本通过封装逻辑实现了“类似 init + 模块启动 + watchdog”功能。

  • $1 &$1 就是用户程序最终调用 execve() 并进入 main() 的入口。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐