LV05-01-Linux系统-01-linux系统组件

本文主要是Linux系统——linux系统组件的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
PC端开发环境 Windows Windows11
Ubuntu Ubuntu20.04.2的64位版本
VMware® Workstation 17 Pro 17.6.0 build-24238078
终端软件 MobaXterm(Professional Edition v23.0 Build 5042 (license))
Win32DiskImager Win32DiskImager v1.0
Linux开发板环境 Linux开发板 正点原子 i.MX6ULL Linux 阿尔法开发板
uboot NXP官方提供的uboot,使用的uboot版本为U-Boot 2019.04
linux内核 linux-4.19.71(NXP官方提供)
点击查看本文参考资料
分类 网址 说明
官方网站 https://www.arm.com/ ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档
https://www.nxp.com.cn/ NXP官方网站
https://www.nxpic.org.cn/NXP 官方社区
https://u-boot.readthedocs.io/en/latest/u-boot官网
https://www.kernel.org/linux内核官网
其他网站 kernel - Linux source code (v4.15) - Bootlin linux内核源码在线查看
点击查看相关文件下载
分类 网址 说明
NXP https://github.com/nxp-imx NXP imx开发资源GitHub组织,里边会有u-boot和linux内核的仓库
nxp-imx/linux-imx/releases/tag/v4.19.71 NXP linux内核仓库tags中的v4.19.71
nxp-imx/uboot-imx/releases/tag/rel_imx_4.19.35_1.1.0 NXP u-boot仓库tags中的rel_imx_4.19.35_1.1.0
I.MX6ULL i.MX 6ULL Applications Processors for Industrial Products I.MX6ULL 芯片手册(datasheet,可以在线查看)
i.MX 6ULL Applications ProcessorReference Manual I.MX6ULL 参考手册(下载后才能查看,需要登录NXP官网)
Source Code https://elixir.bootlin.com/linux/latest/source linux kernel源码
https://elixir.bootlin.com/u-boot/latest/source uboot源码

一个完整的linux系统,通常包含了Uboot、kernel、**.dtb以及根文件系统, 它们是系统运行需要的4个基础组件。

一、U-Boot

1. U-Boot简介

U-Boot ,全称 Universal Boot Loader,是遵循GPL条款的开源项目,U-Boot是 从FADSROM、8xxROM、PPCBOOT逐步发展演化而来的。

U-Boot发展至今,已经可以 实现非常多的功能,在操作系统方面,它不仅支持嵌入式Linux系统的引导,还支持NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS, Android等嵌入式操作系统的引导。在CPU架构方面 ,U-Boot支持PowerPC、MIPS、x86、ARM、NIOS、XScale等诸多常用系列的处 理器。

U-Boot的主要作用是用来启动操作系统内核,它分为两个阶段,即boot + loader,boot阶段启动系统,初始化硬件设备,建立内存空间映射图,将系统的 软硬件带到一个合适的状态,loader阶段将操作系统内核文件加载至内存,之后跳转到内核所在地址运行。

当一个开发板上电的时候,即使是执行非常简单的程序,都需要进行很多初始化的 操作,如初始化时钟、初始化存储控制器、将代码拷贝到运行内存中等操作,大多数 处理器在上电的时候都会有默认的指令执行位置,如ARM架构的处理器会从0x00000000地址开始读取第一条指令。

一般来说BootLoader必须提供系统上电时的初始化代码,在系统上电时初始化相关 环境后,BootLoader需要引导完整的操作系统,然后将控制器交给操作系统。 简单来说BootLoader是一段小程序,它在系统上电时执行,通过这段小程序可以将硬件 设备进行初始化,如CPU、SDRAM、Flash、串口、网络等,初始化完毕后调用操作系统内核。 另外,某些BootLoader可能含有一些高级特性,如校验操作系统镜像,从多个操作系统镜像中选择引导合适的操作系统, 甚至可以添加网络功能,让系统自主从网上寻找合适的镜像并且进行引导等等。

2. 两种模式

