学习操作系统原理最好的方法是自己写一个简单的操作系统。


新买的硬盘和优盘在第一次使用时需要格式化,有时候还需要分区。这是为什么呢?分区和格式化到底是干啥呢?本讲将为大家解开这些疑惑。

一、文件系统

1.分区

首先说一下分区,我们平时看到的C盘、D盘等就是一个个分区。硬盘第一个扇区的一部分固定空间叫做分区表,划分分区就是在这个分区表中记录一下各分区的信息,包括各个分区从哪个扇区开始,到哪个扇区结束等。由于GrapeOS所用虚拟硬盘的空间大小只有4MB,没必要分区,所以我们在MBR中也没有填写分区表。

2.格式化

格式化是在某个分区上做的。如果一个盘没有做分区,那就将整个盘作为一个分区看待,GrapeOS就是这样的。大家如果对硬盘或优盘做过格式化就会知道,格式化的时候会让你选择一种文件系统,常见的选项有NTFS、FAT32、exFAT等。所谓格式化就是将某种文件系统的信息写入到这个分区的一部分扇区上。那什么是文件系统呢?下面来简单介绍一下。

3.文件系统

计算机一开始是没有文件和文件系统概念的。前面我们学习了对硬盘的读写,我们知道对硬盘的读写是按扇区为单位进行的。在读写硬盘的时候我们有用到文件的概念吗?没有。到目前为止,我们用的虚拟硬盘上并没有任何文件系统,就和刚买的新硬盘一样,但并不影响我们读写硬盘。但是在没有文件系统的情况下实际使用会非常麻烦。比如你将多个文件写入到硬盘上,你需要记录每个文件存放在了那些扇区上;如果为一个文件增加了一些内容,需要多占用一些扇区,你需要知道哪些扇区是空闲的。这些问题都是需要文件系统处理的。早期的计算机之所以没有文件系统也能用是因为当时的每个外部存储器上的数据都是为某一件事专用的,有配套的程序做处理,并不能像现在随意往硬盘里存放各种文件。总之,文件系统是为了方便在硬盘或其它存储设备上存储数据而抽象出来的一种数据管理方式。只说是抽象出来的,这个不好理解,文件系统有很多种,需要结合一种具体的文件系统讲解才能明白。下面我们介绍一下GrapeOS中用的文件系统FAT16。

二、FAT16

1.FAT16空间分布

首先大家需要明白两个概念,文件属性和文件内容。文件属性一般包含文件名称、大小、修改日期等信息。文件内容是指文件内具体包含的东西,比如一个文本文件,它的内容就是里面的文本信息。

在FAT16文件系统中,一般会将硬盘或某个分区划分为5个部分:引导扇区、FAT1表、FAT2表、根目录区、数据区。如下图所示:

  • 引导扇区就是硬盘或分区的第一个扇区。
  • 根目录区存放的就是根目录中文件和文件夹的属性信息。
  • 数据区存放的是所有文件和文件夹的内容。
  • FAT1表和FAT2表存放的是文件内容的簇号,也就是记录每个文件的内容存放在了哪些扇区中。簇是FAT16数据区中的一个空间单位,每个簇等于若干个扇区,具体等于多少个扇区,需要在引导扇区中设置。在GrapeOS中每个簇设置等于一个扇区。簇是FAT16中用来存放文件数据的基本单位,不可分割,一个簇内的空间不能一部分属于一个文件,而另一部分属于另一个文件。比较特殊的一点是数据区中的簇号不是从0开始的,而是从2开始的。

    FAT2表是FAT1表的备份,大小完全相同,正常情况下里面的数据也完全相同。如果发生不正常的情况可以用FAT2表中的数据恢复FAT1表。在GrapeOS中我们不考虑这种不正常的情况,所以舍弃了FAT2表。如下图所示:

2.FAT16引导扇区

FAT16文件系统引导扇区结构表:

名称 偏移 长度 内容 GrapeOS的值
BS_jmpBoot 0 3 一个短跳转指令 jmp boot_start nop
BS_OEMName 3 8 厂商名称 GrapeOS
BPB_BytsPerSec 11 2 每扇区字节数 0x0200
BPB_SecPerClus 13 1 每簇扇区数 0x01
BPB_RsvdSecCnt 14 2 保留扇区数(引导扇区的扇区数) 0x0001
BPB_NumFATs 16 1 FAT表的份数 0x01
BPB_RootEntCnt 17 2 根目录可容纳的目录项数 0x0200
BPB_TotSec16 19 2 扇区总数 0x2000(4MB)
BPB_Media 21 1 介质描述符 0xf8
BPB_FATSz16 22 2 每个FAT表扇区数 0x0020
BPB_SecPerTrk 24 2 每磁道扇区数 0x0020
BPB_NumHeads 26 2 磁头数 0x0040
BPB_HiddSec 28 4 隐藏扇区数 0x00000000
BPB_TotSec32 32 4 如果BPB_TotSec16是0,由这个值记录扇区数。 0x00000000
BS_DrvNum 36 1 int 13h的驱动器号 0x80
BS_Reservedl 37 1 未使用 0x00
BS_BootSig 38 1 扩展引导标记 0x29
BS_VolID 39 4 卷序列号 0x00000000
BS_VolLab 43 11 卷标 Grape OS
BS_FileSysType 54 8 文件系统类型 FAT16
引导代码及其它 62 448 引导代码、数据及其它填充字符等
结束标志 510 2 0xAA55 0xAA55

