4、构建基础目录结构:创建标准目录

好,到了这一步,我们终于要开始动手搭建根文件系统的骨架了。说白了,就是创建那些你天天见、但可能从来没想过为什么存在的目录——/bin、/sbin、/usr、/etc、/lib、/dev、/proc、/sys 等等。

我刚开始做嵌入式的时候,觉得这步就是 mkdir 一下,没什么技术含量。后来有一次,我图省事少建了一个 /run 目录,结果某个服务启动时直接报错,排查了半天才发现是 tmpfs 挂载点没地方落。嗯,从那以后我再也不敢小看这个步骤了。

4.1 为什么需要这些目录?

你想想看,Linux 根文件系统不是随便扔一堆文件就完事的。它有一套约定俗成的规矩——FHS(Filesystem Hierarchy Standard)。这套标准规定了每个目录该放什么、不该放什么。

我个人的习惯是:先理解,再动手。下面这张图能帮你快速建立整体认知:

根文件系统标准目录结构 / (根目录) /bin /sbin /usr /etc /lib /dev /proc /sys /tmp /var /run /mnt 📌 说明: 绿色 = 用户命令相关 | 蓝色 = 系统软件层次 | 红色 = 配置/运行时数据 紫色 = 库文件 | 橙色 = 设备/临时/挂载点 | 青色 = 内核虚拟文件系统

4.2 各目录的职责

咱们一个一个来看。我按实际使用频率和重要性排个序:

目录 用途 典型内容 是否必须
/bin 用户级基础命令 ls, cp, mv, cat, sh ✅ 必须
/sbin 系统管理命令 init, mount, fdisk, ifconfig ✅ 必须
/usr 用户软件资源 头文件、库、文档、额外工具 ✅ 必须
/etc 系统配置文件 passwd, fstab, inittab ✅ 必须
/lib 共享库和内核模块 ld-linux.so, libc.so ✅ 必须
/dev 设备节点 console, null, ttyS0 ✅ 必须
/proc 进程和内核信息(虚拟) cpuinfo, meminfo, uptime ✅ 必须
/sys 设备和驱动信息(虚拟) class, block, devices ✅ 必须
/tmp 临时文件 运行时产生的临时数据 ⚠️ 建议
/var 可变数据 日志、缓存、锁文件 ⚠️ 建议
/run 运行时数据(tmpfs) pid 文件、socket ⚠️ 建议
/mnt 临时挂载点 U盘、SD卡挂载目录 🔧 按需

💡 我的经验:嵌入式系统里,/proc 和 /sys 虽然是虚拟文件系统,但目录必须提前创建好。否则内核启动时 mount 会失败,很多工具直接罢工。我见过有人把 /proc 漏了,结果 ps 命令报错,排查了半天才发现是目录没建。

4.3 动手创建目录

好了,理论说完了,咱们直接上命令。假设你的根文件系统暂存目录是 ~/rootfs:

# 创建基础目录
mkdir -p ~/rootfs/bin
mkdir -p ~/rootfs/sbin
mkdir -p ~/rootfs/usr
mkdir -p ~/rootfs/etc
mkdir -p ~/rootfs/lib
mkdir -p ~/rootfs/dev
mkdir -p ~/rootfs/proc
mkdir -p ~/rootfs/sys
mkdir -p ~/rootfs/tmp
mkdir -p ~/rootfs/var
mkdir -p ~/rootfs/run
mkdir -p ~/rootfs/mnt

# 验证结果
ls -la ~/rootfs/

执行完之后,你应该能看到类似这样的输出:

drwxr-xr-x  2 user user  4096 Mar 15 10:00 bin
drwxr-xr-x  2 user user  4096 Mar 15 10:00 dev
drwxr-xr-x  2 user user  4096 Mar 15 10:00 etc
drwxr-xr-x  2 user user  4096 Mar 15 10:00 lib
drwxr-xr-x  2 user user  4096 Mar 15 10:00 mnt
drwxr-xr-x  2 user user  4096 Mar 15 10:00 proc
drwxr-xr-x  2 user user  4096 Mar 15 10:00 run
drwxr-xr-x  2 user user  4096 Mar 15 10:00 sbin
drwxr-xr-x  2 user user  4096 Mar 15 10:00 sys
drwxr-xr-x  2 user user  4096 Mar 15 10:00 tmp
drwxr-xr-x  2 user user  4096 Mar 15 10:00 usr
drwxr-xr-x  2 user user  4096 Mar 15 10:00 var