在开发的时候,我们可能需要利用一些命令去操作BootLoader,让BootLoader完成一 些事情,我们可以称这种模式为下载模式,比如从内存中的ELF镜像启动,从内存启动 应用程序镜像,使用BOOTP/TFTP协议通过网络启动镜像,打印控制台设备和信息,打印 有关文件系统的信息,列出目录中的文件等等。

当然,下载模式是对于开发人员才有意义,而对于用户则不需要这些操作,直接可以启动 操作系统运行,这种直接启动操作系统的模式可以称之为启动模式

U-Boot的 功能十分强大,它可以同时支持下载模式与启动模式,并且可以切换模式,它可以在系统上电 时默认等待N秒(N可以随意设置),在这N秒内没有命 令(如按下任意按键、串口发送任意数据等)去操作BootLoader时,U-Boot将启动默认 的操作系统内核(如Linux)。当有命令操作BootLoader时,系统将进入下载模式,在这 个模式下,BootLoader将不会继续启动操作系统内核,而是由开发者去指定BootLoader的工作,如通过串口下 载操作系统镜像,通过网络启动镜像等操作,如下图所示。

image-20241030225020716

而当N秒内没有命令去操作BootLoader时,BootLoader将启动默认的操作系统,并将 控制块交给操作系统,显然,在产品发布时BootLoader是工作在启动模式下的,这种模 式是不需要用户通过BootLoader的命令去控制BootLoader,而是直接启动操作系统即可,具体见下图:

image-20241030225115944

3. 两个阶段

U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下:

  • 第一阶段

(1)硬件设备初始化。

(2)加载U-Boot第二阶段代码到RAM空间。

(3)设置好栈。

(4)跳转到第二阶段代码入口。

  • 第二阶段

(1)初始化本阶段使用的硬件设备。

(2)检测系统内存映射。

(3)将内核从存储区域(Flash、SD Card、eMMC)读取到RAM中。

(4)为内核设置启动参数。

(5)调用内核。

二、Kernel

1. Linux内核

Kernel即是Linux内核,Linux内核采用宏内核架构,即Linux大部分功能都 会在内核中实现,如进程管理、内存管理、设备管理、文件管理以及网络管 理等功能,它们是运行在内核空间中(也可以称之为特权模式下运行)。

除此之外还有一种与宏内核相反的内核架构——微内核,它仅仅是将内核的基本功 能放入内核中 ,如进程管理、进程调度等,而其他的设备管理、文件管理等功能都放在内核 空间之外(即运行在非特权模式下),这种微内核的架构有很优越的扩展性,它 将系统分为各个小的功能模块,把设计难度大大降低。而宏内核的设计架构则没有 非常好的扩展性。

但Linux在发展的过程中,很早就引入了内核模块(Loadable Kernel Module,LKM)这一机制,弥补了这一不足之处,内核模块全称为动态可 加载内核模块,就是在内核运行时可以动态加载一组目标代码来实现某些特定的 功能,在这过程中不需要重新编译内核就可以实现动态扩展。模块是具有独立功能 的程序,它可以被单独编译,但不能独立运行。它在运行时被链接到内核作为内核 的一部分在内核空间运行,这与运行在用户空间的进程是不同的。模块通常由一组 函数和数据结构组成,用来实现一种文件系统、一个驱动程序或其他内核上层的功 能,关于内核模块我们后续会学习。

2. Linux 系统组成

Linux内核主要由5部分组成,分别为:进程管理子系统,内存管理子系统,文件 子系统,网络子系统,设备子系统:

从上图可以看出,整个内核是由5部分组 成,并且由系统调用层(系统调用子系统)进行统一管理,应用层通过系统调用层的函数接口与内核进 行交互,用户应用程序执行的地方是用户空间,用户空间之下则是 内核空间,Linux 内核正是位于内核空间中。

2.1 进程管理子系统

进程管理的核心就是进程的调度。在Linux内核中,进程调 度的单元是进程,进程调度控制系统中的多个进程对CPU的访问,从宏观上看,系统 中的进程在CPU中是并发执行的。此外内核通过系统调用提供了应用程序 编程接口,例如:创建新进程(fork,exec),结束进程(kill, exit),并且提供了控制进程,同步进程和进程间通信的接口。

