rootfs & bootfs & bootloader

版权声明:原创作品请标明出处 https://blog.csdn.net/Huangxiang6/article/details/84749469

文件系统 & bootloader

注:本博客为本人的学习记录,如存在不准确的地方请大家帮忙指出 ,谢谢!

一、文件系统

​ 一个linux系统按启动顺序可以划分为:引导加载程序、内核、文件系统、应用程序。

​ 文件系统可以通俗的理解为管理文件存放位置的管家,不同的操作系统有不同的文件系统。Linux中一般使用EXT2格式的文件系统,(EXT3/EXT4增加了日志功能)是一种索引式(inode)文件系统,不需要像U盘那样经常磁盘重组。resize2fs命令可以变更文件系统大小。dumpe2fs命令可以查询文件系统的状态,包括inode节点和block块的用量情况。

在这里插入图片描述

当系统载入一个文件到内存后,如果该文件没有被更动过,则在内存区段的文件数据会被设置为干净(clean) 的。 但如果内存中的文件数据被更改过了 ,此时该内存中的数据会被设置为脏的 (Dirty)。此时所有的动作都还在内存中执 行,并没有写入到磁盘中! 系统会不定时的将内存中设置为“Dirty”的数据写回磁盘,以保持 磁盘与内存数据的一致性。

$ cat /proc/filesystem可以查看linux系统目前已载入内存中支持的文件系统:

sysfs rootfs ramfs bdev proc cpuset cgroup tmpfs devtmpfs debugfs tracefs securityfs sockfs bpf pipefs devpts ext3 ext2 ext4
等等。

$ df命令列出文件系统的整体磁盘使用量和挂载点信息。

[root@study ~]# df [-ahikHTm] [目录或文件名]
选项与参数:
-a :列出所有的文件系统,包括系统特有的 /proc 等文件系统;
-k :以 KBytes 的容量显示各文件系统;
-m :以 MBytes 的容量显示各文件系统;
-h :以人们较易阅读的 GBytes, MBytes, KBytes 等格式自行显示;
-H :以 M=1000K 取代 M=1024K 的进位方式;
-T :连同该 partition 的 filesystem 名称 (例如 xfs) 也列出;
-i :不用磁盘容量,而以 inode 的数量来显示

在这里插入图片描述

  • Filesystem:代表该文件系统是在哪个 partition ,所以列出设备名称;
  • 1k-blocks:说明下面的数字单位是 1KB 。可利用 -h 或 -m 来改变容量;
  • Used:使用掉的磁盘空间。
  • Available:也就是剩下的磁盘空间大小;
  • Use%:就是磁盘的使用率。如果使用率高达 90% 以上时, 就要警惕了,要避免容量不足造成系统问题!(例如最容易满的 /var/spool/mail 这个放置邮件的磁盘);
  • Mounted on:就是磁盘挂载的所在目录(挂载点)。

/proc目录下的内容都是 Linux系统所需要载入的系统数据,并且是挂载在“内存当中”的,所以不占磁盘空间。(可用df -a显示全部文件系统看到)

/dev/shm/目录,其实是利用内存虚拟出来的磁盘空间,因此在这个目录下面创建任何数据文件时,存取速度是 非常快速的,通常是总实体内存的一半,这个文件系统的大小在每部主机上都不一样,而且创建的东西在下次开机时就消失了。

$ lsblk列出磁盘分区信息

在这里插入图片描述

NAME:是设备的文件名,会省略 /dev 等前导目录

MAJ:MIN:核心认识的设备都是通过这两个代码来熟悉的!分别是主要:次要设备代码

RM:是否为可卸载设备 (removable device),如光盘、USB 磁盘等等

SIZE:容量

RO:是否为只读设备

TYPE:是磁盘 (disk) 、分区 (partition) 还是只读存储器 (rom) 等输出

MOUTPOINT:就是挂载点

[root@ ~]# lsblk [-dfimpt] [device]
选项与参数:
-d :仅列出磁盘本身,并不会列出该磁盘的分区数据
-f :同时列出该磁盘内的文件系统名称
-i :使用 ASCII 的线段输出,不要使用复杂的编码 (再某些环境下很有用)
-m :同时输出该设备在 /dev 下面的权限数据 (rwx 的数据)
-p :列出该设备的完整文件名!而不是仅列出最后的名字而已。
-t :列出该磁盘设备的详细数据,包括磁盘伫列机制、预读写的数据量大小等
VFS(virtual filesystem switch)机制

​ VFS是Linux文件系统实现必须遵循的一种机制,rootfs是一种具体实现的文件系统、Linux下所有文件系统的实现都必须符合VFS的机制(符合VFS的接口) 。linux系统通过VFS的核心功能去读取filesystem,在VFS的管理下,用户不需要知道每个分区中的fs是哪一个。

buildroot构建rootfs