⚠️ 权限说明:默认权限是 755(drwxr-xr-x),这没问题。但 /tmp 和 /var/tmp 建议改成 777,因为很多程序需要在这里创建临时文件。命令:chmod 1777 ~/rootfs/tmp。那个 1 是 sticky bit,防止用户乱删别人的临时文件。

4.4 /usr 目录的内部结构

/usr 是个大目录,里面还有子目录。我个人习惯一次性建好,省得后面补:

mkdir -p ~/rootfs/usr/bin
mkdir -p ~/rootfs/usr/sbin
mkdir -p ~/rootfs/usr/lib
mkdir -p ~/rootfs/usr/include
mkdir -p ~/rootfs/usr/share
mkdir -p ~/rootfs/usr/local

为什么要这么分?说白了,/usr 是给用户安装的软件用的。/usr/bin 放用户命令,/usr/sbin 放系统管理命令,/usr/lib 放库文件,/usr/include 放头文件——编译程序时要用。

4.5 设备节点的预创建

/dev 目录比较特殊。现代系统大多用 devtmpfs 或 udev 动态管理设备节点,但根文件系统刚启动时,内核还没挂载 devtmpfs,所以你得先手动创建几个关键节点:

# 创建控制台和 null 设备
sudo mknod -m 600 ~/rootfs/dev/console c 5 1
sudo mknod -m 666 ~/rootfs/dev/null c 1 3

# 验证
ls -la ~/rootfs/dev/

输出应该是:

crw------- 1 root root 5, 1 Mar 15 10:00 console
crw-rw-rw- 1 root root 1, 3 Mar 15 10:00 null

⚠️ 重要提醒:主设备号和次设备号不能写错。console 是 5,1,null 是 1,3。我曾经有一次把 console 的主设备号写成了 4,结果内核启动后控制台完全没有输出,屏幕一片黑。嗯,排查了半小时才发现是 mknod 参数错了。

4.6 创建过程的自动化脚本

每次手动敲这么多 mkdir 太累了。我建议你写个脚本,以后复用:

#!/bin/bash
# create_rootfs_dirs.sh - 创建根文件系统标准目录

ROOTFS_DIR="${1:-~/rootfs}"

echo "Creating standard directories under ${ROOTFS_DIR}..."

# 核心目录
mkdir -p ${ROOTFS_DIR}/{bin,sbin,etc,lib,dev,proc,sys}

# 扩展目录
mkdir -p ${ROOTFS_DIR}/{tmp,var,run,mnt}

# /usr 子目录
mkdir -p ${ROOTFS_DIR}/usr/{bin,sbin,lib,include,share,local}

# 设备节点
sudo mknod -m 600 ${ROOTFS_DIR}/dev/console c 5 1
sudo mknod -m 666 ${ROOTFS_DIR}/dev/null c 1 3

# 设置 /tmp 权限
chmod 1777 ${ROOTFS_DIR}/tmp

echo "Done. Directory structure created successfully."

保存为 create_rootfs_dirs.sh,然后:

chmod +x create_rootfs_dirs.sh
./create_rootfs_dirs.sh ~/rootfs

4.7 常见问题与避坑

  • 目录权限不对:有些程序要求 /tmp 可写,权限必须是 777。如果忘了,程序启动时会报 "Permission denied"。
  • 设备节点缺失:没有 /dev/console,内核启动后你看不到任何输出。没有 /dev/null,很多程序会崩溃。
  • /proc 和 /sys 没建:虽然它们是挂载点,但目录必须存在。否则 mount 命令会失败。
  • /run 目录被忽略:现在很多系统服务(如 systemd)依赖 /run 存放 pid 文件。没有它,服务可能无法正常启动。

💡 我的习惯:每次建完目录后,我会用 tree ~/rootfs 看一眼整体结构。如果没装 tree,用 find ~/rootfs -type d | sort 也行。一目了然,心里踏实。

好,目录结构搭好了。下一步就是往里面填充内容了——把编译好的 busybox、库文件、配置文件一个个放进去。不过那是后面章节的事,咱们先把基础打牢。