2.1.1 进程与程序有什么区别?

  • 程序:存放在磁盘上的一系列代码和数据的可执行映像,是一个静止的实体。
  • 进程: 是一个执行中的程序,它是动态的实体。

2.1.2 进程的四要素

(1)有一段程序供其执行,这段程序不一定是某个进程所专有,可以与其他进程共用。

(2)有进程专有的系统堆栈空间(也可以称之为内核堆栈空间)。

(3)在内核中有一个task_struct数据结构,即进程控制块。有了这个数据结构,进程才能被内核调度器识别并参与内核调度,除此之外它还记录着 进程所占有的各项资源。

(4)除上述的专有的系统堆栈空间外,进程还需要有独立的用户堆栈空间,这 就是mm_struct数据结构,该数据结构位于task_struct结构中,字段名称为mm。

每个进程都有自己的堆栈,内核在创建一个新的进程时,在创建进程控制块task_struct的同时,也为进程创建自己堆栈。一个进程有2个堆栈,即用户堆栈和系统堆栈;用户堆栈的空间指向用户地址空间,内核堆栈的空间指 向内核地址空间。当进程在用户态运行时,CPU堆栈指针寄存器指向用户堆栈地址,使用用户堆栈,当进程运行在内核态时,CPU堆栈指针寄存器指向的是内核栈空间地址,使用的是内核栈。

在Linux中进程与线程的区分也不是十分严格,一般在讲到进程时常常也包括了线程,以上的进程四个要素是必要条件,缺了其中任何一条都不能称其为”进程”,如果完全没有用户空间,即mm_struct为NULL,那么可以称它为内核线程,如果有共享的用户空间,那么可以称之为用户线程,具体如下图所示。

2.1.3 进程调度

进程调度是进程管理子系统中最重要的一个功能,它决定了哪个进程 可以运行,什么时候运行,什么时候停止,它实际上就是一个管理进程 之间使用CPU资源的管理程序,因此Linux内核必须提供一种方法方法,让各个 进程之间尽可能公平地共享CPU资源,并且还需要考虑各个进程的优先级。因此进程 调度器的设计就要解决如何有 效地为各个进程分配其使用 的CPU资源 时间,同时又要达到良好的用户体验效果,此外 调度器还需要解决一些互相冲突的情况,例如既要实现实时任务的最小化响应时间, 又 要最大限度地提高 CPU 的总体利用率等。

其实在Linux2.6版本之后,进程调度器使用新的进程调 度算法——Completely Fair Scheduler,简称CFS,即完全公平调度算法,该算 法会按所需分配的计算能力,向系统中每个进程提供最大的公正性,或者从另 外一个角度上说,它试图确保没有进程被亏待。经过多年来的测试,完全公平调度算法在 高负载情况下依然有极其出色的性能,它负责将CPU资源,分配给正在执行的进 程,目标在于最大化程式互动效能,最小化整体CPU的运用,这个算法使用红黑树 来实现,算法效率为O(log(n))。

2.1.4 进程状态

每个进程都有状态属性,在整个Linux中运行着多个进程,某些进程可能 处于运行态,某些进程可能处于就绪态,某些进程可能处于睡眠态或者是深度 睡眠态,还有一些进程可能处于暂停态或者僵死态等,这些进程的状态都是 动态变化的并且是由进程调度器决定的,具体见下图:

  • 就绪态R (TASK_RUNNING):就绪态也被称作可执行状态

只有在该状态的进程才能被允许参与调度器调度并且使用CPU资源,而同一时 刻可能有多个进程处于就绪态,这些进程的task_struct结构(进程控制块)被放入 对应CPU的可执行队列中(一个进程最多只能出现在一个CPU的可执行队列中)。进程调度器的任务就是从各个CPU的可执行队列中分别选择一个进程在该CPU上运行。

  • 运行态R (TASK_RUNNING):此时进程正在使用CPU资源。