​ Buildroot是一个简单,高效且易于使用的工具,可通过交叉编译生成嵌入式Linux系统,内核映像编译和引导加载程序编译。buildroot是一个开源项目,类似于busybox的一种集成包,其主要功能是提供了交叉编译工具链和rootfs的制作。 使用Buildroot构建基本系统非常简单,通常需要15-30分钟。

核心(kernel)与核心模块

​ 在整个开机的过程中,核心决定了是否能够成功驱动主机的硬件配备,核心一般都是压缩文件(如vmlinuz,是可引导的,压缩的linux内核 ),因此在使用核心之前,需要解压缩后(如vmlinux,是一个包含linux kernel的静态链接的可执行文件,文件类型是linux接受的可执行文件格式之一(ELF、COFF或a.out) )载入内存当中。

​ 目前的核心都是具有“可读取模块化驱动程序”的功能,即所谓的“ modules (模块化) ”的功能。 该模块可以由硬件开发厂商提供,也可能我们的核心本来就支持,不过较新的硬件通常都需要硬件开发商提供驱动程序模块。

核心与核心模块存放位置:

  • 核心: /boot/vmlinuz 或 /boot/vmlinuz-version;
  • 核心解压缩所需 RAM Disk: /boot/initramfs (/boot/initramfs-version) ;
  • 核心模块: /lib/modules/version/kernel 或 /lib/modules/$(uname -r) /kernel;
  • 核心源代码: /usr/src/linux 或 /usr/src/kernels/ (要安装才会有,默认不安装)

在这里插入图片描述

​ Linux初始RAM磁盘(initrd)是在系统引导过程中挂载的一个临时根文件系统,用来支持两阶段的引导过程。initrd文件中包含了各种可执行程序和驱动程序,它们可以用来挂载实际的根文件系统,然后再将这个 initrd RAM磁盘卸载,并释放内存。在桌面或服务器Linux 系统中,initrd 是一个临时的文件系统。其生存周期很短,只会用作到真实文件系统的一个桥梁。在没有存储设备的嵌入式系统中,initrd 是永久的根文件系统。

​ 当在新的硬件设备上运行某个不支持的操作系统时,可以尝试重新编译核心,并加入最新的硬件驱动程序源代码,将该硬件的驱动程序编译成为模块,在开机时载入该模块。

