制作嵌入式Linux根文件系统

article/2025/9/19 19:57:44

文章目录

    • 1. 根文件系统布局
    • 2. 使用BusyBox生成二进制工具
      • 2-1. 获取BusyBox源码
      • 2-2. 配置BusyBox
        • 2-2-1. 选择编译静态库
        • 2-2-2. 选择交叉编译工具链
        • 2-2-3. 选择安装目录
        • 2-2-4. 编译安装
    • 3. 构建根文件系统
      • 3-1. 完善目录结构
      • 3-2. 添加C运行库文件
      • 3-3. 添加初始化配置脚本
        • 3-3-1. 修改/etc/inittab文件
        • 3-3-2. 修改/etc/init.d/rcS文件
        • 3-3-3. 修改/etc/fstab文件
        • 3-3-4. 修改/etc/profile文件
    • 4. 制作根文件系统镜像
      • 4-1. 根文件系统类型
      • 4-2. 制作UBIFS根文件系统镜像

1. 根文件系统布局

  嵌入式 Linux 根文件系统布局,建议还是按照FHS标准来安排,事实上大多数嵌入式Linux都是这样做的。但是,嵌入式系统可能并不需要桌面/服务器那样庞大系统的全部目录,可以酌情对系统进行精简,以简化Linux的使用。如嵌入式Linux文件系统中通常不会放置内核源码,因而存的 常不会放置内核源码,因而存的 常不会放置内核源码,因而存放源码的 /usr/src目录是不必要的, 甚至连头文件也不需要,即/usr/include目录也不必要;但是 /bin、/dev 、/etc、/lib 、/proc 、/sbin、/usr几个目录是不可或缺的。
  所以,允许嵌入式 Linux 对系统目录结构进行精简,以适应具体用场合的需求,一个典型的嵌入式Linux根文件系统目录如下所示:

典型嵌入式Linux根文件系统目录
目录
内容
bin系统命令和工具
dev系统设备文件
etc系统初始化脚本和配置文件
lib系统运行库文件
procproc文件系统
sbin系统管理员命令和工具
syssysfs文件系统
tmp临时文件
usr用户命令和工具,下分usr/bin和usr/sbin目录
var系统运行产生的可变数据

  要构建一个可用的Linux根文件系统,需要的二进制和库都不少,完全从零开始也是不现实的,推荐参考其它现有可用的文件系统,在原基础上按需修改;或者使用文件系统制作工具如 BusyBox 来实现文件系统的生成。

2. 使用BusyBox生成二进制工具

2-1. 获取BusyBox源码

  Busybox的官方源码下载路径为:https://busybox.net/downloads/。
  目前最新版本为busybox-1.29.3.tar.bz2。

2-2. 配置BusyBox

  解压源码,进入根目录

$ tar jxvf busybox-1.29.3.tar.bz2
$ cd busybox-1.29.3/

  首先,执行

$ make menuconfig

  进入图形化配置界面
图形化配置界面

2-2-1. 选择编译静态库

  进入

Settings --->

  使用空格键选择编译静态库

--- Build Options                                               
[*] Build static binary (no shared libs)  

  如图:
选择编译静态库

2-2-2. 选择交叉编译工具链

  在

Settings --->

  设置项下,填写交叉编译工具链前缀
选择交叉编译工具链

2-2-3. 选择安装目录

  在

Settings --->

  设置项下,找到

--- Installation Options ("make install" behavior) What kind of applet links to install (as soft-links)  --->  
(./_install) Destination path for 'make install' (NEW)  

默认为当前目录下目录,这里我使用默认_install目录
在这里插入图片描述

2-2-4. 编译安装

  退出保存后,执行编译

$ make

  大概几分钟后编译完成,执行

$ make install

  很快就会安装完成
在这里插入图片描述
  进入_install目录,查看生成的文件
在这里插入图片描述

  新建一个目录用来存放制作的根文件系统,可以命名为rootfs。将利用BusyBox生成的二进制文件及目录,即_install目录下的所有文件及目录复制到rootfs目录下。