很多操作系统的书将正在CPU上执行的进程定义为RUNNING状态、而将可执行但是尚未被调度执行的进程定义为READY状态,这两种状态在linux下统一为TASK_RUNNING状态。

  • 暂停态T (TASK_STOPPED or TASK_TRACED)

向进程发送一个SIGSTOP信号,它就会因响应该信号而进入TASK_STOPPED状态,向进程发送一个SIGCONT信号,可以让 其从TASK_STOPPED状态恢复到TASK_RUNNING状态,当进程正在被跟踪时,它处于TASK_TRACED这个特殊的状态。“正在被跟踪”指的是进程暂停下来,等待跟踪它的进程对它进行操作。比如在gdb中对被跟踪的进程下一个断点,进程在断点处停下来的时 候就处于TASK_TRACED状态。

  • 可中断睡眠态S (TASK_INTERRUPTIBLE)

对应上图的睡眠态,处于这个状 态的进程因为等待某些事件的发生而进入睡眠状态(比如等待socket连接、等待信号量等)。当这些事件发生的时候进程将被唤醒,如产生一个硬件中断、释放进程正在等待的系统资源或是传 递一个信号都可以是唤醒进程的 条件。一般情况下系统的大多数进程都是处于这个状态的,在终端可以通过ps命令查看系统进程状态。

  • 不可中断睡眠状态D (TASK_UNINTERRUPTIBLE)

不可中断睡眠状态又被称作深度睡眠态,它与可中断睡眠状态类似,但是它有一个例外,那就是把信号传递到这种睡眠状态的进程不能改变它的状态,也就是说它不响应信号的唤醒,这种状态一般由IO引起,同步IO在做读或写操作 时(比如进程对某些硬件设备 进行操作,等待磁盘IO,等待网络IO),此时CPU不能做其它事情,只能处于这种状态 进行等待,这样一来就能保证进程执行期间不被外部信号打断,但这种状态时间非常 短,很难在终端中将它捕获到。

  • 僵死态Z (TASK_DEAD - EXIT_ZOMBIE)

僵死态又被称之为僵尸态或者退出态,进程在退出的过程中,除了task_struct数据结构(以及少数资源)以外,进程所占有的资源将被系统回收,此时进程没法继续运行了,但它还有task_struct数据结构,所以被称为僵死态。之所以保留task_struct数据结构,是因为task_struct中保存了进程的退出码、以及一些其他的信息,而其父进程很可能会关心这些信息,因此会暂时被保留下来。

可以使用ps –aux命令查看当前系统中进程的状态,因为当前系统觉得大部分进程是处于可中断睡眠态的,如运行态、不可中断睡眠态、僵死态等进程状态是很难被终端捕获到的,因此下面的图是经过处理的,能更直观显示一些任务状态:

2.2 内存管理子系统

内存管理的主要作用是保证系统安全访问内存区域,且绝大部 分CPU都是支持内存管理单元的(Memory Management Unit,MMU),那么在 Linux中,内存管理子系统就负责管理每个进程完成从虚拟 内存到物理内存的转换,以及系统可用内存空间,此外还顺便 提一点,Linux的2.6版本后引入了对无MMU CPU的支持。

内存管理的硬件按照分页方式管理内存,分页就是把系统的物 理内存按照相同大小等分,每个内存分片称作内存页,通常内存页大小 是4KB。内存管理子系统要管理的不仅是4KB缓冲区,它提供了对4KB缓冲区 的抽象,例如slab分配器。这种内存管理模式使用4KB缓冲区为基数,然 后从中分配管理结构,并跟踪内存页使用情况 ,系统中哪些内存页是满的,哪些内存页面为空,哪些内存页没有完全 使用。这样一来,系统就支持动态调整内存使用情况。除此之外,Linux还 支持内存交换,因为Linux中使用的是虚拟内存,当物理内存不足时,内存管理子系 统会将内存暂时移到磁盘中,在物理内存充裕时又将内存页从磁盘移到物理内存中,这就是内存交换