从上表中可以看到,FAT16引导扇区中前62个字节是有固定格式的,FAT16的格式化就是将上表中的格式数据写入到引导扇区中。上表中的数据并非每一行都有用,我们用到的有:BPB_BytsPerSec、BPB_SecPerClus、BPB_RsvdSecCnt、BPB_NumFATs、BPB_RootEntCnt、BPB_TotSec16、BPB_FATSz16。根据这些信息就能推断出GrapeOS的硬盘FAT16扇区分布:

3.FAT16目录项

文件夹也叫目录,文件和文件夹的属性都存储在目录项中,在根目录和其它目录中存放的是一个一个的目录项,也就是说文件夹的内容就是目录项列表。每个目录项有32个字节,具体结构如下:

名称 偏移 长度 描述
DIR_Name 0 11 文件名8字节,扩展名3字节
DIR_Attr 11 1 目录项属性(0x10代表文件夹,0x20代表文件)
保留位 12 10 保留位
DIR_WrtTime 22 2 最后一次写入时间
DIR_WrtDate 24 2 最后一次写入日期
DIR_FstClus 26 2 起始簇号
DIR_FileSize 28 4 文件大小

每个扇区可以存放16个目录项。

4.FAT表和FAT表项

前面我们讲到,在FAT16的数据区中是以簇为单位编号的,簇号从2开始依次递增。FAT16的簇号是用16位二进制数表示的,这也是FAT16中16的含义。除了前2个簇号不用,最后的16个簇号有特殊用途,FAT16最多可以管理216-2-16=65518个簇。

在目录项中,DIR_FstClus存放的是起始簇号。如果一个文件或文件夹的内容在一个簇里放不下,需要多个簇,其它簇号需要记录到FAT表中。在FAT16的FAT表中,每两个字节是一个FAT表项,每个FAT表项代表一个簇,从第一个FAT表项开始依次代表簇0、簇1、簇2、簇3、簇4等等,用来表示每个簇是否已被占用或下一个簇。对于每个FAT表项,如果它的值是0表示该簇未使用,可以用来存放新数据,对于一个刚格式化完的硬盘,FAT表中除了前2个表项,其它表项应该都是0。如果FAT表项的值不为0,表示该簇已被占用,而且这个值就是文件内容下一个簇的簇号,这样就实现了文件内容在数据区中的链式存储。从目录项中拿到文件的起始簇号,在起始簇号对应的FAT表项中的值就是存放文件内容的第二个簇号,在第二个簇号对应的FAT表项中的值就是存放文件内容的第三个簇号……举个例子,比如一个文件的起始簇号是5,在第5个FAT表项中存放的是文件内容的第2个簇号,假设第2个簇号是8,则会在第8个FAT表项中存放第3个簇号,以此类推,就像链表一样,直到下一个簇号大于等于0xfff8,表示文件内容结束,请见下图。

上图中表示这个文件共占用3个簇的空间,簇号分别是5、8、9。我们只要把这个簇链表中每个簇的数据从数据区里读取出来,并按簇链表的顺序存放在一起就是文件的完整内容。

FAT表项取值说明:

FAT表项 实例值 描述
0 0xfff8 磁盘表示字(实际无用,设为0即可。)
1 0xffff 第一个簇不可用(实际无用,设为0即可。)
2
3
……
0x0003
0x0004
……
0x0000:可用簇
0x00020xffef:已用簇,标识下一个簇的簇号<br>0xfff00xfff6:保留簇
0xfff7:坏簇
0xfff8~0xffff:文件的最后一个簇

前面我们已经提到,GrapeOS的FAT表有32个扇区,每个扇区有256个FAT表项,则共有8192个FAT表项。由于数据区簇号是从2开始的,FAT表中的前2个FAT表项不使用,也就是最多能管理8190个簇。我们这里一个簇等于一个扇区,所以这里的FAT表最多能管理8190个扇区。而我们这里的数据区共8127个扇区,FAT表大小够用了。


视频版地址:https://space.bilibili.com/1688387238

配套的代码与资料在:https://gitee.com/jackchengyujia/grapeos-course

GrapeOS操作系统交流QQ群:643474045

