嵌入式 Linux 开发 6:定制根文件系统
简介根文件系统
根文件系统是 kernel 挂载的第 1 个文件系统,挂载的位置是文件系统层次结构的顶端,表示为 /
Linux 要求根文件系统中包含应用程序和工具软件,通过它们来引导系统,初始化系统服务,加载设备驱动程序和挂载额外的文件系统。
从零开始构建根文件系统是一个艰巨的任务,为了专注【将产品尽快出货】这个头等目标,使用厂商提供的根文件系统可以节省大量的时间和精力。
如果厂商提供的根文件系统尺寸合适,包含了设备必需的组件,特别是 C 程序库(常见为 uClibc)。
最佳的方法是建立应用程序,将其复制到现在的根文件系统,修改启动脚本,并重建根文件系统的二进制镜像。
尽管有 Yocto Buildroot 等构建 Linux 发布版工具,然而它们的学习曲线大致与【自行构建 Linux 系统】相当。
后者学习到的经验更容易应用于 Linux 的其它领域,这包括:交叉编译器,内核,根文件系统,部署 MTD 镜像。
根文件系统顶层目录
目录 | 内容 |
---|---|
bin | 用户和系统管理员必备的二进制文件 |
sbin | 系统管理员必备的二进制文件 |
usr | 大量应用程序和文件 |
dev | 设备文件 |
etc | 系统配置和启动文件 |
home | 用户主目录 |
lib | 程序库和内核模块 |
proc | 描述内核与进程信息的虚拟文件系统 |
sys | 描述总线/设备/驱动程序的虚拟文件系统 |
tmp | 临时文件 |
var | 系统日志和临时配置文件 |
小窍门
在嵌入式 Linux 系统中 var 一般是软链接指向 tmp@SDRAM 这样,避免 FLASH 耗损和提高 IO 效率。
先进入 var 目录,再创建子目录如 ln -s ../tmp ./log
这是相对路径 ../tmp 而不是绝对路径 /tmp(在 NFS 场景下这是开发主机目录)
烧录文件系统
定制根文件系统流程
- 根据应用需求选择文件系统类型,请链接Linux 文件系统类型
- 配置 kernel 支持该文件系统
- 将数据(文件和目录)转换成所选文件系统格式的镜像文件
- 将镜像文件烧录到目标系统上的 MTD 储存设备
- 配置 u-boot 通过命令行参数使 kernel 启动 rootfs
定制 yaffs2 和 squashfs
配置 kernel 支持 yaffs2 和 squashfs
File systems--->Miscellaneous filesystems--->yaffs2 file system support:Autoselect yaffs2 format:Enable yaffs2 xattr support
配置 kernel 支持 squashfs
File systems--->Miscellaneous filesystems--->squashed file system support:Include support for LZO compressed file systems
生成 yaffs2 镜像文件
mkyaffs2 $SOURCE $IMAGE --inband-tags -p 2048
生成 squashfs 镜像文件
mksquashfs $SOURCE $IMAGE -comp lzo -no-xattrs
配置 u-boot 通过命令行参数使 kernel 启动 yaffs2 根文件系统
bootargs=noinitrd root=/dev/mtdblock2 rootfstype=yaffs2 rootflags=inband-tags console=ttyS0 rdinit=/sbin/init mem=64M mtdparts=nand0:2M(u-boot),20M(kernel),40M(rootfs),2M(config),-(log) ignore_loglevel
配置 u-boot 通过命令行参数使 kernel 启动 squashfs 根文件系统
bootargs=noinitrd root=/dev/mtdblock2 rootfstype=squashfs console=ttyS0 rdinit=/sbin/init mem=64M mtdparts=nand0:2M(u-boot),20M(kernel),40M(rootfs),2M(config),-(log) ignore_loglevel
经典的文件系统布局
下图是一个使用了 Flash 芯片的嵌入式 Linux 系统典型布局。
- 具有系统软件的根文件系统被放在一个只读,经压缩的 SquashFS 文件系统中
- 配置文件被储存在不同分区上一个可供读写的 YAFFS2 文件系统中
- 临时文件被储存在使用 TmpFS 的 RAM 中
+-----------+
| SqushFS |
| 只读压缩 |
| 根文件系统 |
+-----------+ Flash
| YAFFS2 |
| 可读可写 |
| 配置文件 |
+-----------+
+-----------+
| TmpFS |
| 可读可写 | RAM
| 临时数据 |
+-----------+
配置网络
配置嵌入式 Linux 网络静态 IP 地址的脚本
#!/bin/sh
HOSTNAME=<your_host_name>
DOMAINNAME=<your_domain_name>
IP=192.168.1.99
NETMASK=255.255.255.0
GATEWAY=192.168.1.1
NS1=114.114.114.114
NS2=8.8.8.8
# Sets the hostname
echo 127.0.0.1 $HOSTNAME.$DOMAINNAME $HOSTNAME > /etc/hosts
/bin/hostname $HOSTNAME
# Sets up the loopback interface
/sbin/ifconfig lo 127.0.0.1 up
/sbin/route add -net 127.0.0.0 netmask 255.0.0.0 lo
# Sets up our IP and default gateway
/sbin/ifconfig eth0 $IP up
/sbin/route add default gw $GATEWAY
echo nameserver $NS1 > /etc/resolv.conf
echo nameserver $NS2 > /etc/resolv.conf
动态设备管理 udev
背景
Linux 系统 /dev 存放着设备节点的特殊文件,它是指向实际设备驱动程序的“指针”,而设备驱动程序控制和管理了应用程序对设备的访问。
在不久之前,脚本文件(如 MAKEDEV)静态创建 /dev 下大量的设备节点,而它们对应的设备实际上并不存在,给设备管理带来混乱。
为了解决这一问题,开发了 udev 设备管理子系统,它能够根据系统中实际存在的硬件动态创建 /dev 目录中的条目。
udev 工作原理
- 当内核发现一个新的设备时,它会创建一个 uevent 事件,并通过 netlink 套接字将它发送给一个用户空间的侦听者,它正是 udev。
- 当 udev 接收到 uevent 时,它会扫描其规则数据库:
A 如果 udev 使用设备的属性在数据库中查找了匹配的条目,这些条目规定了它要执行的动作。
B 如果 udev 找不到任何匹配的规则,udev 的默认动作只是创建一个设备节点,其名称由内核提供,主次设备号由 uevent 指定。
启动 mdev
拥有强大的规则引擎的 udev 一般用于 Linux 工作站,嵌入式 Linux 经常使用 busybox 版的 udev,它就是 mdev。
和 udev 一样,mdev 需要内核支持 sysfs 和热插拔。发现设备时,mdev 会在 /dev 目录中动态创建设备节点。
下面的代码清单是一个使用 mdev 的示例启动脚本。
#!/bin/sh
# mount virtual file systems
mount -t proc /proc /proc
mount -t sysfs /sys /sys
mount -t tmpfs -o size=80% /tmp /tmp # prevent greedy one from eating up all RAM
mount -t devpts /pts /dev/pts # for SSH/Telnet
# mount /dev as a tmpfs
mount -n -t tmpfs -o mode=0755 udev /dev
# copy default static devices, which were duplicated here
cp -a -f /lib/udev/devices/* /dev
# set hotplug agent
echo "/bin/mdev" > /proc/sys/kernel/hotplug
# start busybox mdev
/bin/mdev -s
前 4 行命令创建了 4 个虚拟文件系统:/proc /sys /tmp /dev/pts
第 5 行命令将 tmpfs 挂载到 /dev 使其成为一个空目录
第 6 行命令复制一组静态设备节点到 /dev 目录
第 7 行命令将热插拔代理设置为 /bin/mdev
第 8 行命令引导 mdev 处理所有自启动后“悬而未决”的内核 uevent
配置 mdev
一个名为 /etc/mdev.conf 的配置文件用来定制 mdev 的行为,如下例所示
.* 0:0 777
将默认的访问权限改为 777 默认的 user:group 则保持不变,还是 root:root
mouse* 0:0 660 input/
将所有鼠标设备移动到 /dev 目录的 input 子目录中。
系统关机
有几个 Linux 工具可以帮助实现关机操作,包括 shutdown halt 和 reboot 命令。关机流程如下所示:
- 关机进程会向所有的进程发送 SIGTERM 信号,让用户进程完成自身的关机操作,比如关闭文件,保存状态等。
- 关机进程会向所有的进程发送 SIGKILL 信号,终止用户进程。
- 关机进程卸载所有已经挂载的文件系统,并调用与具体架构相关的关机或重启函数。