rootfs

📅 2026/7/30 1:30:31 👁️ 阅读次数 📝 编程学习
rootfs

注:本篇笔记只是在BusyBox + Buildroot基础上进行讲解

BusyBox,Buildroot和rootfs的关系

Buildroot 造出 rootfs;BusyBox 住在 rootfs 里,负责开机后当 pid 1。

Buildroot(PC 上构建) │ 编译 BusyBox、拷贝配置/脚本、打包 ▼ rootfs 镜像(ext4 / squashfs / tar…) │ 烧录 / 挂载 ▼ 板子上的 / ├── /sbin/init ← BusyBox 开机后的第一个程序 ├── /etc/inittab ├── /etc/init.d/rcS └── …

阶段一: 开机用户态的起点:/sbin/init

kernel_execve("/sbin/init") → busybox 启动 → 识别 applet 名 "init" → init_main(argc, argv) // 用户态真正入口,init.c里面

阶段二:init_main 主流程

int init_main(int argc, char **argv) { /* 屏蔽一批信号,延后处理(CAD / halt / reboot / CHLD…) */ sigprocmask(SIG_BLOCK, &G.delayed_sigset, NULL) if (getpid() != 1) die("must be run as PID 1"); // 内核约定:init 必须是 1 号进程 console_init(); set_sane_term(); xchdir("/"); setsid(); /* PATH / SHELL / USER 等环境 */ message(..., "init started: %s", bb_banner) if (单用户模式参数) new_init_action(RESPAWN, shell, ...); else parse_inittab(); // ★ 读 /etc/inittab,init 的任务清单 /* 一次性阶段 */ run_actions(SYSINIT); // 含 rcS ,开机服务总开关脚本 run_actions(WAIT) run_actions(ONCE); while (1) { run_actions(RESPAWN | ASKFIRST); /* 等信号、收尸、必要时重启 respawn 进程 */ check_delayed_sigs(...); waitpid(...); sleep1(); }

阶段三:解析 /etc/inittab

::sysinit:/bin/mount -t proc proc /proc ::sysinit:/bin/mount -o remount,rw / ::sysinit:/bin/mkdir -p /dev/pts /dev/shm ::sysinit:/bin/mount -a ::sysinit:/bin/mkdir -p /run/lock/subsys ::sysinit:/sbin/swapon -a null::sysinit:/bin/ln -sf /proc/self/fd /dev/fd null::sysinit:/bin/ln -sf /proc/self/fd/0 /dev/stdin null::sysinit:/bin/ln -sf /proc/self/fd/1 /dev/stdout null::sysinit:/bin/ln -sf /proc/self/fd/2 /dev/stderr ::sysinit:/bin/hostname -F /etc/hostname # now run any rc scripts ::sysinit:/etc/init.d/rcS ......

行格式:[id]:[runlevels]:[action]:[process]

字段BusyBox 含义
id终端节点(空 = 默认 console;ttyS0/dev/ttyS0
runlevels忽略(BusyBox init无 SysV runlevel
actionsysinit/wait/once/respawn/askfirst/shutdown/
process要执行的命令行
注:如果没有inittab的话,会有一套默认动作执行

阶段四:run_actions(SYSINIT) 跑通 sysinit

static void run_actions(int action_type) { for (a = G.init_action_list; a; a = a->next) { if (!(a->action_type & action_type)) continue; if (a->action_type & (SYSINIT | WAIT | ONCE | ...)) { pid_t pid = run(a); /* fork/vfork + 子进程 exec */ if (a->action_type & (SYSINIT | WAIT | ...)) waitfor(pid); /* SYSINIT:等子进程结束 */ } /* RESPAWN/ASKFIRST:只在 pid==0 时启动,不等待 */ } }

阶段五:rcS 按序号执行 init.d

只匹配Sxx(两位序号+名字),启动顺序根据字典序来排:S00S03S49S99
在本项目中,建立分区文件的脚本:buildroot/board/rockchip/rk3562/ctu-overlay/etc/init.d/S03ctu-mount等。UI合成器的有buildroot/package/weston/S49weston。都是两位序号+名字。也可以个性化编写脚本,跑系统喂狗或者开机软件等

阶段六:主循环 respawn / 关机 shutdown

主循环

while (1) { run_actions(RESPAWN | ASKFIRST); check_delayed_sigs(NULL); while ((wpid = waitpid(-1, NULL, WNOHANG)) > 0) { a = mark_terminated(wpid); /* RESPAWN 项 pid 置 0 → 下次再 run */ } sleep1(); }

关机

::shutdown:/etc/init.d/rcK ::shutdown:/sbin/swapoff -a ::shutdown:/bin/umount -a -r

rcK 反向对 S?? 调stop

Rootfs 的制作

注:制作在 PC 上完成;启动是板子每次上电跑已经做好的文件。二者别混。

制作视角:三者关系

Buildroot 造出 rootfs;BusyBox 被编进 rootfs,板上当/sbin/init(pid 1)。

BusyBox 源码 ──make──► busybox ELF + /bin/ls、/sbin/init … 链接 │ ▼ 装进某个目录(CONFIG_PREFIX / TARGET_DIR) 其余材料(skeleton、inittab、libc、overlay…)──► 完整 rootfs 树 │ ▼ mkfs / Buildroot 打镜像 rootfs.img(可烧录)

通用:只用 BusyBox 源码怎么编

cd busybox-x.y.z make menuconfig # 打开 Init→init;交叉编译设 Cross Compiler prefix make -j$(nproc) # 编出一个多呼 ELF mkdir -p /tmp/myroot make CONFIG_PREFIX=/tmp/myroot install

make install会:

  1. busybox拷进 CONFIG_PREFIX(常见/bin/busybox
  2. 按 .config 建/sbin/init/bin/sh/bin/ls… 链接指向它

交叉编译注意:

  • 必须用板子同架构工具链,别用宿主机 gcc 直接编目标程序
  • 可选CONFIG_STATIC=y静态链接(可不依赖目标机 libc,体积更大)
  • 没开CONFIG_INIT→ 没有/sbin/init,内核 exec 后起不来

凑齐可启动 rootfs(BusyBox alone 不够)

此时/tmp/myroot只有 BusyBox 那一层。还要:

需要作用
/etc/proc/sys/dev/tmp挂载点与配置落点
/etc/inittabinit 任务清单
/etc/init.d/rcS+S??*开机脚本
passwd / hostname登录、主机名
动态库(非静态时)否则一 exec 就缺 .so
设备节点或 mdev/udevconsole / 串口

手搓示意:

ROOT=/tmp/myroot # 已 make CONFIG_PREFIX=$ROOT install mkdir -p $ROOT/{etc/init.d,proc,sys,dev,tmp,root} # 放入 inittab(含 sysinit + getty respawn)、简易 rcS # 动态链接则拷 .so 到 $ROOT/lib # 再 mkfs.ext4 / 打包成镜像烧进去

Buildroot 怎么自动做上面这些

产品不天天手敲 CONFIG_PREFIX,Buildroot 内部等价:

Buildroot make ├─ 准备 TARGET_DIR(= 即将成为 rootfs 的目录树) ├─ 解压/配置 BusyBox ├─ CROSS_COMPILE=… make ├─ make CONFIG_PREFIX=$(TARGET_DIR) install ├─ 再装 inittab、initscripts、其它包、overlay └─ 按配置打成 ext4 / squashfs …

只重编 BusyBox:

cd buildroot make busybox-menuconfig make busybox-rebuild

本 SDK

./build.sh rootfs → mk-rootfs.sh → mk-buildroot.sh rockchip_kx_rk3562_sp → output/.../target/(TARGET_DIR)→ rootfs.img

Overlay(后写覆盖先写):

BR2_ROOTFS_OVERLAY="board/rockchip/common/base board/rockchip/rk3562/sp-overlay board/rockchip/rk3562/ctu-overlay"
想改改哪里
init / appletbusybox menuconfig / busybox 配置
早期启动动作package/busybox/inittab
开机服务overlay 或 package 的S??*(如 S03ctu-mount、S49weston)
整包选型rockchip_kx_rk3562_sp_defconfig

做完自检

ls -l …/target/sbin/init # 期望指向 busybox cat …/target/etc/inittab ls …/target/etc/init.d/

做出来的文件 ↔ 启动阶段

镜像里有启动时
/sbin/init(BusyBox)阶段一 init_main
/etc/inittab阶段二/三 parse_inittab → SYSINIT
/etc/init.d/rcS+S??*阶段四/五 按序号起服务
getty respawn阶段六 主循环保活登录