自己动手从零写桌面操作系统GrapeOS系列教程——22.文件系统与FAT16的更多相关文章

  1. 别人写的一个Bootstrap系列教程

    http://www.cnblogs.com/lansy/category/659061.html

  2. 一个人写的操作系统 - Sparrow OS

    一个人写的操作系统 - Sparrow OS 自己写一个操作系统,这是在过去的几年里我一直为之努力的目标,现在终于完成了. 缘起 自己动手写操作系统的动机最初来自于学习Linux遇到的困难. 我是一个 ...

  3. 自制 os 极简教程1:写一个操作系统有多难

    为什么叫极简教程呢?听我慢慢说 不知道正在阅读本文的你,是否是因为想自己动手写一个操作系统.我觉得可能每个程序员都有个操作系统梦,或许是想亲自动手写出来一个,或许是想彻底吃透操作系统的知识.不论是为了 ...

  4. 【操作系统】关于Linux桌面操作系统

    以前是Win+Ubuntu+黑苹果,周末想体验一下deepin,于是简单安装了一下,安装过程很简单,这里不再描述.安装之后,第一次打开系统,确实很惊艳,赏心悦目的操作系统. 之前用Ubuntu时候,C ...

  5. 盘点|2021年最受欢迎Linux桌面操作系统前十名

    镜像下载.域名解析.时间同步请点击 阿里云开源镜像站 阿里云开源镜像站利用云服务上的优势,提供快速.稳定的镜像分发服务.和免费的CDN加速服务.更新频率高,基本上一天一更新,对于Centos/Ubun ...

  6. 手把手教你从零写一个简单的 VUE

    本系列是一个教程,下面贴下目录~1.手把手教你从零写一个简单的 VUE2.手把手教你从零写一个简单的 VUE--模板篇 今天给大家带来的是实现一个简单的类似 VUE 一样的前端框架,VUE 框架现在应 ...

  7. 《一步一步写嵌入式操作系统》读书笔记1—Skyeye介绍、安装和HelloWorld

    2013-11-14 最近在看<一步一步写嵌入式操作系统>,感觉此书甚好,许多地方讲得很清楚.可操作性强,计划边读边实践边写笔记,希望能够逐步熟悉嵌入式操作系统底层的东西,最终剪裁出一套实 ...

  8. Python之美[从菜鸟到高手]--一步一步动手给Python写扩展(异常处理和引用计数)

    我们将继续一步一步动手给Python写扩展,通过上一篇我们学习了如何写扩展,本篇将介绍一些高级话题,如异常,引用计数问题等.强烈建议先看上一篇,Python之美[从菜鸟到高手]--一步一步动手给Pyt ...

  9. Zedboard学习(一):移植Ubuntu桌面操作系统 标签: ubuntu移植zedboardFPGA 2017-07-04 21:53 26人阅读

    环境准备: 首先,需要的肯定是Ubuntu操作系统.可以在自己的电脑上安装物理机,也可以是虚拟机下运行的.我的是在Vmware下运行的Ubuntu14.04 32位操作系统. 由于zedboard上的 ...

  10. 27、从零写UVC驱动之分析数据传输(设置ubuntu通过串口打印,指定打印到文件,ubuntu切换root用户)

    A. 设置ubuntu让它从串口0输出printk信息a. 设置vmware添加serial port, 使用文件作为串口(在vmware中设置,文件是保存在windows中)b. 启动ubuntu, ...

随机推荐

  1. redis - 常用方法封装总结

    package com.citydo.utils; import org.springframework.data.redis.connection.DataType; import org.spri ...

  2. Pnetlab中锐捷镜像反复重启或telnet无法键入内容

    PNETLab 版本: 5.2.7 或 5.3.3等 锐捷镜像版本: V1.03 故障详情: 基于前文的系统环境和锐捷镜像.替换后的yml文件,更新PnetLAB版本到5.3.3后,设备循环重启,无法 ...

  3. AX2012 去掉浮点数后面的0

    static void Job116(Args _args) { str string1; real num1; ; num1 = 0.00; string1 = System.String::For ...

  4. 项目:口令保管箱,批处理文件配置.bat

    #! python3 import sys import pyperclip PASSWORDS = {'email': 'F7minlBDDuvMJuxESSKHFhTxFtjVB6', 'blog ...

  5. ubuntu14.04 网络配置ubuntu14.04 网络配置

    流程分析: 在Ubuntu系统网络设备启动的流程中,会依赖/etc/network/interface的配置文件初始化网络接口,所以直接在/etc/network/interface之中配置好对应的d ...

  6. js字符串常用的方法

    1.  charAt( ) 获取指定下标处的字符 let str = 'hello' console.log(str.charAt(0));//h 2.  charCodeAt 获取下标出的字符的Un ...

  7. Android adb命令 安装

    adb的全称为Android Debug Bridge.是android司机经常用到的工具. 查看本地adb版本 打开cmd 命令:adb version 显示adb版本.如果显示不是内部或者外部命令 ...

  8. CF1422

    CF1422 那个博客搭好遥遥无期. A: 看代码就行. #include<bits/stdc++.h> using namespace std; void work() { int a, ...

  9. nojejs 弹出子窗口,取值后返回

    1.主窗口: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <ti ...

  10. Js 代码递归实现树形数据与数组相互转换。

    贴代码: // Grid->Tree 结构组装. var tree = []; this.setTreeData(table, tree, ""); //组装树形 setTr ...