一般而言,在32位的系统上,每个进程都最大享有4GB的内存空间,因为由于32位的 系统寻址空间只有4G,当然这是虚拟内存,0~3GB是属于用户内存空间,3~4GB是属于 系统内存空间,实际上用户的程序几乎使用不完那么大的用户空间,一旦超出将无法正常运行,当然系统内存空间与用户内存空间是可以调整的。

2.3 文件管理子系统

在Linux系统中有一个重要的概念:一切皆文件,它把一切资源都看 作是文件,包括硬件设备,通常称为设备文件。Linux的文件管理子系 统主要实现了虚拟文件系统(Virtual File System,VFS),虚拟文件系统屏蔽了各种硬件上的差异以及具体实现的细节,为所有的硬件设备提供统一的接口,这样子也就实现了设备无关性,同时文 件管理系统还为应用层提供统一的API接口。

总的来说,Linux 的文件系统体系结构是对一个对复杂系统进行了抽象化,通 过使用一组通用的 API 函数,Linux 可以在许多种存储设备上支持多种文件系 统,如NTFS、EXT2、EXT3、EXT4 、FAT等等;而用户空间包含一些应用程序和 GNUC 库(glibc),它们使用的API接口是由系统调用层提供(如打开、读、写和关闭等),其框架如下图:

2.4 网络子系统

在Linux 内核中,与网络相关的代码被Linux独立开,形成一个相对独立的子系统,称为网络子系统,网络子系统是一个层次化的结构,可分为以下几个层次:

(1)Socket 层(也可以称之为协议无关层):Linux 在发展过程中,采 用 BSD Socket API 作为自己的网络相关的 API 接口。同时, Linux 的目标又要能 支持各种不同的协议族,而且这些协议族都可以使用 BSD Socket API 作为应用层的编程接口,这样一来将Socket层抽象出来就能屏蔽不同协议族之间的差异,不会对应用层的使用产生影响。

(2)协议层:Linux 网络子系统功能上相当完备,它不仅支持 INET 协议 族(也就是通常所说的 TCP/IP 协议族),而且还支持其它很多种协议族,如 INET6、DECnet,ROSE,NETBEUI 等,对于 INET 、 INET6 协议族来说,又会进一步将协议族划分为传输层和网络层以及 链路层等,此处就不深入了。

(3)网络设备层:网络设备其实是设备驱动层的内容了,它抽象了网卡 数据结构,在一个系统中可能存在多种网卡,屏蔽了不同硬件上的差 异,这一层提供了一组通用函数供底层网络设备驱动程序使用。

网络子系统框架如下图所示:

2.5 设备子系统

设备子系统又被称之为设备驱动,在日常生活中,嵌入式中使用的设备多种多样,如LCD、摄像头、USB、音频等都是属于设备,且设备的厂 商不同其驱动程序也是不同的,但是对于Linux来说,不可能去将每个设备都包含到内核,它只能抽象去描述某种设备。

从前面的几个子系统的框图中可以了解到,系统调用层是Linux内核与应用程序之间的接口,而设备驱动则是Linux内核与硬件之间的接口,设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作(打开、读、写和关闭)。设备驱动程序是内核的一部分,它主要完成以下的功能:

(1)对设备初始化和释放

(2)把数据从内核传送到硬件和从硬件读取数据

(3)读取应用程序传送给设备文件的数据和回送应用程序请求的数据

(4)检测和处理设备出现的错误

设备驱动框架如下图所示

其实Linux在发展的时候就根据设备的共性将设备分层3大类,分别为:

  • 字符设备

字符设备是以字节为单位传输的IO设备,可以提供连续的数据流,应用程 序可以顺序读取,通常不支持随机存取。这种字符传输的效率通常是比较低 的,如鼠标、键盘、串口等都是字符设备,也是一种比较常见的设备。

  • 块设备