在这里插入图片描述

  • arch :与硬件平台有关的项目,例如 CPU 的等级等等;
  • crypto :核心所支持的加密的技术,例如 md5 或者是 des 等等;
  • drivers :一些硬件的驱动程序,例如显卡、网卡、PCI 相关硬件等等;
  • fs :核心所支持的 filesystems ,例如 vfat, reiserfs, nfs 等等;
  • lib :一些函数库;
  • net :与网络有关的各项协定数据,还有防火墙模块 (net/ipv4/netfilter/*) 等等;
  • sound :与音效有关的各项模块;

$ lsmod命令可以查看当前核心载入了多少核心模块

二、X86架构的bootloader(注意与ARM架构的区分)

x86架构的机器最好使用Grub作为bootloader。x86架构的CPU在运行bootloader之前先运行一段BIOS中固化的程序(固件,firmware),然后才运行硬盘第一个分区(MBR)。

PC机上电后BIOS会获得处理器的控制权,是一个复杂的系统配置软件,拥有硬件架构的底层信息,接下来就是会到第一个开机设备的 MBR(Main Boot Record 主引导记录区 )去读取 boot loader ,MBR 是整个硬盘的第一个 sector 内的一个区块(位于整个硬盘的0磁道0柱面1扇区 ),大小只有446 Bytes(在512字节的主引导扇区中,MBR只占用了其中的446个字节,另外的64个字节交给了 DPT(Disk Partition Table硬盘分区表),最后两个字节“55,AA”是分区的结束标志。这个整体构成了硬盘的主引导扇区。)。

开机——>BIOS——>GRUB(或者LILO)——>Linux Kernel

执行流程图和重要函数:

在这里插入图片描述

①BIOS启动引导阶段,调用Grub将操作系统内核镜像vmlinuz载入到RAM中。CPU硬件逻辑设计为在加电瞬间强行将CS值置为0XF000,IP为0XFFF0,这样CS:IP就指向0XFFFF0这个位置,这个位置正是BIOS程序的入口地址,BIOS程序被固化在计算机主机板上的一块很小的ROM芯片里 。 当机器上电后,x86架构的CPU主动进入实模式,并从地址0xFFFF0(载入BIOS的地址)开始自主运行程序代码。BIOS首先进行POST(上电后自检),检测系统中的一些关键设备(比如内存和显卡)是否存在是否正常。第二步是枚举本地设备并初始化,物理地址0x0处初始化中断向量。然后BIOS启动第一个扇区,读入内存绝对地址0x7C00处(拷贝到此处的内容就是bootloader),跳转到这个地址并运行。

在这里插入图片描述

​ BIOS程序在内存最开始的位置(0x00000)用1KB的内存空间(0x00000 ~ 0x003FF)构建中断向量表,在紧挨着它的位置用256KB的内存空间构建BIOS数据区(0x00400~0x004FF),并在大约57KB以后得位置(0x0e05b)加载了8KB左右的与中断向量表相应的若干中断服务程序。中断向量表有256个中断向量,每个中断向量占4个字节,其中两个字节是CS值,两个字节是IP值。每个中断向量都指向一个具体的中断服务程序。

②bootloader阶段

bootloader的两级启动模式

Stage 1:执行 boot loader 主程序: 第一阶段为执行 boot loader 的主程序,这个主程序必须要被安装在开机区,亦即 MBR 或者是 boot sector 。因为 MBR 实在太小了,所以,MBR 或 boot sector 通常仅安装 boot loader 的最小主程序, 并没有安装 loader 的相关配置文件;

Stage 2:主程序载入配置文件: 第二阶段为通过 boot loader 载入所有配置文件与相关的环境参数文件(包括文件系统定义与主要配置文件 grub.cfg),一般配置文件都在 /boot 下面。

Grub(GRand Unified Bootloader )

Grub是一个来自GNU项目的多操作系统启动程序,它是一种多启动规范的实现,允许用户在计算机同时拥有多个操作系统。可在操作系统分区上选择不同的内核,也可以向这些内核传递启动参数,用来引导不同操作系统,如windows、linux。

实模式存在的时间非常短,CPU复位或上电时就是以实模式启动,处理器以实模式工作时不能实现权限分级,只能访问16位的地址线,只能寻址1MB内存,所以采用的寻址方式是段地址*16+偏移地址。之后就加载操作系统模块,进入保护模式。

③加载内核从实模式切换到保护模式

当内核映像被加载到内存中(加载过程仍然用int 0x13中断向量),并且次引导加载程序释放控制权之后,内核阶段就开始了。 这个例程会执行一些基本的硬件设置,并调用 ./arch/x86/boot/compressed/head_32.S 中的 startup_32,设置一个基本的环境(堆栈等),并清除 Block Started by Symbol(BSS)。然后调用一个叫做 decompress_kernel 的 C 函数(在 ./arch/x86/boot/compressed/misc.c 中)来解压内核。当内核被解压到内存中之后,就可以调用它了。

  • 进入保护模式
  • 设置中断描述附表和全局描述符表
  • 创建了内存分页机制

④启动内核

  • start_kernel启动内核
  • 创建init进程

三、ARM架构的bootloader

在这里插入图片描述

​ 与x86架构区分,大部分的嵌入式系统中并没有固件,系统上电或复位后执行的第一段程序就是 bootloader 。ARM架构的CPU从地址0x0000000开始,嵌入式开发板中需要把存储器件ROM或Flash映射到这个地址,bootloader就存放在这个地址开始处,一上电就能执行。(不同架构CPU起始地址可能有差异,比如MIPS结构就是从0xBFC00000取第一条指令)

bootloader的两种操作模式:①启动加载模式;②下载模式;

Stage1:

完成基本的硬件初始化,为加载stage2准备RAM空间(初始化RAM,包括设置 CPU 的控制寄存器参数,以便能正常使用 RAM 以及检测RAM 大小 ),从Flash中拷贝内核映像和文件系统映像到RAM中,设置堆栈指针sp跳到stage2的C入口点。

Stage2:

初始化本阶段要使用到的硬件设备,检测系统的内存映射(检测内存使用量、地址空间。可以根据具体的板子设置),从Flash加载内核和根文件系统映像(可能是未解压的,若内核没有自解压功能,则需要进行解压)读到RAM空间,设置内核的启动参数,调用内核。

1)初始化RAM : Linux 内核一般都会在 RAM 中运行 。

2)初始化串口端口 :通过串口输出信息是调试 bootloader 和Linux 内核的手段。

3)检测处理器类型 :Linux 内核在启动过程中会根据该处理器类型调用相应的初始化程序。

4)设置Linux启动参数 :目前流行通过struct tag(标记列表 )结构来传递启动参数。

5)调用Linux内核映像

https://blog.csdn.net/u013819957/article/details/49664665 可以参考这篇博文

https://blog.csdn.net/blue_carrot/article/details/50517901

结论

大致了解了x86和arm架构的bootloader加载内核过程,了解了linux的启动过程,bootloader非常依赖于具体的硬件,即使有相同的CPU,外设不同(比如flash),如果知道linux内核中哪些部分与硬件有关,哪些是linux内核内部实现的功能,在移植到不同硬件平台时就比较容易。要进一步了解整个过程,必须结合uboot源码来看。

ftp://ftp.denx.de/pub/u-boot/ uboot源码下载地址

**目录很多,不知从何看起。。。**u-boot是一个流程控制程序,所以就是只关心控制的流动情况即可。

关于如何阅读uboot源码,参考https://www.cnblogs.com/80scd/p/5872425.html

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Huangxiang6/article/details/84749469