3. 构建根文件系统

  使用BusyBox编译后,仅有 binsbinusr这 3个目录和软链接linuxrc,目录里都是二进制命令工具,这还不足以构成 一个可用的根文件系统,必须进行其它完善工作,才能构建一个可用的根文件系统。

3-1. 完善目录结构

  根据典型嵌入式Linux根文件系统目录,在rootfs目录中创建其他目录

$ mkdir dev etc lib proc sys tmp var

3-2. 添加C运行库文件

  库文件可直接从交叉工具链获取,一般在工具链的libc/lib/目录下。我这里是在ubuntu下安装的Linaro的交叉工具链
在这里插入图片描述

  库文件是在/usr/arm-linux-gnueabihf/lib/目录下,拷贝动态链接库文件(.so文件)到新制作的根文件系统根目录下/lib目录里

$ cp -a  /usr/arm-linux-gnueabihf/lib/*so* ./lib/

在这里插入图片描述

  这里只是拷贝动态链接库。一般开发程序使用动态编译需要板子上动态库的支持才能运行,所以拷贝动态库。而静态库一般在静态编译的时候用到,由于交叉编译的工作放在了PC上所以板子上不需要静态库,所以没有必要拷贝,这样还可以减小根文件系统的体积。
  一般使用gcc编译后的可执行文件、目标文件和动态库都带有调试信息和符号信息,这些在调试的时候用到,但是却增大了文件的大小。通常在PC上调试,或者调试时使用这些带有调试信息和符号信息的库文件,程序发布后使用去掉这些信息的库文件,可以大大缩小根文件系统的体积。这里我们去掉这些信息,方法是使用strip工具:

$ arm-linux-gnueabihf-strip ./*

3-3. 添加初始化配置脚本

  初始化配置脚本放在在/etc目录下,用于系统启动所需的初始化配置脚本。BusyBox提供了一些初始化范例脚本,在examples/bootfloppy/etc/目录下。将这些配置文件复制到 ”目录下。将这些配置文件复制到 ”目录下。将这些配置文件复制到新制作的根文件系统etc目录下

cp -a ../busybox/busybox-1.29.3/examples/bootfloppy/etc/* etc/

  添加后如图所示:
在这里插入图片描述

3-3-1. 修改/etc/inittab文件

  /etc/inittab文件是init进程解析的配置文件,通过这个配置文件决定执行哪个进程,何时执行。
  将文件修改为

# 系统启动时
::sysinit:/etc/init.d/rcS# 系统启动按下Enter键时
::askfirst:-/bin/sh# 按下Ctrl+Alt+Del键时
::ctrlaltdel:/sbin/reboot# 系统关机时
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r# 系统重启时
::restart:/sbin/init

  以上内容定义了系统启动时,关机时,重启时,按下Ctrl+Alt+Del键时执行的进程。

3-3-2. 修改/etc/init.d/rcS文件

#! /bin/sh# 挂载 /etc/fstab 中定义的所有文件系统
/bin/mount -a# 挂载虚拟的devpts文件系统用于用于伪终端设备
/bin/mkdir -p /dev/pts
/bin/mount -t devpts devpts /dev/pts# 使用mdev动态管理u盘和鼠标等热插拔设备
/bin/echo /sbin/mdev > /proc/sys/kernel/hotplug# 扫描并创建节点
/sbin/mdev -s

3-3-3. 修改/etc/fstab文件

  /etc/fstab文件存放的是文件系统信息。在系统启动后执行/etc/init.d/rcS文件里/bin/mount -a命令时,自动挂载这些文件系统。
  内容如下

# <file system>    <mount point>    <type>    <options>    <dump>    <pass>     
proc                  /proc          proc     defaults       0         0
sysfs                 /sys           sysfs    defaults       0         0
tmpfs                 /tmp           tmpfs    defaults       0         0
tmpfs                 /dev           tmpfs    defaults       0         0

  注:这里我们挂载的文件系统有三个proc、sysfs和tmpfs,在内核中proc和sysfs默认都支持,而tmpfs是没有支持的,我们需要添加tmpfs的支持。

3-3-4. 修改/etc/profile文件

  /etc/profile文件作用是设置环境变量,每个用户登录时都会运行它。
  将文件内容修改为

# 主机名
export HOSTNAME=zyz# 用户名
export USER=root# 用户目录
export HOME=/root# 终端默认提示符
export PS1="[$USER@$HOSTNAME:\$PWD]\# "    # 环境变量
export PATH=/bin:/sbin:/usr/bin:/usr/sbin# 动态库路径
export LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH

  

  因为指定了root用户的家目录为/root,所以需要创建该目录,否则执行cd ~时会失败

$ mkdir root

  登录系统后效果为

...
Please press Enter to activate this console.
[root@zyz:/]#
[root@zyz:/]# cd ~
[root@zyz:/root]#
[root@zyz:/root]# echo $PATH
/bin:/sbin:/usr/bin:/usr/sbin

  至此,根文件系统就基本构建好了。

4. 制作根文件系统镜像

4-1. 根文件系统类型

  如果文件系统已经布局完成, 则可以发到目标中了。通常会制作一个镜像然后通过某种方式固化到目标系统中,具体采用什么样的形发布需要根据资源状况、内核情况和系统需求等方面进行裁决:

  • 硬件方面,至少需要考虑主存储介质的类型和大小如Flash是NOR Flash还是NAND Flash,RAM的大小等。
  • 内核方面, 则需考虑所裁剪后的支持哪些文件系统采用种最合适, 能满足性、速度等要求。
  • 系统需求方面,要考虑运行速度、是否可写压缩等因素。
      常见的可用于根文件系统类型有ramdisk 、cramfs、jffs2 、yaffs/yaffs2和 ubifs等,各类型的特性如表所列。
类型介质是否压缩是否可写掉电保存存在于RAM中
Ramdisk
cramfs-
jffs2NOR Flash
yaffs/yaffs2NAND Flash
ubifsNAND Flash

  尽管文件系统固件以某一种文件系统的镜像发布,但是整个文件系统实际上还是并存多种逻辑文件系统的。例如,一个系统根文件系统以ubifs挂载,但是/dev目录却是以tmpfs挂载的、/sys目录挂载的是sysfs文件系统。
  现在,似乎ubifs是一种趋势。

4-2. 制作UBIFS根文件系统镜像

  Linux下制作UBIFS的命令有两个,mkfs.ubifsubinize

  • mkfs.ubifs,将一个目录制作为UBIFS文件系统。使用范例
$ mkfs.ubifs -m 2048 -e 128KiB -c 4096 -r ./rootfs -o rootfs.ubifs

  其中

-r, -d, --root=DIR       build file system from directory DIR(目录)
-m, --min-io-size=SIZE   minimum I/O unit size(最小输入输出单元大小)
-e, --leb-size=SIZE      logical erase block size(逻辑擦除块大小)
-c, --max-leb-cnt=COUNT  maximum logical erase block count(最大逻辑擦除块数目)
-o, --output=FILE        output to FILE(输出文件)

  所以制作ubifs镜像文件,需要知道3个关键参数,即最小输入输出单元大小逻辑擦除块大小最大逻辑擦除块数目,其中最大逻辑擦除块数目可由Flash分区大小逻辑擦除块大小计算出来,这些信息可以通过u-boot命令查看

=> mtdparts default
=> ubi part rootfs
  • ubinize,将mkfs.ubifs制作的UBIFS文件系统制作成含有卷标的可以直接烧写在Flash上的镜像。使用范例
$ ubinize -m 2048 -p 128KiB ubinize.cfg -o rootfs_ubifs.img

  其中

-o, --output=<file name>     output file name(输出文件)
-p, --peb-size=<bytes>       size of the physical eraseblock of the flash(物理擦除块大小)this UBI image is created for in bytes,kilobytes (KiB), or megabytes (MiB)(mandatory parameter)
-m, --min-io-size=<bytes>    minimum input/output unit size of the flashin bytes

  这里需要两个参数物理擦除块大小最小输入输出单元大小
  ubinize.cfg是配置文件,内容如下

[ubifs]
mode=ubi
image=rootfs.ubifs
vol_id=0
vol_size=1024MiB
vol_type=dynamic
vol_name=rootfs
vol_flags=autoresize

  说明

[ubifs]
mode=ubi
image=rootfs.ubifs       # mkfs.ubi生成的源镜像 
vol_id=0                 # 卷号
vol_size=1024MiB         # 卷大小,一般要设置的比分区大,防止有坏块
vol_type=dynamic         # 卷类型,动态卷
vol_name=rootfs          # 卷名,rootfs
vol_flags=autoresize     # 自动大小

  至此,根文件系统就制作好了,rootfs_ubifs.img可以烧写到flash使用。


http://chatgpt.dhexx.cn/article/f9atox1B.shtml

相关文章

[架构之路-30]:目标系统 - 系统软件 - Linux OS根文件系统rootfs的概念、组成、制作以及用busybox制作根文件系统

目录 前言&#xff1a; 第1章 什么是根文件系统 1.1 什么是文件 1.2 什么是文件系统 1.3 文件系统组织文件的方式&#xff1a;树形结构 1.4 统一的虚拟文件系统 1.5 物理存储介质与物理文件系统类型 1.5 什么是根文件系统 第2章 根文件系统的标准结构 2.1 根文件系统…

安装Ubuntu 16.04时出现:没有定义根文件系统,请到分区菜单修改

在安装Ubuntu 16.04时&#xff0c;尤其是选项空闲硬盘新建分区安装时&#xff0c;容易出现这种情况&#xff0c;这个是由于没有配置挂载点导致的&#xff0c;解决方法如下&#xff1a; 在挂在点输入“&#xff0f;”。 原理&#xff1a; Linux和Windows的文件系统不一样&#x…

Ubuntu提示“没有根文件系统 ”

安装Ubuntu时,提示“没有定义根文件系统” 原因:分区错误 解决: 将Ubuntu分区,删除重新创建分区,注意挂载点为“/” 此时就可以单击“继续”,进行下一步安装 以上分区方式,是没有交换分区的,下面提供两种带交换分区的分区方式: 第一种,包含4个分区,分别为 /分区(…

安装linux显示没有定义根文件系统,XP用Wubi安装Ubuntu提示“没有定义根文件系统,请返回分区菜单...

在安装Ubuntu时&#xff0c;到自定义分区一步时&#xff0c;会出现“没有根文件系统”&#xff0c;这时千万别将硬盘分区表重建&#xff0c;那样会让硬盘到数据都格式化到&#xff0c;你只需在ext4 或者 ext3 分区项上双击&#xff0c;加上挂载点为“\”就可以了。 有图有真相&…

在虚拟机安装中遇到的问题

问题一&#xff1a;在命令行模式下输入ifconfig时&#xff0c;显示系统不能识别这个命令&#xff0c;需要安装&#xff0c;输入sudo apt install net-tools。 问题二&#xff1a;sudo命令输入密码时&#xff0c;光标不移动&#xff0c;只要输入正确密码&#xff0c;回车就可以。…

安装Ubuntu时,提示“没有根文件系统 ”

安装Ubuntu时&#xff0c;提示“没有定义根文件系统” 原因&#xff1a;分区错误 解决&#xff1a; 将Ubuntu分区&#xff0c;删除重新创建分区&#xff0c;注意挂载点为“/” 此时就可以单击“继续”&#xff0c;进行下一步安装 以上分区方式&#xff0c;是没有交换分区的&a…

synchronized,voliate-详解

一.synchronized底层原理: synchronized关键字&#xff0c;在底层编译后的jvm指令中&#xff0c;会有monitorenter(枷锁)和monitorexit(释放锁)两个指令. monitorenter指令执行的时候会干什么呢? 每个对象都有一个关联的monitor&#xff0c;比如一个对象实例就有一个monitor…

volatile的作用及原理

前言 voliate关键字的两个作用&#xff1a; 1、 保证变量的可见性&#xff1a;当一个被volatile关键字修饰的变量被一个线程修改的时候&#xff0c;其他线程可以立刻得到修改之后的结果。当一个线程向被volatile关键字修饰的变量写入数据的时候&#xff0c;虚拟机会强制它被值刷…

voliate和synchronized

线程安全考虑三个方面&#xff1a;原子性&#xff0c;可见性&#xff0c;有序性 为什么使用voliate关键字&#xff1f; 正常情况下编译器为了加快程序运行的速度,对一些变量的写操作会先在寄存器或者是CPU缓存上进行,最后才写入内存.而在这个过程,变量的新值对其他线程是不可…

关于voliate关键字

现象 private static boolean is false;public static void main(String[] args) {new Thread(new Runnable() {Overridepublic void run() {System.out.println("thread 1 start");while (!is) {}System.out.println("thread 1 end");}}).start();try {T…

并发专题之---Voliate引发的各种原理问题

文章目录 前言JMMvoliate不保证原子性不保证原子性的解释AtomicInteger解决不保证原子性的问题为什么AtomicInteger可以解决原子性问题?CASCAS的内部原理CAS的缺点ABA问题原子引用解决ABA问题 禁止指令重排多线程环境下单例模式出现的问题双端检索机制解决办法双端检索机制的隐…

voliate类型的原理及用法

voliate类型&#xff0c;机器才读取执行代码读取内存后&#xff0c;将读取的内容存到高速缓冲区里&#xff0c;在硬件里是寄存器&#xff0c;这样在一下次读取的时候就可以直接从高速缓存区里面读取&#xff08;cache&#xff09;,这也是读取速度加快的原因&#xff0c;但是如果…

voliate(轻量级的同步机制)

voliate特点: JMM内存模型三大特性&#xff1a;可见性&#xff0c;原子性&#xff0c;有序性。 1.禁止指令重排序&#xff1a; voliate实现禁止指令重排&#xff0c;避免多线程环境下程序出现乱序执行的现象。 多线程环境中线程交替执行&#xff0c;由于编译器优化重排的…

voliate工作实际应用场景

哈喽大家好&#xff0c;我是IT老哥&#xff0c;今天我们来讲讲面试必问的voliate 单线程的情况下呢&#xff0c;我们肯定用不到这个voliate 只有在多线程的情景下才能用到&#xff0c;文章结尾我会举一个经典的案例 voliate三特性 保证可见性&#xff1b;不保证复合操作的原子性…

voliate原理

voliate原理 voliate 当使用voliate关键字修饰共享变量(实例变量、静态变量)时&#xff0c;它将具备两个特性&#xff1a;可见性和禁止指令重排序优化 1.可见性 变量被修改后&#xff0c;会立即保存在主存中&#xff0c;并清除工作内存中的值。新值对于线程来说都是可见的…

C语言关键字之voliate

C语言关键字之voliate voliate的作用是作为指令关键字&#xff0c;确保本条指令不会因为编译器的优化而省略&#xff0c;而且要求每次从内存中直接读取值 当使用voliate 声明变量值时&#xff0c;系统总是重新从它所在的内存读取数据&#xff0c;直接访问变量地址&#xff0c…

java中voliate的讲解

Java并发编程&#xff1a;volatile关键字解析 volatile这个关键字可能很多朋友都听说过&#xff0c;或许也都用过。在Java 5之前&#xff0c;它是一个备受争议的关键字&#xff0c;因为在程序中使用它往往会导致出人意料的结果。在Java 5之后&#xff0c;volatile关键字才得以重…

多线程 - voliate 关键字

介绍 volatile 是 Java 中的关键字&#xff0c;用于修饰变量。它的作用是强制对被修饰的变量的写操作立即刷新到主存中&#xff0c;并强制对该变量的读操作从主存中读取最新的值&#xff0c;而不是使用缓存中的值。 作用 保证变量的可见性&#xff1a; 可见性指的是多个线程…

volatitle

被volatitle修饰的变量能够保证可见性&#xff0c;不保证原子性&#xff0c;每个线程能够获取该变量的最新值。 实现的机制&#xff1a;在写volatitle变量写到主内存时&#xff0c;指令前会加上lock&#xff0c;该指令有两个影响&#xff1a; 将当前处理器缓存行的数据写回系统…

volatile

一、不得不提的volatile volatile是个很老的关键字&#xff0c;几乎伴随着JDK的诞生而诞生&#xff0c;我们都知道这个关键字&#xff0c;但又不太清楚什么时候会使用它&#xff1b;我们在JDK及开源框架中随处可见这个关键字&#xff0c;但并发专家又往往建议我们远离它。比如…