块设备是以块为单位进行传输的设备,应用程序可以随机访问块设备中的 数据,程序可以指定读取数据的位置。我们的磁盘就是一种常见的块设备,应用 程序可以寻址磁盘上的任何位置,并在这个位置读取数据。不过需要注意的是,块设 备读取的数据只能以块为单位的倍数进行(通常是512Byte的整数倍),而不能与字符设备一样以 字节为单位读取,因此通常来说块设备的传输速度是比较高的。

  • 网络设备

网络设备,它其实就是我们上一章网络子系统中描述的网络 设备层,它其实也是一个抽象,统一描述了不同的网卡设备,如WIFI、以 太网等。因为网络设备存在协议栈(协议族),它涉及了网络层协议,所 以Linux将网络设备单独分层一类设备,网络设备的传输速率通常是很高的。

三、dtb

1. 设备树简介

在2011年之前,ARM Linux中存在大量描述芯片平台以及板级差 异的垃圾代码,它们位于kernel/arch/arm/plat-xxx目录 和kernel/arch/arm/mach- xxx目录下,用代码描述硬件,如注册platform设备,声明设备的resource等,但这些代码对于Linux内核来说都是垃圾,因此Linux创始人Linus Torvalds在2011年3月份向Linux社区发送一封邮件,他表达了对 ARM 架构配置方式的不满, 并宣称”Gaah. Guys, this whole ARM thing is a f*cking pain in the ass”。 这引起了广泛的讨论和反思。 他提出 ARM架构平台应该参考其他平台如PowerPC的设备树机(Device Tree)制描述硬件,这样子在Linux内核中就不再需要进行大量的冗余编码,许多硬件及芯片平台的细节可以通过设备树传递给Linux内核。

其实设备树是一种描述硬件的数据结构,它把这些硬件设备的信 息,用一个非C语言的脚本语言来描述,而这个脚本文件,就是传说中的Device Tree(设备树),设备树包括设备树源码(Device Tree Source,DTS)文件、设 备树编译工具(Device Tree Compiler,DTC)与二进制格式设备树(Device Tree Blob,DTB)。DTS包含的头 文件格式为DTSI,DTS文件是一种人类可以看懂的编码格式,由节点(Node)和属 性(Property)组成,节点中又可能会包含子节点,而属性则是可以简单理解为 成对出现的名称与值,如下面的示例。

1
2
3
4
5
6
7
8
9
10
node1 {
a-string-property = “A string”;
a-string-list-property = “first string”, “second string”;
a-byte-data-property = [0x01 0x23 0x34 0x56];
child-node1 {
first-child-property;
second-child-property = <1>;
a-string-property = “Hello, world”;
};
};

因为Uboot和Linux不能直接识别DTS文件,它们只能识别二进制 文件,所以需要把DTS文件编译成DTB文件,而DTC就是将设备树源 码文件(.dts / .dtsi)编译成二进制格式设备树文件(.dtb)的编译工具,它位于Linux内 核源码的scripts/dtc目录下,在Linux配置中使能了设备树情况下,内核会 自动编译对应的设备树,当然用户也可以单独编译设备树。

DTB可以被内核与BootLoader识别解析,通常在制作NAND Flash、SD Card启动 镜像时,通常会为DTB文件留下一部分存储区域以存储DTB,在BootLoader启 动内核时,会先读取DTB到内存。

Linux的设备树中,可描述的硬件信息包括以下几类:

(1)CPU的数量和类别

(2)内存基地址和大小

(3)总线和桥

(4)外设连接

(5)中断控制器和中断使用情况

(6)GPIO控制器和GPIO使用情况

(7)Clock控制器和Clock使用情况

2. DTS的加载过程

使用DTS描述了硬件的信息,那么怎么让内核识别这些信息呢?这就涉及到DTS的加载过程:当我们要使用Device Tree时,我们首先要了解自己的硬件配置和系统运行参数,并把这些信息 组织成Device Tree Source 文件,然后通过DTC将这些适合人类阅读的DTS/DTSI 文件编译变成适合机器处理的DTB文件。

在编译生成DTB文件后,还需要将它加载到Linux内核中,让内核来识 别这些信息。在系统启动时,BootLoader会将保存在flash中的DTB 拷贝到内存,并把DTB在内存中的起始地址传递给内核。

3. 设备树插件

Linux4.4 以后引入了动态设备树(Dynamic DeviceTree) 。 设备树插件(Device Tree Overlay)是一种用于设备树(Device Tree) 的扩展机制。 设备树是一种用于描述硬件设备的数据结构,广泛应用于嵌入式系统中, 特别是基于 Linux 内核的系统中。

设备树插件允许在运行时动态修改设备树的内容, 以便添加, 修改或删除设备节点和属性。它提供了一种灵活的方式来配置和管理硬件设备, 而无需重新编译整个设备树。 通过使用设备树插件, 开发人员可以在不重新启动系统的情况下对硬件进行配置更改。

设备树插件(Dynamic DeviceTree) 通常以一种文本格式定义, 称为设备树源文件(Device Tree Source,DTS) 。 DTS 文件描述了设备树的结构和属性, 包括设备节点, 寄存器地址, 中断信息等。 设备树插件可以通过加载和解析设备树文件, 并将其合并到现有的设备树中, 从而实现对设备树的动态修改。

使用设备树插件, 可以实现一些常见的配置变化, 比如添加外部设备, 禁用不需要的设备,修改设备属性等。 这对于嵌入式系统的开发和调试非常有用, 特别是面对多种硬件配置或需要频繁更改硬件配置的情况下。

四、根文件系统

此处学习的文件系统是根文件系统(rootfs),它是linux在初始化时 加载的第一个文件系统,而非我们在上面文件管理子系统小节所说 的文件系统,这两个是不同的概念。

根文件系统包括根目录和真实文件系统,根文 件系统之所以在前面加一个”根”,说明它是加载其它文件系统的”根”,如果没有这个”根”的 话,其它的文件系统也就没有办法进行加载的。因为它包含系统引导和使 其他文件系统得以挂载(mount)所必要的文件。根文件系统包含Linux启动时 所必须的目录和关键性的文件,例如Linux启动时必要的初始化文件,它在init目录下,此 外根文件系统中还包括了许多的应用程序bin目录等,任何包括这些Linu x 系统启动所必须的文件都可以称为根文件系统。

在Linux内核启动的初始阶段,首先内核会初始化一个基于内存的文件系统,如initramfs,initrd等,然后以只读的方式去加载根文件系统(load rootfs),读取并 且运行/sbin/init初始化文件,根据/etc/inittab配置文件完成系统的初始化工作(提示:/sbin/init是 一个二进制可执行文件,为系统的初始化程序,而/etc/inittab是它的配置文件),在初始化的过程中,还会以读写的方式重新挂载根文件系统,在系统启动后,根文件系统就可用于存储数据了,存在根文件系统是Linux启动时的必要条件。

在Windows的目录结构中,可能会包括C:或者D:盘,它们一般就被称之为特定逻辑磁 盘的根目录。从文件系统的层面来说,每一个分区都包含了一个根目录,即Windows系统中可 以存在多个根目录。

但是,在Linux系统中的文件系统目录结构与Windows上有较大的不同。系统中只有 一个根目录,路径是”/”,而其它的分区只是挂载在根目录中的一个文件夹,如”/home”和”/sys”等,这 里的”/”就是Linux中的根目录,因此Linux中只存在一个根目录,在Linux启动后,根目录就位于真实的文件系统中。

Linux中的文件系统多种多样,同时在Linux中一切皆是文件,普通文件、目录、字符 设备、块设备、套接字等都以文件方式被抽象化;且它们需要向上层提供统一的操作接口。虚拟文 件系统VFS就是Linux内核中的一个软件层,向上给用户空间程序提供文件系统操作接口;向下 允许不同的文件系统共存,所以,所有实际文件系统都必须实现VFS的结构封装。 因为无论是访问设备还是需要通过文件系统来访问它的挂载点。

参考资料

doc.embedfire.com/linux/imx6/base/zh/latest/building_image/image_composition.html