2024-10-20 15:46:52 +00:00

1978 lines
523 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html><html lang="zh-CN" data-theme="dark"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0,viewport-fit=cover"><title>操统实验日志 第四章 勇者之路 | 時痕</title><meta name="author" content="Linloir"><meta name="copyright" content="Linloir"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#0d0d0d"><meta name="description" content="本章第一部分中,将会简要介绍在下一章中将要编写的 KernelLoader以及在开始着手进行它的编写之前所需要完成的包括各种驱动、文件系统接口等在内的诸多准备工作。在第一部分之后我决定按照 KernelLoader 中的函数调用顺序,逐节完成 KernelLoader 中所需要的所有准备工作例如如何在项目中使用C语言和汇编混合编程包括C语言是如何进行函数调用的以及内联汇编中NASM向A">
<meta property="og:type" content="article">
<meta property="og:title" content="操统实验日志 第四章 勇者之路">
<meta property="og:url" content="https://blog.linloir.cn/2022/08/20/os-journal-vol-4/">
<meta property="og:site_name" content="時痕">
<meta property="og:description" content="本章第一部分中,将会简要介绍在下一章中将要编写的 KernelLoader以及在开始着手进行它的编写之前所需要完成的包括各种驱动、文件系统接口等在内的诸多准备工作。在第一部分之后我决定按照 KernelLoader 中的函数调用顺序,逐节完成 KernelLoader 中所需要的所有准备工作例如如何在项目中使用C语言和汇编混合编程包括C语言是如何进行函数调用的以及内联汇编中NASM向A">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://blog.linloir.cn/img/cover.jpg">
<meta property="article:published_time" content="2022-08-20T05:44:51.000Z">
<meta property="article:modified_time" content="2024-10-20T15:46:30.815Z">
<meta property="article:author" content="Linloir">
<meta property="article:tag" content="操作系统">
<meta property="article:tag" content="大学">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://blog.linloir.cn/img/cover.jpg"><link rel="shortcut icon" href="/img/avatar.png"><link rel="canonical" href="https://blog.linloir.cn/2022/08/20/os-journal-vol-4/"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox/fancybox.min.css" media="print" onload="this.media='all'"><script>
(() => {
const saveToLocal = {
set: (key, value, ttl) => {
if (!ttl) return
const expiry = Date.now() + ttl * 86400000
localStorage.setItem(key, JSON.stringify({ value, expiry }))
},
get: key => {
const itemStr = localStorage.getItem(key)
if (!itemStr) return undefined
const { value, expiry } = JSON.parse(itemStr)
if (Date.now() > expiry) {
localStorage.removeItem(key)
return undefined
}
return value
}
}
window.btf = {
saveToLocal,
getScript: (url, attr = {}) => new Promise((resolve, reject) => {
const script = document.createElement('script')
script.src = url
script.async = true
Object.entries(attr).forEach(([key, val]) => script.setAttribute(key, val))
script.onload = script.onreadystatechange = () => {
if (!script.readyState || /loaded|complete/.test(script.readyState)) resolve()
}
script.onerror = reject
document.head.appendChild(script)
}),
getCSS: (url, id) => new Promise((resolve, reject) => {
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = url
if (id) link.id = id
link.onload = link.onreadystatechange = () => {
if (!link.readyState || /loaded|complete/.test(link.readyState)) resolve()
}
link.onerror = reject
document.head.appendChild(link)
}),
addGlobalFn: (key, fn, name = false, parent = window) => {
if (!true && key.startsWith('pjax')) return
const globalFn = parent.globalFn || {}
globalFn[key] = globalFn[key] || {}
if (name && globalFn[key][name]) return
globalFn[key][name || Object.keys(globalFn[key]).length] = fn
parent.globalFn = globalFn
}
}
const activateDarkMode = () => {
document.documentElement.setAttribute('data-theme', 'dark')
if (document.querySelector('meta[name="theme-color"]') !== null) {
document.querySelector('meta[name="theme-color"]').setAttribute('content', 'undefined')
}
}
const activateLightMode = () => {
document.documentElement.setAttribute('data-theme', 'light')
if (document.querySelector('meta[name="theme-color"]') !== null) {
document.querySelector('meta[name="theme-color"]').setAttribute('content', 'undefined')
}
}
btf.activateDarkMode = activateDarkMode
btf.activateLightMode = activateLightMode
const theme = saveToLocal.get('theme')
const mediaQueryDark = window.matchMedia('(prefers-color-scheme: dark)')
const mediaQueryLight = window.matchMedia('(prefers-color-scheme: light)')
if (theme === undefined) {
if (mediaQueryLight.matches) activateLightMode()
else if (mediaQueryDark.matches) activateDarkMode()
else {
const hour = new Date().getHours()
const isNight = hour <= 6 || hour >= 18
isNight ? activateDarkMode() : activateLightMode()
}
mediaQueryDark.addEventListener('change', () => {
if (saveToLocal.get('theme') === undefined) {
e.matches ? activateDarkMode() : activateLightMode()
}
})
} else {
theme === 'light' ? activateLightMode() : activateDarkMode()
}
const asideStatus = saveToLocal.get('aside-status')
if (asideStatus !== undefined) {
document.documentElement.classList.toggle('hide-aside', asideStatus === 'hide')
}
const detectApple = () => {
if (/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)) {
document.documentElement.classList.add('apple')
}
}
detectApple()
})()
</script><script>const GLOBAL_CONFIG = {
root: '/',
algolia: undefined,
localSearch: undefined,
translate: {"defaultEncoding":2,"translateDelay":0,"msgToTraditionalChinese":"繁","msgToSimplifiedChinese":"简"},
noticeOutdate: undefined,
highlight: {"plugin":"highlight.js","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false,"highlightFullpage":false,"highlightMacStyle":false},
copy: {
success: '复制成功',
error: '复制失败',
noSupport: '浏览器不支持'
},
relativeDate: {
homepage: false,
post: false
},
runtime: '',
dateSuffix: {
just: '刚刚',
min: '分钟前',
hour: '小时前',
day: '天前',
month: '个月前'
},
copyright: undefined,
lightbox: 'fancybox',
Snackbar: {"chs_to_cht":"已切换为繁体中文","cht_to_chs":"已切换为简体中文","day_to_night":"已切换为深色模式","night_to_day":"已切换为浅色模式","bgLight":"#49b1f5","bgDark":"#1f1f1f","position":"top-center"},
infinitegrid: {
js: 'https://cdn.jsdelivr.net/npm/@egjs/infinitegrid/dist/infinitegrid.min.js',
buttonText: '加载更多'
},
isPhotoFigcaption: false,
islazyload: false,
isAnchor: true,
percent: {
toc: false,
rightside: false,
},
autoDarkmode: true
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
title: '操统实验日志 第四章 勇者之路',
isPost: true,
isHome: false,
isHighlightShrink: false,
isToc: true,
postUpdate: '2024-10-20 23:46:30'
}</script><meta name="generator" content="Hexo 7.3.0"></head><body><script>window.paceOptions = {
restartOnPushState: false
}
btf.addGlobalFn('pjaxSend', () => {
Pace.restart()
}, 'pace_restart')
</script><link rel="stylesheet" href="/css/minimal.css"/><script src="https://cdn.jsdelivr.net/npm/pace-js/pace.min.js"></script><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="/img/avatar.png" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">11</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">10</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">3</div></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-th"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url(/img/cover.jpg);"><nav id="nav"><span id="blog-info"><a class="nav-site-title" href="/"><span class="site-name">時痕</span></a><a class="nav-page-title" href="/"><span class="site-name">操统实验日志 第四章 勇者之路</span></a></span><div id="menus"><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-th"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div></div><div id="toggle-menu"><span class="site-page"><i class="fas fa-bars fa-fw"></i></span></div></div></nav><div id="post-info"><h1 class="post-title">操统实验日志 第四章 勇者之路</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-08-20T05:44:51.000Z" title="发表于 2022-08-20 13:44:51">2022-08-20</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2024-10-20T15:46:30.815Z" title="更新于 2024-10-20 23:46:30">2024-10-20</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/%E6%8A%80%E6%9C%AF/">技术</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">总字数:</span><span class="word-count">35.8k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>135分钟</span></span><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title=""><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">浏览量:</span><span id="busuanzi_value_page_pv"><i class="fa-solid fa-spinner fa-spin"></i></span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h2 id="关于本章-2">关于本章</h2>
<p>在本章节的<a href="#%E6%9C%80%E5%90%8E%E4%B8%80%E4%B8%AA%E8%B7%B3%E6%9D%BFkernelloader">第一部分</a>中,将会简要介绍在<a href="https://blog.linloir.cn/2022/08/10/os-journal-vol-5/index.html">下一章</a>中将要编写的KernelLoader以及在开始着手进行它的编写之前所需要完成的包括各种驱动、文件系统接口等在内的诸多准备工作。</p>
<p>在第一部分之后我决定按照KernelLoader中的函数调用顺序逐节完成KernelLoader中所需要的所有准备工作因此在<a href="#%E7%86%9F%E6%82%89%E7%9A%84%E6%9C%8B%E5%8F%8Bc%E9%97%AA%E4%BA%AE%E7%99%BB%E5%9C%BA">第二部分</a>将会首先记录如何在项目中使用C语言和汇编混合编程包括C语言是如何进行函数调用的以及内联汇编中NASM向AT&amp;T迁移语法所需要注意的问题。有了这部分基础知识就可以进行<a href="#%E6%8A%BD%E8%B1%A1MBR%E7%BB%93%E6%9E%84">第三部分</a>编写一些常用的驱动,并从我个人的角度讲讲为什么要这么做,它对后续的代码编写能够起到哪些帮助。</p>
<p>在之后的<a href="#%E6%8A%BD%E8%B1%A1fat%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F">第四部分</a>中,会进行有关文件系统的知识的详述,并且带领大家阅读<a href="/img/os-journal-vol-4/fat_specification.pdf">微软关于FAT文件系统的文档</a>根据文档完成FAT文件系统接口的设计和实现。</p>
<p><a href="#%E6%8A%BD%E8%B1%A1mbr%E7%BB%93%E6%9E%84">第五部分</a>则会关注如何在多个分区的磁盘上通过MBR读取不同分区的信息从而为正确读取文件系统中的文件提供条件</p>
<p><a href="#%E6%8A%BD%E8%B1%A1%E9%A1%B5%E8%A1%A8">第六部分</a>中,会介绍我个人认为的操作系统中最重要的概念之一的分页机制,并根据我对它的理解,完成对分页机制中主要数据结构的抽象。</p>
<p>如果你恰好像我一样,在第二部分中阅读的是人生中第一篇官方的文档的话,应当能够克服曾经对于文档的恐惧,甚至会在阅读后面的小节时仍意犹未尽,激动不已,那么<a href="#%E6%8A%BD%E8%B1%A1elf%E6%96%87%E4%BB%B6">第七部分</a>将会趁热打铁,跟随<a href="/assets/os-journal-vol-4/elf_format.pdf">关于ELF文件的文档</a>完成对ELF文件格式中用到的主要数据结构的抽象。</p>
<div class="note red icon-padding flat"><i class="note-icon fas fa-exclamation-circle"></i><p><strong>本章内容提示:</strong><br>
是的,这一章内容确乎是超乎我想象得多,这也就导致了学校官方教程中<strong>绕过了这一部分直接进入Kernel先着手编写Kernel中的内容</strong><br>
事实上,就实现 <em>从BootLoader加载到Kernel</em> 这一步骤甚至KernelLoader都并不是必须的<strong>省略这一部分并不是没有代价的</strong>由于省去了进入Kernel前的分页机制准备工作学校教程不得不在<a target="_blank" rel="noopener" href="https://github.com/yat-sen-os/SYSU-2021-Spring-Operating-System/tree/main/lab8">第八次实验</a>中返回BootLoader添加开启分页的代码<br>
除此之外在我们要实现的ElfLoader的加持下<strong>Kernel不再需要以<code>.bin</code>的形式编译,也不再需要单独的<code>entry.asm</code>文件作为入口,并且可以加载到任意指定的地址处</strong></p>
</div>
<h2 id="最后一个跳板KernelLoader">最后一个跳板KernelLoader</h2>
<p><a href="https://blog.linloir.cn/2022/07/19/os-journal-vol-3/index.html">上一章</a>已经成功地从MBR跳转进入了BootLoader并且在BootLoader中开启了保护模式。</p>
<p>在成功开启了保护模式之后其实就已经可以进入内核运行了。事实上学校的教程也确乎是这么实现的就如同MBR跳转BootLoader那样将内核编译为<code>.bin</code>文件并从BootLoader加载后跳转到内核加载地址上运行。</p>
<p>而我的实现则相较于学校教程而言复杂了许多在我的实现中BootLoader与Kernel间额外添加了一层跳板也就是在下一章中要完成的KernelLoader它负责从硬盘活动分区的<code>/system/</code>文件夹中读取<code>kernel.elf</code>文件,并且解析<code>kernel.elf</code>文件中的信息将内核加载到正确的地址之后开启分页机制最后跳转到Kernel运行。</p>
<p>看上去,学校教程的实现方式更加简单,但这样的捷径并非没有代价,至于学校教程有哪些弊端,以及为什么我采用了一条这样的路线,在后续小节和章节的实现中,都会渐渐明朗。</p>
<p>为了完成这样一个复杂的跳板程序,需要为其做许多的前期准备工作:</p>
<ul>
<li><strong>驱动准备</strong>文件读取离不开对硬盘的读写操作硬盘的读写则离不开对接口的读写。自然使用类似于MBR和BootLoader中使用汇编是一种解决方式但是为了增强代码可读性不妨使用C++对这部分操作进行包装,提供接口供程序使用</li>
<li><strong>文件系统的抽象</strong>:为了能够让程序能够识别硬盘中的文件系统,并且从文件系统中找到需要的路径和文件,就需要对文件系统的组织结构进行抽象,提供更为直观的函数接口供程序调用</li>
<li><strong>MBR的抽象</strong>对于一个多分区的磁盘MBR会存储关于分区的位置信息和属性如果需要加载磁盘分区文件系统中的文件那么从MBR获得关于分区的信息就是必不可少的第一步。因此需要了解数据在MBR中是怎样存储的以及怎样从MBR中提取关于分区的信息</li>
<li><strong>页表结构抽象</strong>:在后面实现虚拟地址和分页机制时,需要用到一些特殊的数据结构,将这些数据结构抽象成类并提供接口可以大大提高代码的可读性</li>
<li><strong>Elf文件结构抽象</strong><code>.elf</code>文件中包含了一个加载并执行一个程序的诸多关键信息,例如程序需要加载到的地址、程序的入口点等等。为了解析文件内的信息,需要像文件系统一样,对<code>.elf</code>文件的结构进行抽象,最后提供接口供程序使用</li>
</ul>
<p>这也就意味着我们的项目目录也会产生一些变化:</p>
<ul>
<li>由于需要实现一系列驱动,所以需要在<code>src</code>目录下添加<code>driver</code>文件夹</li>
<li>需要实现端口和硬盘的驱动,所以需要在<code>driver</code>文件夹下添加<code>hdd</code><code>port</code>文件夹</li>
<li>由于需要实现许多抽象后的结构,所以需要一个<code>struct</code>文件夹</li>
<li>需要对文件系统、ELF文件、MBR和页表进行抽象所以需要为它们的文件准备<code>fs</code><code>elf</code><code>mbr</code><code>paging</code>文件夹</li>
<li>同时,操作系统的代码还会依赖一些可重用的功能函数,例如常用的字符串处理函数、<code>memcpy</code><code>memset</code>等等,这些都可以被称作操作系统需要用到的“库”,可以放在<code>src</code>目录下的<code>lib</code>文件夹中。同样,抽象后的结构也可以看作库的一部分,所以将<code>struct</code>文件夹也放置在<code>lib</code>文件夹下。</li>
<li>在之后的代码中还会用到一些通用的常数值和一些常用的类别如<code>unsigned int</code><code>unsigned char</code><code>unsigned long long</code>和它们的别名<code>uint32</code><code>uint8</code><code>uint64</code>等等,可以将它们编写到头文件<code>constants.h</code><code>types.h</code>中。由于它们同样可以视为操作系统库的一部分,所以可以放在<code>lib</code>中叫做<code>common</code>的文件夹下</li>
<li>此外,可以提前为内核准备文件夹<code>kernel</code>,放置在<code>src</code>目录下</li>
</ul>
<p>在准备妥当后,新的项目结构如下</p>
<figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── build</span><br><span class="line">├── run</span><br><span class="line">└── src</span><br><span class="line"> ├── boot</span><br><span class="line"> │   ├── bootloader.asm</span><br><span class="line"> │   ├── boot.inc</span><br><span class="line"> │   ├── klentry.asm</span><br><span class="line"> │   └── mbr.asm</span><br><span class="line"> ├── driver</span><br><span class="line"> │   ├── harddisk</span><br><span class="line"> │   └── port</span><br><span class="line"> ├── kernel</span><br><span class="line"> └── lib</span><br><span class="line"> ├── common</span><br><span class="line"> │   ├── constants.h</span><br><span class="line"> │   └── types.h</span><br><span class="line"> ├── mem</span><br><span class="line"> │   ├── memory.cpp</span><br><span class="line"> │   └── memory.h</span><br><span class="line"> ├── str</span><br><span class="line"> │   ├── str.cpp</span><br><span class="line"> │   └── str.h</span><br><span class="line"> └── struct</span><br><span class="line"> ├── elf</span><br><span class="line"> ├── fs</span><br><span class="line"> ├── mbr</span><br><span class="line"> └── paging</span><br></pre></td></tr></table></figure>
<p>现在,就让我们开始接下来激动人心的旅程吧!</p>
<h2 id="熟悉的朋友C-闪亮登场">熟悉的朋友C++闪亮登场</h2>
<p><s>大家不会觉得KernelLoader要使用汇编完成吧不会吧不会吧</s></p>
<p>离开了实模式进入32位保护模式后C++就已经可以大展身手了也就是说从接下来开始就将脱离汇编苦海投入C++的怀抱之中了。</p>
<p>不过在正式开始C++的工作前需要完成一些交接的工作在这一小节中将会介绍一个C++程序是如何从源文件编程可执行文件的以及如何进行C++与汇编的混合编程</p>
<h3 id="从代码到可执行文件">从代码到可执行文件</h3>
<div class="note blue icon-padding flat"><i class="note-icon fas fa-external-link-square"></i><p><strong>参考:</strong><br>
本节内容参考了学校教程第四次实验中<a target="_blank" rel="noopener" href="https://github.com/yat-sen-os/SYSU-2021-Spring-Operating-System/blob/main/lab4/README.md#%E4%BB%8E%E4%BB%A3%E7%A0%81%E5%88%B0%E5%8F%AF%E6%89%A7%E8%A1%8C%E6%96%87%E4%BB%B6">从代码到可执行文件</a>一章以及<a target="_blank" rel="noopener" href="https://github.com/yat-sen-os/SYSU-2021-Spring-Operating-System/blob/main/lab4/README.md#cc%E5%92%8C%E6%B1%87%E7%BC%96%E6%B7%B7%E5%90%88%E7%BC%96%E7%A8%8B">C/C++和汇编混合编程</a>一章的内容</p>
</div>
<p>在编译器将代码编译成可执行文件的过程中,其实际上进行了四个步骤:预处理、编译、汇编和链接</p>
<p><strong>预处理</strong>又称为预编译,在这一环节中,编译器主要处理宏定义,如<code>#include</code>, <code>#define</code>, <code>#ifndef</code>等,并删除注释行,还会添加行号和文件名标识,在编译时编译器会使用上述信息产生调试、警告和编译错误时需要用到的行号信息。<br>
经过预编译生成的<code>.i</code>文件不包含任何宏定义,因为所有的宏已经被展开,并且包含的文件也已经被插入到<code>.i</code>文件中</p>
<p>我们可以通过新建一个简单的C程序<code>main.cpp</code>来测试这一步骤:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">&quot;Hello world!\n&quot;</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>之后在编译命令中使用<code>-E</code>参数只进行预编译步骤</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gcc -o main.i -E main.cpp</span><br></pre></td></tr></table></figure>
<div class="note info flat"><p><strong>查看Linux关于指令的手册</strong><br>
如果想要知道关于某一程序参数的具体描述,可以使用<code>man</code>命令<br>
例如,<code>man gcc</code>可以查看关于<code>gcc</code>所有参数的描述和用法,其中关于<code>-E</code>参数就有如下描述:<br>
<em>-E Stop after the preprocessing stage; do not run the compiler proper. The output is in the form of preprocessed source code, which is sent to the standard output.</em></p>
</div>
<p>可以看到生成了一个<code>main.i</code>文件,其内容如下:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"># 1 &quot;main.cpp&quot;</span><br><span class="line"># 1 &quot;&lt;built-in&gt;&quot;</span><br><span class="line"># 1 &quot;&lt;command-line&gt;&quot;</span><br><span class="line"># 1 &quot;/usr/include/stdc-predef.h&quot; 1 3 4</span><br><span class="line"># 1 &quot;&lt;command-line&gt;&quot; 2</span><br><span class="line"># 1 &quot;main.cpp&quot;</span><br><span class="line">...</span><br><span class="line"># 10 &quot;main.cpp&quot;</span><br><span class="line">int main() &#123;</span><br><span class="line"> printf(&quot;Hello world!\n&quot;);</span><br><span class="line"> return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>编译</strong>是将预处理后的代码翻译成汇编语法的步骤,具体包括了<strong>词法分析</strong><strong>语法分析</strong><strong>语义分析及相关的优化</strong><strong>中间代码生成</strong>以及<strong>目标代码生成</strong>五个环节</p>
<p><code>gcc</code>中使用<code>-S</code>参数可以进行编译步骤,<code>-masm=intel</code>指定输出的语法格式为<a href="https://blog.linloir.cn/2022/07/16/os-journal-vol-2/index.html#nasm%E8%AF%AD%E6%B3%95">第二章</a>中介绍的Intel汇编语法</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gcc -o main.s -S main.cpp -masm=intel</span><br></pre></td></tr></table></figure>
<p>生成的<code>main.s</code>文件如下:</p>
<figure class="highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"> .file</span> <span class="string">&quot;main.cpp&quot;</span></span><br><span class="line"><span class="meta"> .text</span></span><br><span class="line"><span class="meta"> .section</span> .rodata</span><br><span class="line"><span class="symbol">.LC0:</span></span><br><span class="line"><span class="meta"> .string</span> <span class="string">&quot;Hello world!&quot;</span></span><br><span class="line"><span class="meta"> .text</span></span><br><span class="line"><span class="meta"> .globl</span> main</span><br><span class="line"><span class="meta"> .type</span> main, @function</span><br><span class="line"><span class="symbol">main:</span></span><br><span class="line"><span class="symbol">.LFB0:</span></span><br><span class="line"><span class="meta"> .cfi_startproc</span></span><br><span class="line"> endbr64</span><br><span class="line"> pushq %rbp</span><br><span class="line"><span class="meta"> .cfi_def_cfa_offset</span> <span class="number">16</span></span><br><span class="line"><span class="meta"> .cfi_offset</span> <span class="number">6</span>, -<span class="number">16</span></span><br><span class="line"> <span class="keyword">movq</span> %rsp, %rbp</span><br><span class="line"><span class="meta"> .cfi_def_cfa_register</span> <span class="number">6</span></span><br><span class="line"> leaq .LC0(%rip), %rdi</span><br><span class="line"> <span class="keyword">call</span> puts@PLT</span><br><span class="line"> movl <span class="number">$0</span>, %eax</span><br><span class="line"> popq %rbp</span><br><span class="line"><span class="meta"> .cfi_def_cfa</span> <span class="number">7</span>, <span class="number">8</span></span><br><span class="line"> <span class="keyword">ret</span></span><br><span class="line"><span class="meta"> .cfi_endproc</span></span><br><span class="line"><span class="symbol">.LFE0:</span></span><br><span class="line"><span class="meta"> .size</span> main, .-main</span><br><span class="line"><span class="meta"> .ident</span> <span class="string">&quot;GCC: (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0&quot;</span></span><br><span class="line"><span class="meta"> .section</span> .note<span class="number">.</span>GNU-stack,<span class="string">&quot;&quot;</span>,@progbits</span><br><span class="line"><span class="meta"> .section</span> .note<span class="number">.</span>gnu<span class="number">.</span>property,<span class="string">&quot;a&quot;</span></span><br><span class="line"><span class="meta"> .align</span> <span class="number">8</span></span><br><span class="line"><span class="meta"> .long</span> 1f - 0f</span><br><span class="line"><span class="meta"> .long</span> 4f - 1f</span><br><span class="line"><span class="meta"> .long</span> <span class="number">5</span></span><br><span class="line"><span class="number">0</span>:</span><br><span class="line"><span class="meta"> .string</span> <span class="string">&quot;GNU&quot;</span></span><br><span class="line"><span class="number">1</span>:</span><br><span class="line"><span class="meta"> .align</span> <span class="number">8</span></span><br><span class="line"><span class="meta"> .long</span> <span class="number">0xc0000002</span></span><br><span class="line"><span class="meta"> .long</span> 3f - 2f</span><br><span class="line"><span class="number">2</span>:</span><br><span class="line"><span class="meta"> .long</span> <span class="number">0x3</span></span><br><span class="line"><span class="number">3</span>:</span><br><span class="line"><span class="meta"> .align</span> <span class="number">8</span></span><br><span class="line"><span class="number">4</span>:</span><br></pre></td></tr></table></figure>
<p><strong>汇编</strong>步骤中编译生成的汇编代码将会被翻译成机器语言并且组织成为可重定位文件的格式。在Linux下可重定位文件是ELF格式<a href="/img/os-journal-vol-4/elf_format.pdf">关于ELF格式的文档</a>中详细地介绍了ELF格式中的各个部分以及它们如何对重定位起作用。</p>
<div class="note info flat"><p><strong>ELF文件有多种类别</strong><br>
ELF不仅可以用来描述需要重定位的文件也可以用在<strong>链接</strong>环节的生成的可执行文件中,在本文的<a href="#%E6%8A%BD%E8%B1%A1elf%E6%96%87%E4%BB%B6">第六部分</a>就要根据文档来介绍如何解析一个可执行的ELF格式文件</p>
</div>
<p>汇编步骤仅仅只是将单个的文件转换成了机器语言如果项目中有许多文件它们中又存在着调用关系的话此时是没有办法执行的。可以这么理解在编译器进行汇编的时候它会遇到一些只有声明而没有定义的符号例如一些外部函数和变量这部分函数和变量的地址此时还并不知道因此编译就会将它们标记起来放置在ELF文件一些特殊的区域存储</p>
<p><strong>链接</strong>步骤中,链接器接受多个输入的可重定位文件,根据输入的参数设置,可以规定程序的入口点地址、程序代码段放置的地址等,然后按照设置的地址将文件中的各个部分组合拼接在一起。</p>
<p>由于在链接的步骤中,程序起始的地址通过参数规定,同时所有的文件此时都“齐聚一堂”,每个文件各个代码段就可以像安排座位那样依次入座了,整个程序各个符号的绝对地址也就确定了。这样,原先那些没有定义的符号此刻也能够找到了,并且也具有了绝对的地址值,各个文件中的相对地址此刻也都有了对应的绝对地址值,链接器根据这些新确定的绝对地址替换掉类似<code>jmp</code><code>call</code>中原先使用的值,将整个程序完整地串联在一起。</p>
<div class="note info flat"><p><strong>类似<code>jmp</code>这样的跳转指令,就是在链接的步骤中,才真正确定跳转的绝对地址的。</strong></p>
</div>
<h3 id="C-与汇编混编">C++与汇编混编</h3>
<p>即便有了C++这一利器,在实验中的许多地方仍然不可避免会需要用到汇编语言,例如需要操作某个特殊寄存器或是需要读写端口的时候。在这种情况下,有两种可行的解决方案,分别是 <strong>内联汇编</strong><strong>调用汇编函数</strong></p>
<p>学校所提供的教程在第四次实验的<a target="_blank" rel="noopener" href="https://github.com/yat-sen-os/SYSU-2021-Spring-Operating-System/blob/main/lab4/README.md#cc%E5%92%8C%E6%B1%87%E7%BC%96%E6%B7%B7%E5%90%88%E7%BC%96%E7%A8%8B">C/C++和汇编混合编程</a>这一部分介绍了调用汇编函数这一解决方案</p>
<p>在这个解决方案中,主要需要三个步骤:</p>
<ol>
<li>将需要通过汇编实现的功能通过汇编函数的形式写在<code>.asm</code>文件中,并声明<code>global</code></li>
<li>在需要调用函数的C程序头文件中提供<code>extern &quot;C&quot; boo baz()</code>这样的外部函数声明,并在程序中调用这个函数来执行对应的功能</li>
<li>在链接步骤中将汇编和C程序一同链接</li>
</ol>
<p>此外,为了能够让汇编函数的编写和调用能够顺利进行,还需要了解编译器是如何处理带参函数的调用的,我们不妨写一个带有参数的函数来试一试</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">(<span class="type">int</span> a, <span class="type">char</span> b)</span> </span>&#123;</span><br><span class="line"> <span class="comment">//Do nothing here</span></span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="built_in">func</span>(<span class="number">1</span>, <span class="string">&#x27;A&#x27;</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>之后使用编译器编译成汇编代码其中main函数对应调用func部分的代码如下</p>
<figure class="highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">push</span> <span class="number">65</span></span><br><span class="line"><span class="keyword">push</span> <span class="number">1</span></span><br><span class="line"><span class="keyword">call</span> _Z4funcic</span><br><span class="line"><span class="keyword">add</span> <span class="built_in">esp</span>, <span class="number">8</span></span><br></pre></td></tr></table></figure>
<p>可以看出,两个<code>push</code>操作分别向栈内压入了<code>'A'</code><code>1</code>两个参数,之后使用<code>call</code>指令调用函数,在函数返回后再使用<code>add esp, 8</code>将参数移出栈,</p>
<p><img src="/img/os-journal-vol-4/IMG_20220809-143742401.png" alt="图 1"></p>
<p>进入函数内也不能干坐着按照C语言的规定被调函数需要使用<code>ebp</code>从栈上取值,同时还需要为主调函数保留<code>ebp</code>寄存器的值,因此可以看到在汇编被调函数内部有这样的代码</p>
<figure class="highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">push</span> <span class="built_in">ebp</span> //Preserve <span class="built_in">ebp</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ebp</span>, <span class="built_in">esp</span> //Move <span class="built_in">esp</span> to <span class="built_in">ebp</span></span><br><span class="line"></span><br><span class="line">//Function body</span><br><span class="line"></span><br><span class="line"><span class="keyword">pop</span> <span class="built_in">ebp</span> //Restore <span class="built_in">ebp</span></span><br><span class="line"><span class="keyword">ret</span> //Return</span><br></pre></td></tr></table></figure>
<p>当然尽管C语言似乎并没有要求被调函数主动保存其他寄存器的值但我个人而言还是习惯在进入函数之后进行一次<code>pushad</code>操作主动保存,这也就让固定的函数骨架变为了</p>
<figure class="highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">push</span> <span class="built_in">ebp</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ebp</span>, <span class="built_in">esp</span></span><br><span class="line"><span class="keyword">pushad</span></span><br><span class="line"></span><br><span class="line">//Function body</span><br><span class="line"></span><br><span class="line"><span class="keyword">popad</span></span><br><span class="line"><span class="keyword">pop</span> <span class="built_in">ebp</span></span><br><span class="line"><span class="keyword">ret</span></span><br></pre></td></tr></table></figure>
<div class="note blue icon-padding flat"><i class="note-icon fas fa-external-link-square"></i><p><strong>关于调用汇编函数的实例:</strong><br>
如果想要查看关于上述方案的实例,可以参考学校教程的第四次实验 <a target="_blank" rel="noopener" href="https://github.com/yat-sen-os/SYSU-2021-Spring-Operating-System/blob/main/lab4/README.md#example-1-%E6%B7%B7%E5%90%88%E7%BC%96%E7%A8%8B">Example 1 混合编程</a> 相关内容</p>
</div>
<p>上述方法为了调用一个汇编函数还要额外添加一个<code>.asm</code>文件,实在是不够优雅,所以我在后续的实现中改为了使用<strong>内联汇编</strong>来完成汇编代码的部分</p>
<p>实际上,内联汇编的使用极其简单(对于实验中所需要用到的部分而言),由于主要需要和内联汇编打交道的地方几乎无外乎都是要么从寄存器读出一个值,要么往寄存器写入一个值,所以并不需要学习多么复杂的语法,够用即可</p>
<p>在GCC中使用内联汇编只需要使用<code>asm</code>关键字,如果不希望编译器改动汇编代码顺序,可以额外添加<code>volatile</code>关键字,于是,一个最简单的内联汇编语句可以长这样:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">asm</span> <span class="title">volatile</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;nop&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function">)</span></span>;</span><br></pre></td></tr></table></figure>
<p>如果代码有很多行的话,就长这样:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">asm</span> <span class="title">volatile</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;nop\n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;nop\n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;nop&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function">)</span></span>;</span><br></pre></td></tr></table></figure>
<p>是否已经跃跃欲试了且慢内联汇编默认使用的是AT&amp;T的语法因此我们需要进行一次Intel语法向AT&amp;T语法的迁移在了解了Intel与AT&amp;T语法的主要区别之后我们不论是写<code>.asm</code>文件还是写内联汇编都将得心应手</p>
<div class="note blue icon-padding flat"><i class="note-icon fas fa-external-link-square"></i><p><strong>参考资料:</strong><br>
关于语法迁移的内容,参考了 <a target="_blank" rel="noopener" href="http://xbtzone.com/blog/gcc-assembler/">GCC 内联汇编与 Intel 语法迁移</a> 一文</p>
</div>
<p>Intel和AT&amp;T的语法区别无外乎以下几点</p>
<ul>
<li><strong>操作数位置相反</strong><br>
在Intel语法中一个带有目的操作数和源操作数的汇编指令为<code>op [dest], [src]</code><br>
而在AT&amp;T语法中这样的指令则为<code>op [src], [dest]</code><br>
也就是说AT&amp;T中源操作数和目的操作数在指令中的位置与Intel语法中相反</li>
<li><strong>操作数写法</strong><br>
在Intel语法中寄存器直接写作寄存器名字<code>eax</code>;立即数直接写立即数值,如<code>0x1000</code><br>
而在AT&amp;T语法中寄存器前要用<code>%</code>标明,如<code>%eax</code>;立即数前用<code>$</code>标明,如<code>$0x1000</code></li>
<li><strong>寻址格式</strong><br>
在Intel语法中使用基址变址寻址方式的汇编代码格式形如<code>[ebx + esi * 2 - 0x10]</code><br>
而在AT&amp;T语法中同样的汇编代码应写为<code>-0x10(%ebx, %esi, 2)</code><br>
也就是在AT&amp;T语法中使用了基址<code>base</code>、索引<code>index</code>、元素大小<code>size</code>以及偏移量<code>disp</code>四个量进行寻址,格式为<code>disp(base, index, size)</code></li>
<li><strong>操作数大小</strong><br>
在Intel语法中对于寄存器和立即数间的操作并不需要提供操作数的大小而在使用<code>[]</code>寻址的过程中,需要指定大小,如<code>mov eax, word [ebx]</code><br>
而在AT&amp;T语法中始终需要提供操作数的大小其中<code>b</code><code>byte</code><code>w</code><code>word</code><code>l</code><code>dword</code><code>q</code><code>qword</code>64-bit操作数的大小附加在指令名称后<code>movl</code><code>addl</code></li>
</ul>
<p>最后为了能够让内联汇编功能更强大GCC还提供了一系列扩展的指令操作</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">asm</span> <span class="title">volatile</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;nop&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> : <span class="comment">//Output</span></span></span></span><br><span class="line"><span class="params"><span class="function"> : <span class="comment">//Input</span></span></span></span><br><span class="line"><span class="params"><span class="function"> : <span class="comment">//Registers</span></span></span></span><br><span class="line"><span class="params"><span class="function">)</span></span></span><br></pre></td></tr></table></figure>
<p>可以看到在正常指令的尾部GCC允许程序员添加三个参数列表类似于<code>printf</code>函数那样,通过在汇编代码中插入特定的操作数,并在后面的参数列表中按照规定的格式提供变量,可以使用变量来 <em>“替换”</em> 代码中对应的操作数,从而起到将变量值写入寄存器或是将寄存器值写入变量的操作。</p>
<p>其中,参数列表中每一个参数都由一个可选的名称<code>[name]</code>,一个限制符如<code>&quot;=r&quot;</code><code>&quot;i&quot;</code>等以及对应的变量<code>(var)</code>组成。</p>
<p>限制符在实验中只需要用到<code>&quot;r&quot;</code>以及<code>&quot;i&quot;</code>,其中</p>
<ul>
<li><code>&quot;r&quot;</code>代表可以使用任意的寄存器</li>
<li><code>&quot;i&quot;</code>代表立即整型操作数</li>
</ul>
<p>除此之外,还有更多的限制符,如</p>
<ul>
<li><code>&quot;a&quot;</code>代表可以使用<code>%eax</code><code>%ax</code><code>%al</code></li>
<li><code>&quot;b&quot;</code>代表可以使用<code>%ebx</code><code>bax</code><code>%bl</code></li>
<li><code>&quot;c&quot;</code>代表可以使用<code>%ecx</code><code>%cx</code><code>%cl</code></li>
<li><code>&quot;d&quot;</code>代表可以使用<code>%edx</code><code>%dx</code><code>%dl</code></li>
<li><code>&quot;s&quot;</code>代表可以使用<code>%esi</code><code>%si</code></li>
<li><code>&quot;D&quot;</code>代表可以使用<code>%edi</code><code>%di</code></li>
<li><code>&quot;m&quot;</code>代表内存操作数</li>
<li><code>&quot;o&quot;</code>代表内存操作数,仅用于偏移量</li>
<li><code>&quot;V&quot;</code>代表内存操作数,仅用于非偏移量</li>
<li><code>&quot;n&quot;</code>代表立即整型操作数,允许已知的数值</li>
<li><code>&quot;g&quot;</code>代表允许任何寄存器,但寄存器不是常规的寄存器</li>
</ul>
<p>在汇编指令中,可以用<code>%0</code><code>%1</code><code>%n</code>这样从零开始的数字来表示参数列表中的第一个、第二个、…第n个变量例如</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> out_eax, in_eax = <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">asm</span> <span class="title">volatile</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;movl %%eax, %0\n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;movl %1, %%eax&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> : <span class="string">&quot;=r&quot;</span>(out_eax) <span class="comment">//%0</span></span></span></span><br><span class="line"><span class="params"><span class="function"> : <span class="string">&quot;r&quot;</span>(in_eax) <span class="comment">//%1</span></span></span></span><br><span class="line"><span class="params"><span class="function">)</span></span>;</span><br></pre></td></tr></table></figure>
<p>也可以使用<code>%[name]</code>的方式通过名称来直接对应参数列表中指定名字的变量,例如:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> out_eax, in_eax = <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">asm</span> <span class="title">volatile</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;movl %%eax, %[out]\n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;movl %[in], %%eax&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> : [out]<span class="string">&quot;=r&quot;</span>(out_eax)</span></span></span><br><span class="line"><span class="params"><span class="function"> : [in]<span class="string">&quot;r&quot;</span>(in_eax)</span></span></span><br><span class="line"><span class="params"><span class="function">)</span></span>;</span><br></pre></td></tr></table></figure>
<div class="note red icon-padding flat"><i class="note-icon fas fa-exclamation-circle"></i><p><strong>陷阱:额外的%</strong><br>
当使用了扩展内联汇编之后,由于诸如<code>%0</code>这样的操作数会使用一个<code>%</code>符号,为了区分,<strong>所有的语句中</strong>的寄存器都需要额外加一个<code>%</code>,例如<code>movl %0, %%eax</code></p>
</div>
<div class="note red icon-padding flat"><i class="note-icon fas fa-exclamation-circle"></i><p><strong>陷阱:变量大小与指令操作数大小对应</strong><br>
例如,如果使用了<code>movl</code>指令那么对应的参数列表中的变量也应当是32位长度例如<code>int</code></p>
</div>
<p>至此内联汇编的优势已然显现出来其不但减少了杂乱文件的数目而且还可以方便的与C程序中的变量进行操作在后续的实验中选择哪种方案相比心里也有了答案。</p>
<p>但不论选择哪一个方案完成C和汇编的混合编程至此我们都已经顺利完成了进入C++语言环境的全部知识准备,足够我们进入后面的小节继续实验的进程了。</p>
<h2 id="驱动:抽象硬件交互">驱动:抽象硬件交互</h2>
<p>在操作系统实现的各个环节中都离不开和硬件打交道的部分在我们已经完成的MBR中就有我们为了加载BootLoader而写的读取磁盘扇区的函数。</p>
<p>这些需要和硬件进行通信的操作往往都需要用到一些特殊的端口通过我们实现MBR的经历可以知道使用汇编的<code>in</code><code>out</code>指令就可以对端口进行读写操作而这样简单的操作如果是使用C++这样的语言进行编程的话却是无法实现的。</p>
<p>这时大家可能就会说了不是刚刚学完了C++和汇编混编吗,这不直接上来就是一个<code>asm (&quot;inb dx, al&quot;)</code>,很快啊!</p>
<p>不可否认这不失为一个解决方式但放在C++中,它就是显得不那么优雅:如果能把一个端口视为一个对象,比如<code>port</code>,直接<code>port.read()</code>,这才叫优雅嘛!</p>
<p>可喜的是这在C++中是完全可以实现的:只需要实现一个类,其持有一个端口号作为成员变量,读写操作的实现则都使用内联汇编完成。</p>
<p>这样就相当于在汇编操作外附加了一层C++的外衣将原先裸的内联汇编代码包装成了C++代码不仅提升了代码的可读性而且由于存在C++的类作为中间层,可以添加很多保护性的代码,比如判断非法端口的访问、只读端口的写入等等,相比直接操作裸汇编代码提升了安全性和灵活性。</p>
<h3 id="端口驱动">端口驱动</h3>
<p>在实现端口驱动的时候,可以根据端口的读写权限分为三个类:</p>
<ul>
<li><code>Port</code>类用来表示可读写的端口</li>
<li><code>PortWriteOnly</code>类用来表示只能写的端口</li>
<li><code>PortReadOnly</code>类用来表示只能读的端口</li>
</ul>
<p>对于<code>Port</code>类,由于汇编中的<code>in</code><code>out</code>指令都需要提供端口号,而既然我们现在打算将一个端口封装成一个<code>Port</code>的实例,那就不能每次调用读写函数的时候都提供一次端口号,而是要让实例在初始化的时候就记住它应当是哪个端口。由于这个端口号只有在初始化的时候被赋值,之后就不再需要更改或是暴露给程序,因此可以作为私有的<code>const</code>变量进行储存:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="type">const</span> uint16 _port;</span><br></pre></td></tr></table></figure>
<p>对于端口的读写函数,由于不同的端口位宽会存在不同,因此读写指令的返回值和参数的类型需要借助类模板的帮助:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Port</span> &#123;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function">T <span class="title">read</span><span class="params">()</span> <span class="type">const</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">write</span><span class="params">(T data)</span> <span class="type">const</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>又因为在内联汇编中,<code>in</code><code>out</code>指令都需要指定操作数的宽度,如<code>inb</code>或是<code>inl</code>,因此在读写函数的实现上,要使用<code>sizeof(T)</code>对模板类的类型参数位宽进行判断,再使用合适的内联汇编函数,例如:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line">T Port&lt;T&gt;::<span class="built_in">read</span>() <span class="type">const</span> &#123;</span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">sizeof</span>(dat) == <span class="number">1</span>) &#123;</span><br><span class="line"> <span class="comment">//8-bit port</span></span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(<span class="built_in">sizeof</span>(dat) == <span class="number">2</span>) &#123;</span><br><span class="line"> <span class="comment">//16-bit port</span></span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="comment">//32-bit port</span></span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>对于一个8位端口使用内联汇编的读取代码可以首先将端口号读取到<code>dx</code>,再使用<code>inb</code>指令读取端口数据到<code>al</code>,最后将数据从<code>al</code>写入到外部变量:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">asm</span> <span class="title">volatile</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;movw %[p], %%dx \n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;inb %%dx, %%al \n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;movb %%al, %[o] \n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> : [o] <span class="string">&quot;=r&quot;</span>(_dat)</span></span></span><br><span class="line"><span class="params"><span class="function"> : [p] <span class="string">&quot;r&quot;</span>(_port)</span></span></span><br><span class="line"><span class="params"><span class="function"> :</span></span></span><br><span class="line"><span class="params"><span class="function">)</span></span>;</span><br></pre></td></tr></table></figure>
<p>内联汇编在编译的时候会检查外部变量的大小是不是和对应指令中指定的操作数宽度相匹配,例如<code>movw</code>指令不能提供一个<code>uint16</code>类型的变量,因此如果直接将<code>T data</code>作为参数传入,会发生报错。对于这个问题,可以在使用<code>sizeof()</code>判断后,显示地将<code>data</code>转换为对应的大小,或是先赋值到一个明确宽度的变量(例如:<code>uint8 _data</code>再在C++中赋值给原先的变量:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line">T Port&lt;T&gt;::<span class="built_in">read</span>() <span class="type">const</span> &#123;</span><br><span class="line"> T dat;</span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">sizeof</span>(dat) == <span class="number">1</span>) &#123;</span><br><span class="line"> uint8 _dat = <span class="built_in">static_cast</span>&lt;uint8&gt;(dat);</span><br><span class="line"> <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;movw %[p], %%dx \n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;inb %%dx, %%al \n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;movb %%al, %[o] \n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> : [o] <span class="string">&quot;=r&quot;</span>(_dat) <span class="comment">//: [o] &quot;=r&quot;(dat) will cause compile time error</span></span></span></span><br><span class="line"><span class="params"><span class="function"> : [p] <span class="string">&quot;r&quot;</span>(_port)</span></span></span><br><span class="line"><span class="params"><span class="function"> :</span></span></span><br><span class="line"><span class="params"><span class="function"> )</span></span>;</span><br><span class="line"> dat = _dat;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"> <span class="keyword">return</span> dat;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>由于只写或是只读的端口在读、写函数的实现上与可读写端口中的读写函数没有任何区别,因此在我最初的实现中将<code>Port</code>类作为基类而<code>PortWriteOnly</code><code>PortReadOnly</code>私有继承<code>Port</code>,之后封装父类中对应的函数为类的公有接口,例如:在<code>Port</code>中的<code>public: void _write(T data)</code>函数在私有继承后,在<code>PortWriteOnly</code>中添加<code>public: void write(T data) &#123; _write(data); &#125;</code>来调用基类中的写接口,而不对读接口进行封装,来实现读写权限的限制。</p>
<p>但在后续的实验中我发现,即便父类中的函数由于继承已经实质是类的私有函数,但在<code>Intellisense</code>中仍然可以看到,感觉还是不够安全。因此我决定不继承的方式,转而对于<code>PortWriteOnly</code>存储一个<code>Port</code>类型的成员变量代替<code>Port</code>中的端口号,在写函数中调用<code>Port::write</code>函数,彻底杜绝在<code>PortWriteOnly</code>中出现<code>read</code>函数的提示。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PortWriteOnly</span> &#123;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">PortWriteOnly</span>(uint16 port) : _port(port) &#123;&#125;</span><br><span class="line"> <span class="keyword">private</span>:</span><br><span class="line"> <span class="type">const</span> Port&lt;T&gt; _port;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">write</span><span class="params">(T data)</span> <span class="type">const</span> </span>&#123; </span><br><span class="line"> _port.<span class="built_in">write</span>(data);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>完整的端口驱动<code>ports.h</code>如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;types.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> _PORT_H_</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _PORT_H_</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Port</span> &#123;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">Port</span>(uint16 port): _port(port) &#123;&#125;</span><br><span class="line"> <span class="keyword">private</span>:</span><br><span class="line"> <span class="type">const</span> uint16 _port;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function">T <span class="title">read</span><span class="params">()</span> <span class="type">const</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">write</span><span class="params">(T data)</span> <span class="type">const</span></span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PortWriteOnly</span> &#123;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">PortWriteOnly</span>(uint16 port) : _port(port) &#123;&#125;</span><br><span class="line"> <span class="keyword">private</span>:</span><br><span class="line"> <span class="type">const</span> Port&lt;T&gt; _port;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">write</span><span class="params">(T data)</span> <span class="type">const</span> </span>&#123; </span><br><span class="line"> _port.<span class="built_in">write</span>(data);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PortReadOnly</span> &#123;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">PortReadOnly</span>(uint16 port) : _port(port) &#123;&#125;</span><br><span class="line"> <span class="keyword">private</span>:</span><br><span class="line"> <span class="type">const</span> Port&lt;T&gt; _port;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function">T <span class="title">read</span><span class="params">()</span> <span class="type">const</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> _port.<span class="built_in">read</span>();</span><br><span class="line"> &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line">T Port&lt;T&gt;::<span class="built_in">read</span>() <span class="type">const</span> &#123;</span><br><span class="line"> T dat;</span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">sizeof</span>(dat) == <span class="number">1</span>) &#123;</span><br><span class="line"> uint8 _dat = <span class="built_in">static_cast</span>&lt;uint8&gt;(dat);</span><br><span class="line"> <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;movw %[p], %%dx \n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;inb %%dx, %%al \n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;movb %%al, %[o] \n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> : [o] <span class="string">&quot;=r&quot;</span>(_dat)</span></span></span><br><span class="line"><span class="params"><span class="function"> : [p] <span class="string">&quot;r&quot;</span>(_port)</span></span></span><br><span class="line"><span class="params"><span class="function"> :</span></span></span><br><span class="line"><span class="params"><span class="function"> )</span></span>;</span><br><span class="line"> dat = _dat;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(<span class="built_in">sizeof</span>(dat) == <span class="number">2</span>) &#123;</span><br><span class="line"> uint16 _dat = <span class="built_in">static_cast</span>&lt;uint16&gt;(dat);</span><br><span class="line"> <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;movw %[p], %%dx \n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;inw %%dx, %%ax \n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;movw %%ax, %[o] \n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> : [o] <span class="string">&quot;=r&quot;</span>(_dat)</span></span></span><br><span class="line"><span class="params"><span class="function"> : [p] <span class="string">&quot;r&quot;</span>(_port)</span></span></span><br><span class="line"><span class="params"><span class="function"> :</span></span></span><br><span class="line"><span class="params"><span class="function"> )</span></span>;</span><br><span class="line"> dat = _dat;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">else</span> &#123;</span><br><span class="line"> uint32 _dat = <span class="built_in">static_cast</span>&lt;uint32&gt;(dat);</span><br><span class="line"> <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;movw %[p], %%dx \n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;inl %%dx, %%eax \n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;movl %%eax, %[o] \n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> : [o] <span class="string">&quot;=r&quot;</span>(_dat)</span></span></span><br><span class="line"><span class="params"><span class="function"> : [p] <span class="string">&quot;r&quot;</span>(_port)</span></span></span><br><span class="line"><span class="params"><span class="function"> :</span></span></span><br><span class="line"><span class="params"><span class="function"> )</span></span>;</span><br><span class="line"> dat = _dat;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> dat;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="type">void</span> Port&lt;T&gt;::<span class="built_in">write</span>(T data) <span class="type">const</span> &#123;</span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">sizeof</span>(data) == <span class="number">1</span>) &#123;</span><br><span class="line"> <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;movb %[i], %%al \n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;movw %[p], %%dx \n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">&quot;outb %%al, %%dx \n\t&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function"> : </span></span></span><br><span class="line"><span class="params"><span class="function"> : [i] <span class="string">&quot;r&quot;</span>(<span class="keyword">static_cast</span>&lt;uint8&gt;(data)), [p] <span class="string">&quot;r&quot;</span>(_port)</span></span></span><br><span class="line"><span class="params"><span class="function"> )</span></span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(<span class="built_in">sizeof</span>(data) == <span class="number">2</span>) &#123;</span><br><span class="line"> <span class="keyword">asm</span> <span class="built_in">volatile</span>(</span><br><span class="line"> <span class="string">&quot;movw %[i], %%ax \n\t&quot;</span></span><br><span class="line"> <span class="string">&quot;movw %[p], %%dx \n\t&quot;</span></span><br><span class="line"> <span class="string">&quot;outw %%ax, %%dx \n\t&quot;</span></span><br><span class="line"> : </span><br><span class="line"> : [i] <span class="string">&quot;r&quot;</span>(<span class="built_in">static_cast</span>&lt;uint16&gt;(data)), [p] <span class="string">&quot;r&quot;</span>(_port)</span><br><span class="line"> );</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="keyword">asm</span> <span class="built_in">volatile</span>(</span><br><span class="line"> <span class="string">&quot;movl %[i], %%eax \n\t&quot;</span></span><br><span class="line"> <span class="string">&quot;movw %[p], %%dx \n\t&quot;</span></span><br><span class="line"> <span class="string">&quot;outl %%eax, %%dx \n\t&quot;</span></span><br><span class="line"> : </span><br><span class="line"> : [i] <span class="string">&quot;r&quot;</span>(<span class="built_in">static_cast</span>&lt;uint32&gt;(data)), [p] <span class="string">&quot;r&quot;</span>(_port)</span><br><span class="line"> );</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure>
<div class="note red icon-padding flat"><i class="note-icon fas fa-exclamation-circle"></i><p><strong>陷阱:模板类的函数应在头文件中定义</strong><br>
模板类的函数不能将函数声明写在头文件中,而将函数的定义写在另一个文件中,这样会导致在编译链接的时候出现无法找到函数定义的报错。<strong>所有的模板类函数都需要在头文件中就实现函数的定义</strong></p>
</div>
<h3 id="硬盘驱动">硬盘驱动</h3>
<div class="note blue icon-padding flat"><i class="note-icon fas fa-history"></i><p><strong>回顾:从硬盘读取多个扇区的数据</strong><br>
关于如何使用LBA模式通过端口读写磁盘可以回顾<a href="http://blog.linloir.cn/2022/07/19/os-journal-vol-3/#%E5%9C%A8LBA%E6%A8%A1%E5%BC%8F%E4%B8%8B%E9%80%9A%E8%BF%87%E7%AB%AF%E5%8F%A3%E8%AF%BB%E5%86%99%E7%A3%81%E7%9B%98">第三章中相关内容的介绍</a></p>
</div>
<p>要将硬盘的读写操作抽象成驱动在实验中使用了与第三章中相同的PIO模式对硬盘进行访问在这个模式下与硬盘通信需要使用到8个不同的端口</p>
<table>
<thead>
<tr>
<th style="text-align:center">端口号</th>
<th style="text-align:center">端口数据传输方向</th>
<th style="text-align:center">作用 (LBA28)</th>
<th style="text-align:center">描述</th>
<th style="text-align:center">位长 (LBA28)</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">0x1F0</td>
<td style="text-align:center">读/写</td>
<td style="text-align:center">数据寄存器</td>
<td style="text-align:center">硬盘读出/要写入的数据</td>
<td style="text-align:center">16-bit</td>
</tr>
<tr>
<td style="text-align:center">0x1F1</td>
<td style="text-align:center"></td>
<td style="text-align:center">错误码寄存器</td>
<td style="text-align:center">存储执行的ATA指令所产生的错误码</td>
<td style="text-align:center">8-bit</td>
</tr>
<tr>
<td style="text-align:center">0x1F1</td>
<td style="text-align:center"></td>
<td style="text-align:center">功能寄存器</td>
<td style="text-align:center">用来指定特定指令的接口功能</td>
<td style="text-align:center">8-bit</td>
</tr>
<tr>
<td style="text-align:center">0x1F2</td>
<td style="text-align:center">读/写</td>
<td style="text-align:center">扇区数寄存器</td>
<td style="text-align:center">存放需要读写的扇区数量</td>
<td style="text-align:center">8-bit</td>
</tr>
<tr>
<td style="text-align:center">0x1F3</td>
<td style="text-align:center">读/写</td>
<td style="text-align:center">起始扇区寄存器</td>
<td style="text-align:center">存放起始扇区0-7位</td>
<td style="text-align:center">8-bit</td>
</tr>
<tr>
<td style="text-align:center">0x1F4</td>
<td style="text-align:center">读/写</td>
<td style="text-align:center">起始扇区寄存器</td>
<td style="text-align:center">存放起始扇区8-15位</td>
<td style="text-align:center">8-bit</td>
</tr>
<tr>
<td style="text-align:center">0x1F5</td>
<td style="text-align:center">读/写</td>
<td style="text-align:center">起始扇区寄存器</td>
<td style="text-align:center">存放起始扇区16-23位</td>
<td style="text-align:center">8-bit</td>
</tr>
<tr>
<td style="text-align:center">0x1F6</td>
<td style="text-align:center">读/写</td>
<td style="text-align:center">磁盘、起始扇区寄存器</td>
<td style="text-align:center">选择磁盘和访问模式<br>存放起始扇区24-27位</td>
<td style="text-align:center">8-bit</td>
</tr>
<tr>
<td style="text-align:center">0x1F7</td>
<td style="text-align:center"></td>
<td style="text-align:center">状态寄存器</td>
<td style="text-align:center">读取当前磁盘状态</td>
<td style="text-align:center">8-bit</td>
</tr>
<tr>
<td style="text-align:center">0x1F7</td>
<td style="text-align:center"></td>
<td style="text-align:center">指令寄存器</td>
<td style="text-align:center">传送ATA指令</td>
<td style="text-align:center">8-bit</td>
</tr>
</tbody>
</table>
<p>既然前面已经实现了端口的驱动,那么在这里就可以直接投入使用了,将列表中的端口按照对应的读写权限和用途实例化作硬盘类<code>HDDManager</code>的私有成员变量:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="type">const</span> Port&lt;uint16&gt; _datPort = <span class="number">0x1F0</span>;</span><br><span class="line"> <span class="type">const</span> PortReadOnly&lt;uint8&gt; _errPort = <span class="number">0x1F1</span>;</span><br><span class="line"> <span class="type">const</span> PortWriteOnly&lt;uint8&gt; _funcPort = <span class="number">0x1F1</span>;</span><br><span class="line"> <span class="type">const</span> Port&lt;uint8&gt; _secCntPort = <span class="number">0x1F2</span>;</span><br><span class="line"> <span class="type">const</span> Port&lt;uint8&gt; _lbaLoPort = <span class="number">0x1F3</span>;</span><br><span class="line"> <span class="type">const</span> Port&lt;uint8&gt; _lbaMidPort = <span class="number">0x1F4</span>;</span><br><span class="line"> <span class="type">const</span> Port&lt;uint8&gt; _lbaHiPort = <span class="number">0x1F5</span>;</span><br><span class="line"> <span class="type">const</span> Port&lt;uint8&gt; _drivePort = <span class="number">0x1F6</span>;</span><br><span class="line"> <span class="type">const</span> PortReadOnly&lt;uint8&gt; _statusPort = <span class="number">0x1F7</span>;</span><br><span class="line"> <span class="type">const</span> PortWriteOnly&lt;uint8&gt; _cmdPort = <span class="number">0x1F7</span>;</span><br></pre></td></tr></table></figure>
<p>注意到,对于<code>0x1F7</code>端口,当其作为读端口时用来表示硬盘的状态,其表示形式为按位枚举,端口读出值的每一位都有对应的含义:</p>
<table>
<thead>
<tr>
<th style="text-align:center">端口号</th>
<th style="text-align:center"></th>
<th style="text-align:center">缩写</th>
<th style="text-align:center">作用</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">0x1F7 (Read)</td>
<td style="text-align:center">0</td>
<td style="text-align:center">ERR</td>
<td style="text-align:center">指示是否有错误发生,通过发送新指令可以清除该位</td>
</tr>
<tr>
<td style="text-align:center">0x1F7 (Read)</td>
<td style="text-align:center">1*</td>
<td style="text-align:center">IDX</td>
<td style="text-align:center">索引始终置为0</td>
</tr>
<tr>
<td style="text-align:center">0x1F7 (Read)</td>
<td style="text-align:center">2*</td>
<td style="text-align:center">CORR</td>
<td style="text-align:center">修正数据始终置位0</td>
</tr>
<tr>
<td style="text-align:center">0x1F7 (Read)</td>
<td style="text-align:center">3</td>
<td style="text-align:center">DRQ</td>
<td style="text-align:center"><code>0</code>:硬盘还不能交换数据<br><code>1</code>:硬盘存在可以读取的数据或是可以写入数据</td>
</tr>
<tr>
<td style="text-align:center">0x1F7 (Read)</td>
<td style="text-align:center">4*</td>
<td style="text-align:center">SRV</td>
<td style="text-align:center">重叠模式服务请求</td>
</tr>
<tr>
<td style="text-align:center">0x1F7 (Read)</td>
<td style="text-align:center">5*</td>
<td style="text-align:center">DF</td>
<td style="text-align:center">驱动器故障错误</td>
</tr>
<tr>
<td style="text-align:center">0x1F7 (Read)</td>
<td style="text-align:center">6*</td>
<td style="text-align:center">RDY</td>
<td style="text-align:center"><code>0</code>:驱动器发生了减速或是错误<br><code>1</code>:驱动器运转正常</td>
</tr>
<tr>
<td style="text-align:center">0x1F7 (Read)</td>
<td style="text-align:center">7</td>
<td style="text-align:center">BSY</td>
<td style="text-align:center">忙位<br><code>0</code>:空闲<br><code>1</code>:忙</td>
</tr>
</tbody>
</table>
<div class="note info flat"><p><strong>什么是按位枚举:</strong><br>
按位枚举就是指<strong>利用二进制位来表示某个事物的属性</strong><strong>不同的二进制位描述不同的属性</strong><br>
例如,对于一个人的特征来说,可以用<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mn>0</mn></msup></mrow><annotation encoding="application/x-tex">2^0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span></span></span></span></span></span></span></span>所在的位表示性别1为男0为女<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mn>1</mn></msup></mrow><annotation encoding="application/x-tex">2^1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span></span></span></span></span></span></span></span>所在的位表示是否是短发1为是0为否等等。<br>
需要注意的是,在使用按位枚举的时候,<strong>不同的二进制位应当描述不同的属性</strong>例如如果要表示性别应当使用一个二进制位的0和1来区别<strong>不是</strong>使用两个二进制位,一个置位表示男,另一个置位则表示女。</p>
</div>
<p>由上述的列表中可以看出<code>0x1F7</code>中的每个二进制位都确实描述了一个硬盘状态的不同属性,对于这样的二进制位枚举,可以通过<code>enum</code>来表示:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">enum</span> <span class="title class_">HDDStatus</span> &#123;</span><br><span class="line"> ERR_OCCURRED = <span class="number">1</span>,</span><br><span class="line"> IDX = <span class="number">1</span> &lt;&lt; <span class="number">1</span>,</span><br><span class="line"> CORR = <span class="number">1</span> &lt;&lt; <span class="number">2</span>,</span><br><span class="line"> DATA_READY = <span class="number">1</span> &lt;&lt; <span class="number">3</span>,</span><br><span class="line"> OVERLAPP_SRVICE_REQUEST = <span class="number">1</span> &lt;&lt; <span class="number">4</span>,</span><br><span class="line"> DRIVE_FAULT_ERR = <span class="number">1</span> &lt;&lt; <span class="number">5</span>,</span><br><span class="line"> DRIVE_READY = <span class="number">1</span> &lt;&lt; <span class="number">6</span>,</span><br><span class="line"> BUSY = <span class="number">1</span> &lt;&lt; <span class="number">7</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>但糟就糟在C++对按位枚举的支持并不好由于C++默认将枚举类型作为整型值处理,所以常见的按位操作是默认支持的:</p>
<ul>
<li><code>|</code>:按位或,合并存在的属性</li>
<li><code>&amp;</code>:按位与,用掩码取出某个属性的值,例如<code>flag &amp; 0x0F</code>可以单独取出低四位的属性值</li>
<li><code>~</code>:按位取反,用来反转所有属性值</li>
<li><code>^</code>:按位异或,用来反转某个属性值,例如<code>flag ^= 0x08</code>可以反转第4位属性的值</li>
</ul>
<p>但如果想重载一些特殊的运算,或是添加一些特殊的判断函数,枚举类型则显得无从下手了:</p>
<ul>
<li><code>-</code>:删除某个属性</li>
<li><code>+</code>:添加某个属性</li>
<li><code>contains(flag)</code>:判断是否包含<code>flag</code>中的全部属性</li>
</ul>
<p>为了支持这些操作可以使用C++的类对原本的枚举类型进行一次封装:<strong>将枚举类型声明在C++类内,使用一个私有变量存储实际的枚举值,并通过重载运算符的方式从而让类的实例能够支持上述所有的运算操作</strong></p>
<p>对于目前而言,为硬盘的状态属性定义类<code>HDDStatusFlag</code>,并将原先的枚举类型声明在这个类内:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">HDDStatusFlag</span> &#123;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="keyword">enum</span> <span class="title class_">ATTR</span> &#123;</span><br><span class="line"> ERR_OCCURRED = <span class="number">1</span>,</span><br><span class="line"> IDX = <span class="number">1</span> &lt;&lt; <span class="number">1</span>,</span><br><span class="line"> CORR = <span class="number">1</span> &lt;&lt; <span class="number">2</span>,</span><br><span class="line"> DATA_READY = <span class="number">1</span> &lt;&lt; <span class="number">3</span>,</span><br><span class="line"> OVERLAPP_SRVICE_REQUEST = <span class="number">1</span> &lt;&lt; <span class="number">4</span>,</span><br><span class="line"> DRIVE_FAULT_ERR = <span class="number">1</span> &lt;&lt; <span class="number">5</span>,</span><br><span class="line"> DRIVE_READY = <span class="number">1</span> &lt;&lt; <span class="number">6</span>,</span><br><span class="line"> BUSY = <span class="number">1</span> &lt;&lt; <span class="number">7</span></span><br><span class="line"> &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这样,如果想访问枚举类型中的某个常量值,例如<code>BUSY</code>,可以使用<code>HDDStatusFlag::BUSY</code>或是<code>HDDStatusFlag::ATTR:BUSY</code>访问到。</p>
<p>由于<code>0x1F7</code>端口为8位端口所以对于<code>HDDStatusFlag</code>这个枚举类,其实际需要存储的数据为<code>byte</code>类型,因此可以使用一个<code>byte</code>类型的变量作为其私有成员,用来存储实际的枚举值:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> byte _attr;</span><br></pre></td></tr></table></figure>
<p>为了能直接使用整型值给<code>HDDStatusFlag</code>类型的变量赋值,需要定义一个参数为<code>byte</code>类型的构造函数:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">HDDStatusFlag</span>(byte flags) &#123;</span><br><span class="line"> _attr = <span class="built_in">static_cast</span>&lt;byte&gt;(flags);</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
<p>有了这样的一个构造函数,类的实例就可以使用如下的方式构造了:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">HDDStatusFlag flag = HDDStatusFlag::ERR_OCCURRED | HDDStatusFlag::BUSY;</span><br><span class="line"><span class="comment">//or</span></span><br><span class="line">HDDStatusFlag flag = <span class="number">0x81</span>;</span><br></pre></td></tr></table></figure>
<p>至于其他的按位操作、特殊运算和判断函数的实现此处就不再赘述,无外乎就是对运算符进行重载,然后再对实际的值进行对应的运算或判断即可。</p>
<p>在完成了硬盘硬盘状态类的设计后,就可以开始实现硬盘的读写函数了。以硬盘的读取为例,其分为两个主要的步骤:从硬盘读取以及写入内存中的缓冲区。</p>
<p>由于目前在KernelLoader中没有一个完善的内存管理机制而进入内核之后内存将可以通过堆分配器分配空闲的内存供程序使用两者在内存申请上会存在很大的区别。</p>
<p>如果让<code>HDDManager</code>自行管理其在读写时需要用到的缓冲区的话就需要分别为KernelLoader环境和内核环境设计不同的缓冲区申请和释放的机制相当麻烦。考虑到驱动的通用性所以不妨让内存申请的步骤对<code>HDDManager</code>这个类完全不可见,由调用读写函数的调用者主动申请对应大小的内存,并将申请到的内存首地址作为参数传递给读写函数,所以读写函数的签名声明如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">readSector</span><span class="params">(uint idx, uint cnt, byte* dst)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">writeSector</span><span class="params">(uint idx, uint cnt, byte* src)</span></span>;</span><br></pre></td></tr></table></figure>
<p>在读写函数的实现上,可以将多个扇区的读写拆分为多个单独扇区的读写,将单个扇区的读写函数作为私有成员仅供公有读写函数调用。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="type">bool</span> _readOneSector(uint idx, byte* dst);</span><br><span class="line"> <span class="type">bool</span> _writeOneSector(uint idx, byte* src);</span><br></pre></td></tr></table></figure>
<p>以读取的函数为例,单个扇区的读取步骤如下:</p>
<ol>
<li><code>_secCntPort</code>端口写入读取的扇区数量<code>0x01</code></li>
<li><code>_lbaLoPort</code>端口写入读取的扇区号低8位</li>
<li><code>_lbaMidPort</code>端口写入读取的扇区号8至15位</li>
<li><code>_lbaHiPort</code>端口写入读取的扇区号高16至23位</li>
<li><code>_drivePort</code>端口写入读取的扇区号高4位以及驱动的参数<code>0xE0</code></li>
<li><code>_cmdPort</code>端口写入读取命令<code>0x20</code></li>
<li>等待硬盘就绪,从<code>_statusPort</code>中读取硬盘的状态,如果状态包含了<code>HDDStatusFlag::DATA_READY</code>属性,则意味着硬盘数据准备完毕</li>
<li>读取512字节到缓冲区中</li>
</ol>
<p>其中,如果在等待硬盘就绪的步骤中发生了错误,则会使用<code>false</code>返回值表示读取失败。</p>
<p>具体的实现如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">bool</span> HDDManager::_readOneSector(uint idx, byte* dst) &#123;</span><br><span class="line"> _secCntPort.<span class="built_in">write</span>(<span class="number">0x01</span>); <span class="comment">//Read 1 sector</span></span><br><span class="line"> _lbaLoPort.<span class="built_in">write</span>((uint8)idx);</span><br><span class="line"> _lbaMidPort.<span class="built_in">write</span>((uint8)(idx &gt;&gt; <span class="number">8</span>));</span><br><span class="line"> _lbaHiPort.<span class="built_in">write</span>((uint8)(idx &gt;&gt; <span class="number">16</span>));</span><br><span class="line"> _drivePort.<span class="built_in">write</span>(((uint8)(idx &gt;&gt; <span class="number">24</span>)) | <span class="number">0xE0</span>);</span><br><span class="line"> _cmdPort.<span class="built_in">write</span>(<span class="number">0x20</span>);</span><br><span class="line"> <span class="keyword">while</span>(<span class="literal">true</span>) &#123;</span><br><span class="line"> HDDStatusFlag status = _statusPort.<span class="built_in">read</span>();</span><br><span class="line"> <span class="keyword">if</span>(status.<span class="built_in">contains</span>(HDDStatusFlag::ERR_OCCURRED)) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">if</span>(status.<span class="built_in">contains</span>(HDDStatusFlag::DATA_READY)) &#123;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">512</span>; i += <span class="number">2</span>) &#123;</span><br><span class="line"> uint16 tmpDat = _datPort.<span class="built_in">read</span>();</span><br><span class="line"> *(uint16*)(dst + i) = tmpDat;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在读取多扇区的函数中,如果读取某个扇区时<code>_readOneSector</code>函数返回了<code>false</code>值,则会进而读取<code>_errPort</code>中的错误信息写入缓冲区首2字节中并返回<code>false</code>告知主调函数在硬盘读取过程中出现了错误。如果没有遇到错误,则继续下一个扇区的读取,然后向主调函数返回<code>true</code></p>
<p>具体的实现如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">HDDManager::readSector</span><span class="params">(uint idx, uint cnt, byte* dst)</span> </span>&#123;</span><br><span class="line"> uint offset = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span>(uint i = <span class="number">0</span>; i &lt; cnt; i++) &#123;</span><br><span class="line"> <span class="keyword">if</span>(!_readOneSector(idx + i, (byte*)(dst + offset))) &#123;</span><br><span class="line"> uint8 err = _errPort.<span class="built_in">read</span>();</span><br><span class="line"> <span class="built_in">memcpy</span>(dst, &amp;err, <span class="number">8</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> offset += <span class="number">512</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>对于硬盘写,操作则与硬盘读大同小异,仅仅是将数据的传输方向颠倒,从由<code>_datPort</code>读出数据变为向<code>_datPort</code>写入数据,从写入缓冲区变为从缓冲区取出数据而已,此处便不再赘述。</p>
<p>完整的<code>hdd.h</code>头文件如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;types.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;memory.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;ports.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> _HDD_H_</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _HDD_H_</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">HDDStatusFlag</span> &#123;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="keyword">enum</span> <span class="title class_">ATTR</span> &#123;</span><br><span class="line"> ERR_OCCURRED = <span class="number">1</span>,</span><br><span class="line"> IDX = <span class="number">1</span> &lt;&lt; <span class="number">1</span>,</span><br><span class="line"> CORR = <span class="number">1</span> &lt;&lt; <span class="number">2</span>,</span><br><span class="line"> DATA_READY = <span class="number">1</span> &lt;&lt; <span class="number">3</span>,</span><br><span class="line"> OVERLAPP_SRVICE_REQUEST = <span class="number">1</span> &lt;&lt; <span class="number">4</span>,</span><br><span class="line"> DRIVE_FAULT_ERR = <span class="number">1</span> &lt;&lt; <span class="number">5</span>,</span><br><span class="line"> DRIVE_READY = <span class="number">1</span> &lt;&lt; <span class="number">6</span>,</span><br><span class="line"> BUSY = <span class="number">1</span> &lt;&lt; <span class="number">7</span></span><br><span class="line"> &#125;;</span><br><span class="line"> <span class="built_in">HDDStatusFlag</span>() &#123;&#125;</span><br><span class="line"> <span class="built_in">HDDStatusFlag</span>(byte flags) &#123;</span><br><span class="line"> _attr = <span class="built_in">static_cast</span>&lt;byte&gt;(flags);</span><br><span class="line"> &#125;</span><br><span class="line"> HDDStatusFlag&amp; <span class="keyword">operator</span>=(byte flags) &#123;</span><br><span class="line"> _attr = <span class="built_in">static_cast</span>&lt;byte&gt;(flags);</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> HDDStatusFlag&amp; <span class="keyword">operator</span>=(<span class="type">const</span> HDDStatusFlag&amp; other) &#123;</span><br><span class="line"> _attr = other._attr;</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">//Judge whether two flags are equal</span></span><br><span class="line"> <span class="type">bool</span> <span class="keyword">operator</span>==(byte flags) &#123;</span><br><span class="line"> <span class="keyword">return</span> _attr == <span class="built_in">static_cast</span>&lt;byte&gt;(flags);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">//Combine two flags</span></span><br><span class="line"> HDDStatusFlag <span class="keyword">operator</span>+(byte flags) &#123;</span><br><span class="line"> byte newAttr = _attr | <span class="built_in">static_cast</span>&lt;byte&gt;(flags);</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">HDDStatusFlag</span>(<span class="built_in">static_cast</span>&lt;byte&gt;(newAttr));</span><br><span class="line"> &#125;</span><br><span class="line"> HDDStatusFlag&amp; <span class="keyword">operator</span>+=(byte flags) &#123;</span><br><span class="line"> _attr |= <span class="built_in">static_cast</span>&lt;byte&gt;(flags);</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">//Remove flags</span></span><br><span class="line"> HDDStatusFlag <span class="keyword">operator</span>-(byte flags) &#123;</span><br><span class="line"> byte newAttr = _attr &amp; ~(<span class="built_in">static_cast</span>&lt;byte&gt;(flags));</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">HDDStatusFlag</span>(<span class="built_in">static_cast</span>&lt;byte&gt;(newAttr));</span><br><span class="line"> &#125;</span><br><span class="line"> HDDStatusFlag&amp; <span class="keyword">operator</span>-=(byte flags) &#123;</span><br><span class="line"> _attr &amp;= ~(<span class="built_in">static_cast</span>&lt;byte&gt;(flags));</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">//Bitwise or</span></span><br><span class="line"> HDDStatusFlag <span class="keyword">operator</span>|(byte flags) &#123;</span><br><span class="line"> byte newAttr = _attr | <span class="built_in">static_cast</span>&lt;byte&gt;(flags);</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">HDDStatusFlag</span>(<span class="built_in">static_cast</span>&lt;byte&gt;(newAttr));</span><br><span class="line"> &#125;</span><br><span class="line"> HDDStatusFlag&amp; <span class="keyword">operator</span>|=(byte flags) &#123;</span><br><span class="line"> _attr |= <span class="built_in">static_cast</span>&lt;byte&gt;(flags);</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">//Bitwise and</span></span><br><span class="line"> HDDStatusFlag <span class="keyword">operator</span>&amp;(byte flags) &#123;</span><br><span class="line"> byte newAttr = _attr &amp; <span class="built_in">static_cast</span>&lt;byte&gt;(flags);</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">HDDStatusFlag</span>(<span class="built_in">static_cast</span>&lt;byte&gt;(newAttr));</span><br><span class="line"> &#125;</span><br><span class="line"> HDDStatusFlag&amp; <span class="keyword">operator</span>&amp;=(byte flags) &#123;</span><br><span class="line"> _attr &amp;= <span class="built_in">static_cast</span>&lt;byte&gt;(flags);</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">//Reverse flags</span></span><br><span class="line"> HDDStatusFlag <span class="keyword">operator</span>~() &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">HDDStatusFlag</span>(<span class="built_in">static_cast</span>&lt;byte&gt;(_attr));</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">//Toggle flag bits</span></span><br><span class="line"> HDDStatusFlag&amp; <span class="keyword">operator</span>^=(byte flags) &#123;</span><br><span class="line"> _attr ^= <span class="built_in">static_cast</span>&lt;byte&gt;(flags);</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">//Judge whether flags are contained</span></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">contains</span><span class="params">(byte flags)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> (_attr &amp; <span class="built_in">static_cast</span>&lt;byte&gt;(flags)) == <span class="built_in">static_cast</span>&lt;byte&gt;(flags);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">private</span>:</span><br><span class="line"> byte _attr;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">HDDManager</span> &#123;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">HDDManager</span>() &#123;&#125;</span><br><span class="line"> <span class="keyword">private</span>:</span><br><span class="line"> <span class="type">const</span> Port&lt;uint16&gt; _datPort = <span class="number">0x1F0</span>;</span><br><span class="line"> <span class="type">const</span> PortReadOnly&lt;uint8&gt; _errPort = <span class="number">0x1F1</span>;</span><br><span class="line"> <span class="type">const</span> PortWriteOnly&lt;uint8&gt; _funcPort = <span class="number">0x1F1</span>;</span><br><span class="line"> <span class="type">const</span> Port&lt;uint8&gt; _secCntPort = <span class="number">0x1F2</span>;</span><br><span class="line"> <span class="type">const</span> Port&lt;uint8&gt; _lbaLoPort = <span class="number">0x1F3</span>;</span><br><span class="line"> <span class="type">const</span> Port&lt;uint8&gt; _lbaMidPort = <span class="number">0x1F4</span>;</span><br><span class="line"> <span class="type">const</span> Port&lt;uint8&gt; _lbaHiPort = <span class="number">0x1F5</span>;</span><br><span class="line"> <span class="type">const</span> Port&lt;uint8&gt; _drivePort = <span class="number">0x1F6</span>;</span><br><span class="line"> <span class="type">const</span> PortReadOnly&lt;uint8&gt; _statusPort = <span class="number">0x1F7</span>;</span><br><span class="line"> <span class="type">const</span> PortWriteOnly&lt;uint8&gt; _cmdPort = <span class="number">0x1F7</span>;</span><br><span class="line"> <span class="type">bool</span> _readOneSector(uint idx, byte* dst);</span><br><span class="line"> <span class="type">bool</span> _writeOneSector(uint idx, byte* src);</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">readSector</span><span class="params">(uint idx, uint cnt, byte* dst)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">writeSector</span><span class="params">(uint idx, uint cnt, byte* src)</span></span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure>
<p>头文件中的函数定义则都存放在<code>hdd.cpp</code>中,完整的内容如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;hdd.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">bool</span> HDDManager::_readOneSector(uint idx, byte* dst) &#123;</span><br><span class="line"> _secCntPort.<span class="built_in">write</span>(<span class="number">0x01</span>); <span class="comment">//Read 1 sector</span></span><br><span class="line"> _lbaLoPort.<span class="built_in">write</span>((uint8)idx);</span><br><span class="line"> _lbaMidPort.<span class="built_in">write</span>((uint8)(idx &gt;&gt; <span class="number">8</span>));</span><br><span class="line"> _lbaHiPort.<span class="built_in">write</span>((uint8)(idx &gt;&gt; <span class="number">16</span>));</span><br><span class="line"> _drivePort.<span class="built_in">write</span>(((uint8)(idx &gt;&gt; <span class="number">24</span>)) | <span class="number">0xE0</span>);</span><br><span class="line"> _cmdPort.<span class="built_in">write</span>(<span class="number">0x20</span>);</span><br><span class="line"> <span class="keyword">while</span>(<span class="literal">true</span>) &#123;</span><br><span class="line"> HDDStatusFlag status = _statusPort.<span class="built_in">read</span>();</span><br><span class="line"> <span class="keyword">if</span>(status.<span class="built_in">contains</span>(HDDStatusFlag::ERR_OCCURRED)) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">if</span>(status.<span class="built_in">contains</span>(HDDStatusFlag::DATA_READY)) &#123;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">512</span>; i += <span class="number">2</span>) &#123;</span><br><span class="line"> uint16 tmpDat = _datPort.<span class="built_in">read</span>();</span><br><span class="line"> *(uint16*)(dst + i) = tmpDat;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">bool</span> HDDManager::_writeOneSector(uint idx, byte* src) &#123;</span><br><span class="line"> _secCntPort.<span class="built_in">write</span>(<span class="number">0x01</span>);</span><br><span class="line"> _lbaLoPort.<span class="built_in">write</span>((uint8)idx);</span><br><span class="line"> _lbaMidPort.<span class="built_in">write</span>((uint8)(idx &gt;&gt; <span class="number">8</span>));</span><br><span class="line"> _lbaHiPort.<span class="built_in">write</span>((uint8)(idx &gt;&gt; <span class="number">16</span>));</span><br><span class="line"> _drivePort.<span class="built_in">write</span>(((uint8)(idx &gt;&gt; <span class="number">24</span>)) | <span class="number">0xE0</span>);</span><br><span class="line"> _cmdPort.<span class="built_in">write</span>(<span class="number">0x30</span>);</span><br><span class="line"> <span class="keyword">while</span>(<span class="literal">true</span>) &#123;</span><br><span class="line"> HDDStatusFlag status = _statusPort.<span class="built_in">read</span>();</span><br><span class="line"> <span class="keyword">if</span>(status.<span class="built_in">contains</span>(HDDStatusFlag::ERR_OCCURRED)) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">if</span>(status.<span class="built_in">contains</span>(HDDStatusFlag::DATA_READY)) &#123;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">512</span>; i += <span class="number">2</span>) &#123;</span><br><span class="line"> uint16 tempDat = *(uint16*)(src + i);</span><br><span class="line"> _datPort.<span class="built_in">write</span>(tempDat);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">HDDManager::readSector</span><span class="params">(uint idx, uint cnt, byte* dst)</span> </span>&#123;</span><br><span class="line"> uint offset = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span>(uint i = <span class="number">0</span>; i &lt; cnt; i++) &#123;</span><br><span class="line"> <span class="keyword">if</span>(!_readOneSector(idx + i, (byte*)(dst + offset))) &#123;</span><br><span class="line"> uint8 err = _errPort.<span class="built_in">read</span>();</span><br><span class="line"> <span class="built_in">memcpy</span>(dst, &amp;err, <span class="number">8</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> offset += <span class="number">512</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">HDDManager::writeSector</span><span class="params">(uint idx, uint cnt, byte* src)</span> </span>&#123;</span><br><span class="line"> uint offset = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span>(uint i = <span class="number">0</span>; i &lt; cnt; i++) &#123;</span><br><span class="line"> <span class="keyword">if</span>(!_writeOneSector(idx + i, (byte*)(src + offset))) &#123;</span><br><span class="line"> uint8 err = _errPort.<span class="built_in">read</span>();</span><br><span class="line"> <span class="built_in">memcpy</span>(src, &amp;err, <span class="number">8</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> offset += <span class="number">512</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>现在如果想要从硬盘中读取某个或是某几个扇区例如读取5~12扇区则可以调用<code>HDDManager</code>中的<code>readSectors</code>函数如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Request buffer</span></span><br><span class="line">byte buffer[<span class="number">512</span> * <span class="number">8</span>];</span><br><span class="line"><span class="built_in">memset</span>(buffer, <span class="number">0</span>, <span class="number">512</span> * <span class="number">8</span>);</span><br><span class="line"><span class="comment">//Read sectors</span></span><br><span class="line"><span class="built_in">HDDManager</span>().<span class="built_in">readSectors</span>(<span class="number">5</span>, <span class="number">8</span>, buffer);</span><br></pre></td></tr></table></figure>
<p>可以看到,代码明显比原先使用汇编时更加地简洁,也更易于阅读,这也就是编写驱动的目的。</p>
<h2 id="抽象FAT文件系统">抽象FAT文件系统</h2>
<p>在BootLoader开启了保护模式之后所有的一切操作包括上一小节中编写的驱动都是在为了内核的加载而铺路。</p>
<div class="note info flat"><p><strong>加载:</strong><br>
这里及后文中说的加载,都包括了将内核编译后的输出文件<strong>读入内存</strong>中以及将内核的各个部分<strong>放置在正确的物理地址处</strong>这两个步骤</p>
</div>
<p>加载这一个环节又自然离不开将内核编译后的可执行代码从硬盘读入内存的步骤</p>
<p>在学校教程的实现中,内核被编译为裸二进制文件,也就是<code>.bin</code>文件像MBR和BootLoader那样直接写入硬盘对应的扇区。在读取的时候由于写入的起始扇区和扇区数量都已知所以可以直接从硬盘读取对应的扇区到内存中。由于内核已经被编译为裸二进制文件只需要将整个文件读取到对应的地址处就完成了内核的加载操作而不需要额外的操作。</p>
<p>固然使用这个方式是简单而且方便的但我始终觉得这样不够优雅。于是我简单地了解了Linux中一个叫做grub的启动引导程序简单来讲grub的启动分为了三个阶段</p>
<ol>
<li>第一个阶段 <em>(stage 1)</em> 就基本相当于我们所编写的MBR部分其负责从硬盘上加载中间阶段 <em>(stage 1.5)</em> 的代码的首扇区也就相当于我们的BootLoader</li>
<li>这个中间阶段 <em>(stage 1.5)</em> 首扇区代码负责将剩余代码载入内存,之后剩余代码会找到活动分区下<code>/boot/grub/</code>目录中的启动程序,也即第二阶段程序 <em>(stage 2)</em>,然后加载到内存运行</li>
<li>在第二阶段 <em>(stage 2)</em> 中,程序会解析<code>/boot/grub/grub.cfg</code>文件,显示一个启动菜单给用户,并且负责加载对应系统中的内核运行</li>
</ol>
<div class="note blue icon-padding flat"><i class="note-icon fas fa-external-link-square"></i><p><strong>GNU GRUB</strong><br>
关于grub的更多信息可以查看<a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/GNU_GRUB#Startup_on_systems_using_BIOS_firmware">Wikipedia上关于grub的页面</a></p>
</div>
<p>由于grub在中间阶段就引入了对文件系统的加载从而可以从分区目录中读取需要的文件看起来就比直接读取裸二进制文件要优雅得多了。因此我决定放弃学校教程的思路转而在KernelLoader中实现一个简单版的grub程序其可以从分区的<code>/system/</code>目录下找到内核文件并加载。</p>
<p>为了能够在KernelLoader中读取分区中的目录和文件信息就必须要让代码能够解析分区上的文件系统由于FAT是一个成熟且简单的文件系统所以在实验中硬盘采用了FAT格式进行格式化。</p>
<p>尽管文件系统中存储的文件不尽相同,但对于同一个格式的文件系统,它们又都有着相同之处,例如文件系统包含了哪些结构,文件在文件系统中是怎样被组织的等等,这些不同中的相同之处就是我们解析文件系统的关键之处。</p>
<p>我们现在所需要做的就是<strong>用代码表示出这些相同的部分,并探究这些相同部分中的信息如何给文件的读取以提示</strong>,如何帮助我们了解文件系统中的内容。在下一章中,我们会更进一步,了解如何通过这部分信息从文件系统中读取需要的文件。</p>
<div class="note info flat"><p><strong>抽象:</strong><br>
通过代码去表现共同点,并用接口从共同点中提取出需要的信息,从而能够去解析那些特异的数据,这其实就是抽象的过程。</p>
</div>
<h3 id="简述文件系统与FAT">简述文件系统与FAT</h3>
<div class="note blue icon-padding flat"><i class="note-icon fas fa-external-link-square"></i><p><strong>参考资料</strong><br>
如果想要了解关于文件系统的更为权威、详细的信息,可以阅读课本《操作系统概念》中第四部分关于文件系统的内容,我强烈建议先完成这部分的阅读再继续后面的实验。<br>
除此之外,<a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV1oZ4y1t7ce">Bilibili上南京大学操作系统课程中关于FAT和UNIX文件系统知识的讲解</a>是不错的视频资料其前半部分讲解了文件系统的原理以及FAT的一些基础知识。</p>
</div>
<p>文件系统是对文件的一种组织形式,所以文件固然也就是文件系统中至关重要的部分,了解文件系统首先就要了解文件的构成。</p>
<p>不论是哪种类型的文件,不论是<code>.doc</code><code>.ppt</code>还是<code>.txt</code>抑或是<code>.bin</code>,在硬盘的视角来看,它们中的数据实际上都是一系列二进制的数据块,而类别的区别只在于这些数据内部的组织形式,并不会让它们看起来是除了二进制数据块以外的其他样子。</p>
<p>但是如果一个文件只有数据块又不太够,<strong>文件名称</strong>是用户用以区分不同文件的关键信息,文件名称有时候对于文件系统来说太复杂了,所以文件系统会用一些特殊的方式记录用户提供的文件名称并通过其来识别不同的文件,这种记录有时候会呈现以<strong>标识符</strong>的形式在FAT文件系统中则是一系列字符串。</p>
<p>除此之外,文件还需要一些其他的信息,例如其类型、数据在硬盘上的位置、尺寸、访问权限、创建或修改日期等等。</p>
<p>可见,文件由两个关键的部分组成,一个是文件的数据,也就是一系列二进制数据块,另一个则是文件的信息,它用来区分不同的文件并在硬盘上定位到文件数据的位置。</p>
<p><img src="/img/os-journal-vol-4/IMG_20220811-112229643.png" alt="图 1"></p>
<p>在文件系统组织文件的时候,其实际上就是在建立索引文件的数据结构,由于文件是由两部分构成的,文件系统中也就存在着两种主要的数据结构,分别用来索引文件的这两个组成部分。</p>
<p>其中一个数据结构用来索引文件的信息,这部分数据结构一般呈现树形的结构,文件系统中的文件目录就是由这个数据结构所维护的,树中每个节点都存储着一个文件或是目录的信息。</p>
<p><img src="/img/os-journal-vol-4/IMG_20220811-144251284.png" alt="图 3"></p>
<p>另一个数据结构则用于通过文件的信息来索引文件的数据,由于一个文件可能跨越数个甚至数十、数百个扇区,由于文件时时刻刻都可能会被更改,所以没有办法预先给文件分配一段固定的、连续的、大小适当的空间,因此文件的数据往往分散在磁盘的各个位置,这时就需要一个数据结构来将这些游离的数据块串接起来。说到这里,可能大家就已经能猜到,这部分数据结构一般以链表的形式出现。每一个文件块都对应着一个下一文件块的位置信息,而第一个文件块的位置信息则存储于文件的信息中。</p>
<p>下图表示了一个文件系统磁盘中可能的样子:</p>
<ul>
<li><code>#0</code>标号的内存空间对应着系统的根目录<code>/</code>节点,其内部有两个有效的文件信息,分别是<code>system</code><code>home</code>,均为目录类别。</li>
<li><code>system</code>首文件块指向<code>#8</code>,并且<code>#8</code>为其最后一个文件块,因此<code>#8</code>实际上就对应着<code>/system/</code>节点,其中存储了<code>kernel.elf</code>一个文件的信息。</li>
<li>对于<code>home</code>,首文件块指向<code>#14</code>,其下一个文件块为<code>#16</code>,并且是最后一个文件块,因此<code>#14,#16</code>共同构成<code>/home/</code>节点,在这个节点中存储了两个有效文件信息,分别是<code>boo.baz</code><code>bar</code>,分别是文件和目录。</li>
<li><code>kernel.elf</code>首文件块为<code>#4</code><code>#4</code>下一个文件块为<code>#6</code><code>#6</code>为最后一个文件块,因此<code>kernel.elf</code>对应的文件数据为<code>#4,#6</code>上的数据。</li>
<li><code>boo.baz</code>首文件块为<code>#10</code>,后续文件块依次为<code>#12</code><code>#5</code>。因此<code>#10,#12,#5</code>共同构成<code>boo.baz</code>文件的数据</li>
<li><code>bar</code>首文件块指向<code>#18</code>,后续文件块为<code>#11</code>,因此<code>#18,#11</code>共同构成了目录树中的<code>/home/bar/</code>节点的子树信息</li>
</ul>
<p><img src="/img/os-journal-vol-4/IMG_20220811-140255236.png" alt="图 2"></p>
<p>由上图可以看出,目录树的遍历方式是要与数据结构中树的遍历方式有很大不同的。每一个节点实际上都对应着一个<strong>存储着其子节点的文件的信息的数组</strong>,通过这个数组,可以获得子节点对应文件的首块信息,再依据链表就能够获得全部的文件数据。只有当取得了全部的文件数据,才可以进入下一层的节点。</p>
<p>简单来说,就是在<code>/</code>目录下,我们只能得到<code>system/</code><code>home/</code>的信息,但是我们不能从这些信息中直接得到<code>system/</code>下或是<code>home/</code>下有什么文件,除非我们把它们的数据完整地读取出来。</p>
<p>而在一般的数据结构中,对于根节点<code>root</code>,我们只需要访问<code>child = root-&gt;children[0]</code>就可以获得整个子节点,得到的<code>child</code>就包含了其所有的子节点数据,而不需要额外的加载操作。</p>
<p>现在,假设我们需要访问<code>/system/kernel.elf</code>文件,那么就需要</p>
<ol>
<li>首先从树的根部开始,找到类型为目录,名称为<code>system</code>的信息</li>
<li>从信息中读取出目录数据的第一个块<code>#8</code>,并根据链表中的信息读入整个目录,由于这里只有一个块,所以就读入<code>#8</code></li>
<li>在目录的数据中找到类型为文件,名称为<code>kernel.elf</code>的信息</li>
<li>从信息中读出文件数据的第一个块<code>#4</code>,并根据链表读入<code>#6</code>,得到整个文件。</li>
</ol>
<p>不过,为了提高文件读写的速度,系统一般会维护一个打开的文件表 <em>(Open-file table)</em> 用于维护所有打开的文件的信息,所以一般来说,当打开一个文件的时候,除了会将它的全部数据就被读取到内存中,还会将其信息添加到打开文件表中,后续所有的增删改都只会对内存中的数据起作用,直到文件关闭时才再次写回磁盘。这样就避免了每次读写过程中对文件数据的定位以及读取。</p>
<div class="note info flat"><p><strong>好戏还在后面</strong><br>
关于操作系统如何维护打开文件表,以及更多更为复杂的操作,将会留到内核中实现</p>
</div>
<p>不过,上面只是对文件系统的一种简单的概述,不同的文件系统会有不同的组织文件的方式,但核心总还是离不开怎样组织文件的数据以及如何通过文件的信息索引到文件的数据,区别大抵是数据结构的差异以及具体实现上的差异。</p>
<p>例如在上世纪出现的FAT中由于软盘随机读取的性能奇差所以如果像上文中图片绘制的那样在文件数据块的末尾添加上下一个文件块的信息糟糕的问题就会出现如果尝试在文件尾部添加些什么那需要遍历之前所有的文件块才能知道最后一个文件块在哪个位置</p>
<p>为此,工程师们将所有文件块中关于下一个块的信息独立出来,存放在磁盘中的一个连续的空间内,这个空间也被称作文件分配表 <em>(File Allocation Table)</em>也就是FAT实际的意义。这样只需要提前把这一部分空间读取到内存中就可以很快地定位到文件任意个块的信息了。</p>
<p><img src="/img/os-journal-vol-4/IMG_20220812-101149499.png" alt="图 1"></p>
<p>同时这样做还有一个好处一个512字节或是其整数倍的块需要分配几个字节给下一个块的块号导致每个文件块实际数据大小不是2的幂现在由于将这部分信息独立出来所以每一个文件块都是完整的512字节或是512字节的倍数。</p>
<p>但是如果每个数据块只占据1个扇区的话对于大磁盘来说需要的块号还是太多了于是工程师就提出了簇 <em>(cluster)</em> 的概念,一簇由许多个扇区组成,而这个簇就相当于前文中的数据块。</p>
<p>这样在FAT中存储簇号就大大减少了FAT的大小虽然增加了碎片但是提升了文件读写的效率。</p>
<div class="note info flat"><p><strong>碎片:</strong><br>
当一个簇很大的时候例如4KiB如果一个文件的大小为4KiB + 1B则仍然需要为其分配8KiB的空间也就是两簇。这就使得第二簇中几乎所有的空间都被浪费了这部分空间就被成为碎片</p>
</div>
<h3 id="第一篇文档">第一篇文档</h3>
<p>在了解了文件系统是怎样组织和存储文件之后,对于如何从文件系统中读取文件应该有了思路。</p>
<p>但是细细一想又会发现很多问题,例如:根目录存储在磁盘的哪个位置、如何知道一个簇有多大、文件名是如何存储的、怎样才代表当前的簇是文件的最后一个簇等等</p>
<p>这便是因为文件系统很多具体的细节还没有落实,程序还无法从现有的文件系统中获得需要的数据。而这就是本节的目的,通过阅读<a href="/img/os-journal-vol-4/fat_specification.pdf">微软关于FAT文件系统的规范文档</a>对FAT文件系统进行抽象并且提供接口来解决上面提到的种种问题。</p>
<div class="note blue icon-padding flat"><i class="note-icon fas fa-download"></i><p><strong>下载FAT规范文档</strong><br>
可以从<a href="/assets/os-journal-vol-4/fat_specification.pdf">我的服务器</a>上下载我做过简单批注的版本<br>
也可以通过<a target="_blank" rel="noopener" href="https://academy.cba.mit.edu/classes/networking_communications/SD/FAT.pdf">原链接</a>下载官方版本</p>
</div>
<h4 id="定义与注解">定义与注解</h4>
<p>第一章大体可以略过,其中较为重要的概念如下:</p>
<ul>
<li><strong>簇 (cluster)</strong> 文件分配的单元 <em>(A unit of allocation)</em>其中包括了一组逻辑连续的扇区。卷内的每个簇都可以通过一个簇号N指代 <em>(referred to)</em>。给任何一个文件所分配的全部空间的大小 *(All allocation for a file)*必须是簇的整数倍。</li>
<li><strong>分区 (partition)</strong> 在一个卷内的一系列扇区</li>
<li><strong>卷 (volume)</strong> 一个逻辑上的连续的扇区地址空间</li>
</ul>
<h4 id="卷结构">卷结构</h4>
<p>第二章主要介绍了FAT格式卷的结构除了上文中提到的用来存储下一个簇信息的FAT区域 <em>(FAT Region)</em> 以及存储文件数据的区域 <em>(File and Directory Data Region)</em> ,还包括了一个保留区 <em>(Reserved Region)</em> 和一个根目录区 <em>(Root Directory Region)</em></p>
<div class="note info flat"><p><strong>根目录区在FAT32格式的卷上不存在</strong></p>
</div>
<p>一个FAT格式的卷结构大概长得就像下面这样</p>
<p><img src="/img/os-journal-vol-4/IMG_20220811-164836695.png" alt="图 4"></p>
<div class="note red icon-padding flat"><i class="note-icon fas fa-exclamation-circle"></i><p><strong>陷阱:小端模式</strong><br>
由于所有的FAT文件系统都是为IBM PC机器的架构所设计因此其数据结构在硬盘上的存储方式都是<strong>小端模式</strong></p>
</div>
<h4 id="BPB数据结构">BPB数据结构</h4>
<p>在FAT格式的卷的首扇区有一个叫做BPB <em>(Bios Parameter Block)</em> 的数据结构其主要存储了关于当前卷上FAT文件系统的关键信息包括了文件系统各个区域的首扇区号以及簇大小等等。</p>
<div class="note info flat"><p><strong>注:</strong><br>
首扇区并不是所有的内容都属于BPB结构体的范畴在后续的介绍中<em>BPB_</em> 开头的域才是属于BPB结构体的域<br>
<em>In the following description, all the fields whose names start with BPB_ are part of the BPB. All the fields whose names start with BS_ are part of the boot sector and not really part of the BPB.</em></p>
</div>
<p>对于FAT12/16和FAT32它们的BPB结构在首36字节上完全一致</p>
<table>
<thead>
<tr>
<th style="text-align:center">域名称</th>
<th style="text-align:center">偏移</th>
<th style="text-align:center">大小</th>
<th style="text-align:center">描述</th>
<th style="text-align:center">限制</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">BS_jmpBoot</td>
<td style="text-align:center">0</td>
<td style="text-align:center">3</td>
<td style="text-align:center">跳转到启动代码处执行的指令<br/>由于实验中启动代码位于MBR不会从这里进行启动因此可以不用关心这个域实际的内容</td>
<td style="text-align:center">一般为<code>0x90**EB</code>或是<code>0x****E9</code></td>
</tr>
<tr>
<td style="text-align:center">BS_OEMName</td>
<td style="text-align:center">3</td>
<td style="text-align:center">8</td>
<td style="text-align:center">OEM厂商的名称同样与实验无关不需要关心</td>
<td style="text-align:center">-</td>
</tr>
<tr>
<td style="text-align:center"><strong>BPB_BytsPerSec</strong></td>
<td style="text-align:center">11</td>
<td style="text-align:center">2</td>
<td style="text-align:center"><strong>每个扇区的字节数</strong></td>
<td style="text-align:center"><strong>只能是512、1024、2048或4096</strong></td>
</tr>
<tr>
<td style="text-align:center"><strong>BPB_SecPerClus</strong></td>
<td style="text-align:center">13</td>
<td style="text-align:center">1</td>
<td style="text-align:center"><strong>每个簇的扇区数量</strong></td>
<td style="text-align:center"><strong>只能是1、2、4、8、16、32、64和128</strong></td>
</tr>
<tr>
<td style="text-align:center"><strong>BPB_RsvdSecCnt</strong></td>
<td style="text-align:center">14</td>
<td style="text-align:center">2</td>
<td style="text-align:center"><strong>保留区域的扇区数量</strong><br/>可以用来计算FAT区域的首扇区位置</td>
<td style="text-align:center"><strong>不能为0可以为任意非0值</strong><br/>可以用来将将数据区域与簇大小对齐(使数据区域的起始偏移位于簇大小的整数倍处)</td>
</tr>
<tr>
<td style="text-align:center"><strong>BPB_NumFATs</strong></td>
<td style="text-align:center">16</td>
<td style="text-align:center">1</td>
<td style="text-align:center"><strong>FAT表数量</strong></td>
<td style="text-align:center">一般为2也可以为1</td>
</tr>
<tr>
<td style="text-align:center"><strong>BPB_RootEntCnt</strong></td>
<td style="text-align:center">17</td>
<td style="text-align:center">2</td>
<td style="text-align:center"><strong>根目录中的条目数</strong><br/>指根目录中包含的所有的条目数量,包括有效的、空的和无效的条目<br/>可以用来计算根目录区所占用的字节数</td>
<td style="text-align:center"><strong>FAT32</strong> 必须为0<br/><strong>FAT12/16</strong> 必须满足<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>B</mi><mi>P</mi><mi>B</mi><mi mathvariant="normal">_</mi><mi>R</mi><mi>o</mi><mi>o</mi><mi>t</mi><mi>E</mi><mi>n</mi><mi>t</mi><mi>C</mi><mi>n</mi><mi>t</mi><mo>×</mo><mn>32</mn><mo stretchy="false">)</mo><mo></mo><mn>0</mn><mtext></mtext><mo stretchy="false">(</mo><mi>m</mi><mi>o</mi><mi>d</mi><mtext></mtext><mn>2</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(BPB\_RootEntCnt \times 32) \equiv 0\;(mod\;2)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.06em;vertical-align:-0.31em;"></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.05017em;">BPB</span><span class="mord" style="margin-right:0.02778em;">_</span><span class="mord mathnormal" style="margin-right:0.00773em;">R</span><span class="mord mathnormal">oo</span><span class="mord mathnormal" style="margin-right:0.05764em;">tE</span><span class="mord mathnormal">n</span><span class="mord mathnormal" style="margin-right:0.07153em;">tC</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">32</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel"></span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">0</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mopen">(</span><span class="mord mathnormal">m</span><span class="mord mathnormal">o</span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord">2</span><span class="mclose">)</span></span></span></span></td>
</tr>
<tr>
<td style="text-align:center"><strong>BPB_TotSec16</strong></td>
<td style="text-align:center">19</td>
<td style="text-align:center">2</td>
<td style="text-align:center"><strong>16位长度卷的总扇区数</strong><br/>对于FAT32和更大容量的存储设备有额外的BPB_TotSec32域<br/>应当是为了维持BPB结构的一致性而仍然保留了这个域</td>
<td style="text-align:center"><strong>FAT32</strong> 必须位0<br/><strong>FAT12/16</strong> 如果总扇区数小于0x10000也就是能用16位表示则使用此域表示否则也使用BPB_TotSec32域</td>
</tr>
<tr>
<td style="text-align:center">BPB_Media</td>
<td style="text-align:center">21</td>
<td style="text-align:center">1</td>
<td style="text-align:center">似乎是设备的类型<br/>与实验无关,所以可以不用特别关心</td>
<td style="text-align:center">合法取值包括<code>0xF0</code><code>0xF8</code><code>0xF9</code><code>0xFA</code><code>0xFB</code><code>0xFC</code><code>0xFD</code><code>0xFE</code><code>0xFF</code><br/>本地磁盘(不可移动)的规定值为<code>0xF8</code><br/>可移动磁盘的往往使用<code>0xF0</code></td>
</tr>
<tr>
<td style="text-align:center"><strong>BPB_FATSz16</strong></td>
<td style="text-align:center">22</td>
<td style="text-align:center">2</td>
<td style="text-align:center"><strong>单个FAT表占用的扇区数</strong><br/>只用于FAT12/16格式的文件系统</td>
<td style="text-align:center"><strong>FAT32</strong> 必须为0<br/><strong>FAT12/16</strong> 正整数值</td>
</tr>
<tr>
<td style="text-align:center">BPB_SecPerTrk</td>
<td style="text-align:center">24</td>
<td style="text-align:center">2</td>
<td style="text-align:center">每个扇区的磁道数<br/><code>0x13</code>中断相关<br/>只与具有物理结构(如磁道、磁盘等)并且对<code>0x13</code>中断可见的存储介质有关<br/>与实验无关,可以不用关心</td>
<td style="text-align:center">-</td>
</tr>
<tr>
<td style="text-align:center">BPB_NumHeads</td>
<td style="text-align:center">26</td>
<td style="text-align:center">2</td>
<td style="text-align:center">磁头数量<br/>同样与<code>0x13</code>中断相关,实验不会使用,所以可以不用关心</td>
<td style="text-align:center">-</td>
</tr>
<tr>
<td style="text-align:center"><strong>BPB_HiddSec</strong></td>
<td style="text-align:center">28</td>
<td style="text-align:center">4</td>
<td style="text-align:center"><strong>分区前隐藏的扇区数</strong><br/>在文档中描述这个域为同样只与对<code>0x13</code>中断可见的存储介质有关,但在实验过程中发现对于一个多分区的磁盘,这个域对应了<strong>分区首扇区在整个磁盘中的扇区号</strong>例如首扇区位于磁盘2048扇区从0开始计算分区号的分区其BPB_HiddSec域值就为2048</td>
<td style="text-align:center">-</td>
</tr>
<tr>
<td style="text-align:center"><strong>BPB_TotSec32</strong></td>
<td style="text-align:center">32</td>
<td style="text-align:center">4</td>
<td style="text-align:center"><strong>32位长度卷的总扇区数</strong><br/>用来描述FAT32卷中的总扇区数或是扇区数多于0x10000的FAT12/16卷中的总扇区数</td>
<td style="text-align:center"><strong>FAT32</strong> 必须为非零整数值<br/><strong>FAT12/16</strong> 如果扇区数大于0x10000则为扇区数否则必须为0</td>
</tr>
</tbody>
</table>
<p>从第37字节开始FAT12和FAT16卷上的BPB结构如下</p>
<table>
<thead>
<tr>
<th style="text-align:center">域名称</th>
<th style="text-align:center">偏移</th>
<th style="text-align:center">大小</th>
<th style="text-align:center">描述</th>
<th style="text-align:center">限制</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">BS_DrvNum</td>
<td style="text-align:center">36</td>
<td style="text-align:center">1</td>
<td style="text-align:center">用于<code>0x13</code>中断的驱动器号,可以不用关心</td>
<td style="text-align:center">应当设置为<code>0x80</code>或是<code>0x00</code></td>
</tr>
<tr>
<td style="text-align:center">BS_Reserved1</td>
<td style="text-align:center">37</td>
<td style="text-align:center">1</td>
<td style="text-align:center">保留位</td>
<td style="text-align:center">必须为0</td>
</tr>
<tr>
<td style="text-align:center">BS_BootSig</td>
<td style="text-align:center">38</td>
<td style="text-align:center">1</td>
<td style="text-align:center">用来检验启动扇区的完整性的签名,可以不用关心</td>
<td style="text-align:center">如果BS_VolID、BS_VolLab和BS_FilSysType三个域都存在有效的值 <em>(present)</em>,则置为<code>0x29</code></td>
</tr>
<tr>
<td style="text-align:center">BS_VolID</td>
<td style="text-align:center">39</td>
<td style="text-align:center">4</td>
<td style="text-align:center">卷的序列号,可以不用关心</td>
<td style="text-align:center">-</td>
</tr>
<tr>
<td style="text-align:center">BS_VolLab</td>
<td style="text-align:center">43</td>
<td style="text-align:center">11</td>
<td style="text-align:center">卷标,可以不用关心<br/>在文档中,要求与根目录下的卷标描述文件保持内容一致,但实际上在测试中往往卷标描述文件中存储的是真实的卷标而这个域的内容仍为缺省值&quot;No NAME&quot;</td>
<td style="text-align:center">缺省值为&quot;NO NAME&quot;</td>
</tr>
<tr>
<td style="text-align:center">BS_FilSysType</td>
<td style="text-align:center">54</td>
<td style="text-align:center">8</td>
<td style="text-align:center">用来描述文件系统类型,但<strong>不能用来作为判断文件系统类型的依据</strong></td>
<td style="text-align:center">“FAT12”、“FAT16&quot;或是&quot;FAT32”</td>
</tr>
<tr>
<td style="text-align:center">-</td>
<td style="text-align:center">62</td>
<td style="text-align:center">448</td>
<td style="text-align:center">空余,置零</td>
<td style="text-align:center">必须为0</td>
</tr>
<tr>
<td style="text-align:center">Signature_word</td>
<td style="text-align:center">510</td>
<td style="text-align:center">2</td>
<td style="text-align:center">校验位</td>
<td style="text-align:center">设置为<code>0xAA55</code></td>
</tr>
<tr>
<td style="text-align:center">-</td>
<td style="text-align:center">512</td>
<td style="text-align:center">*</td>
<td style="text-align:center">如果<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi><mi>P</mi><mi>B</mi><mi mathvariant="normal">_</mi><mi>B</mi><mi>y</mi><mi>t</mi><mi>s</mi><mi>P</mi><mi>e</mi><mi>r</mi><mi>S</mi><mi>e</mi><mi>c</mi><mo>&gt;</mo><mn>512</mn></mrow><annotation encoding="application/x-tex">BPB\_BytsPerSec \gt 512</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9933em;vertical-align:-0.31em;"></span><span class="mord mathnormal" style="margin-right:0.05017em;">BPB</span><span class="mord" style="margin-right:0.02778em;">_</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="mord mathnormal">t</span><span class="mord mathnormal">s</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal" style="margin-right:0.02778em;">er</span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mord mathnormal">ec</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">512</span></span></span></span>则存在此域,全部置零</td>
<td style="text-align:center">必须为0</td>
</tr>
</tbody>
</table>
<p>相对的从37字节开始FAT32文件系统中BPB的结构如下</p>
<table>
<thead>
<tr>
<th style="text-align:center">域名称</th>
<th style="text-align:center">偏移</th>
<th style="text-align:center">大小</th>
<th style="text-align:center">描述</th>
<th style="text-align:center">限制</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><strong>BPB_FATSz32</strong></td>
<td style="text-align:center">36</td>
<td style="text-align:center">4</td>
<td style="text-align:center"><strong>单个FAT表占用的扇区数</strong><br/>只用于FAT32格式的文件系统</td>
<td style="text-align:center">非负整数值</td>
</tr>
<tr>
<td style="text-align:center"><strong>BPB_ExtFlags</strong></td>
<td style="text-align:center">40</td>
<td style="text-align:center">2</td>
<td style="text-align:center">标志位</td>
<td style="text-align:center"><code>[0:3]</code> 活动FAT表的标号按照从零开始计数<br/><code>[4:6]</code>保留位<br/><code>[7]</code>当FAT在运行时会自动镜像写入其他FAT表时置零否则对于只有一个活动的FAT表时置位</td>
</tr>
<tr>
<td style="text-align:center">BPB_FSVer</td>
<td style="text-align:center">42</td>
<td style="text-align:center">2</td>
<td style="text-align:center">奇怪的版本号域,文档中写了半天描述最后要求置零…</td>
<td style="text-align:center">必须为0</td>
</tr>
<tr>
<td style="text-align:center"><strong>BPB_RootClus</strong></td>
<td style="text-align:center">44</td>
<td style="text-align:center">4</td>
<td style="text-align:center"><strong>根目录的首簇簇号</strong></td>
<td style="text-align:center"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi><mi>P</mi><mi>B</mi><mi mathvariant="normal">_</mi><mi>R</mi><mi>o</mi><mi>o</mi><mi>t</mi><mi>C</mi><mi>l</mi><mi>u</mi><mi>s</mi><mo>&gt;</mo><mn>2</mn></mrow><annotation encoding="application/x-tex">BPB\_RootClus \gt 2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0044em;vertical-align:-0.31em;"></span><span class="mord mathnormal" style="margin-right:0.05017em;">BPB</span><span class="mord" style="margin-right:0.02778em;">_</span><span class="mord mathnormal" style="margin-right:0.00773em;">R</span><span class="mord mathnormal">oo</span><span class="mord mathnormal" style="margin-right:0.01968em;">tCl</span><span class="mord mathnormal">u</span><span class="mord mathnormal">s</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">2</span></span></span></span></td>
</tr>
<tr>
<td style="text-align:center"><strong>BPB_FSInfo</strong></td>
<td style="text-align:center">48</td>
<td style="text-align:center">2</td>
<td style="text-align:center"><strong><code>FSInfo</code>结构体所在的首扇区号</strong></td>
<td style="text-align:center">一般为1</td>
</tr>
<tr>
<td style="text-align:center">BPB_BkBootSec</td>
<td style="text-align:center">50</td>
<td style="text-align:center">2</td>
<td style="text-align:center">备份启动扇区的扇区号<br/>由于现在的硬盘不像当年软盘那样易失,所以关于备份相关的域实际都可以不用关心,因为用不上</td>
<td style="text-align:center">设置为<code>0</code><code>6</code></td>
</tr>
<tr>
<td style="text-align:center">BPB_Reserved</td>
<td style="text-align:center">52</td>
<td style="text-align:center">12</td>
<td style="text-align:center">保留位</td>
<td style="text-align:center">必须为0</td>
</tr>
<tr>
<td style="text-align:center">BS_DrvNum</td>
<td style="text-align:center">64</td>
<td style="text-align:center">1</td>
<td style="text-align:center">用于<code>0x13</code>中断的驱动器号,可以不用关心</td>
<td style="text-align:center">应当设置为<code>0x80</code>或是<code>0x00</code></td>
</tr>
<tr>
<td style="text-align:center">BS_Reserved1</td>
<td style="text-align:center">65</td>
<td style="text-align:center">1</td>
<td style="text-align:center">保留位</td>
<td style="text-align:center">必须为0</td>
</tr>
<tr>
<td style="text-align:center">BS_BootSig</td>
<td style="text-align:center">66</td>
<td style="text-align:center">1</td>
<td style="text-align:center">用来检验启动扇区的完整性的签名,可以不用关心</td>
<td style="text-align:center">如果BS_VolID、BS_VolLab和BS_FilSysType三个域都存在有效的值 <em>(present)</em>,则置为<code>0x29</code></td>
</tr>
<tr>
<td style="text-align:center">BS_VolID</td>
<td style="text-align:center">67</td>
<td style="text-align:center">4</td>
<td style="text-align:center">卷的序列号,可以不用关心</td>
<td style="text-align:center">-</td>
</tr>
<tr>
<td style="text-align:center">BS_VolLab</td>
<td style="text-align:center">71</td>
<td style="text-align:center">11</td>
<td style="text-align:center">卷标,可以不用关心<br/>在文档中,要求与根目录下的卷标描述文件保持内容一致,但实际上在测试中往往卷标描述文件中存储的是真实的卷标而这个域的内容仍为缺省值&quot;No NAME&quot;</td>
<td style="text-align:center">缺省值为&quot;NO NAME&quot;</td>
</tr>
<tr>
<td style="text-align:center">BS_FilSysType</td>
<td style="text-align:center">82</td>
<td style="text-align:center">8</td>
<td style="text-align:center">用来描述文件系统类型,但<strong>不能用来作为判断文件系统类型的依据</strong></td>
<td style="text-align:center">“FAT12”、“FAT16&quot;或是&quot;FAT32”</td>
</tr>
<tr>
<td style="text-align:center">-</td>
<td style="text-align:center">90</td>
<td style="text-align:center">420</td>
<td style="text-align:center">空余,置零</td>
<td style="text-align:center">必须为0</td>
</tr>
<tr>
<td style="text-align:center">Signature_word</td>
<td style="text-align:center">510</td>
<td style="text-align:center">2</td>
<td style="text-align:center">校验位</td>
<td style="text-align:center">设置为<code>0xAA55</code></td>
</tr>
<tr>
<td style="text-align:center">-</td>
<td style="text-align:center">512</td>
<td style="text-align:center">*</td>
<td style="text-align:center">如果<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi><mi>P</mi><mi>B</mi><mi mathvariant="normal">_</mi><mi>B</mi><mi>y</mi><mi>t</mi><mi>s</mi><mi>P</mi><mi>e</mi><mi>r</mi><mi>S</mi><mi>e</mi><mi>c</mi><mo>&gt;</mo><mn>512</mn></mrow><annotation encoding="application/x-tex">BPB\_BytsPerSec \gt 512</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9933em;vertical-align:-0.31em;"></span><span class="mord mathnormal" style="margin-right:0.05017em;">BPB</span><span class="mord" style="margin-right:0.02778em;">_</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="mord mathnormal">t</span><span class="mord mathnormal">s</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal" style="margin-right:0.02778em;">er</span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mord mathnormal">ec</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">512</span></span></span></span>则存在此域,全部置零</td>
<td style="text-align:center">必须为0</td>
</tr>
</tbody>
</table>
<p>文档在接下来的部分介绍了如何初始化一个FAT卷由于目前只需要读取FAT卷所以可以忽略章节3.4。</p>
<p>在3.5章节中文档就介绍了如何在装载卷时判断FAT的类型其中关键的判断算法为</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(CountofClusters &lt; <span class="number">4085</span>) &#123;</span><br><span class="line"> <span class="comment">/* Volume is FAT12 */</span></span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span>(CountofClusters &lt; <span class="number">65525</span>) &#123;</span><br><span class="line"> <span class="comment">/* Volume is FAT16 */</span></span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="comment">/* Volume is FAT32 */</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>也就是说FAT的类型只与簇的数量有关而与磁盘的大小、扇区数量包括BPB中的BPB_FilSysType域都无关其中</p>
<ul>
<li>FAT12不能有超过4084个簇</li>
<li>FAT16不能有超过65524个簇也不能少于4085个簇</li>
</ul>
<p>在明白了BPB的结构之后就可以开始着手对BPB进行抽象了。</p>
<p>最初我采用了两个不同的类来分别对FAT12/16和FAT32的BPB进行抽象但这样的问题也是十分明显的在我不确定FAT实际的类型时我把首512字节视为哪个数据结构都不是很合理。但如果我不通过抽象后的数据结构去解析首512字节我又没办法判断究竟是那种FAT类型。为了解决这个矛盾我甚至由引入了一个类来描述FAT12/16与FAT32相同的37字节数据这就导致代码越来越乱看起来也让人云里雾里的。</p>
<p>但后来就注意到虽然对于FAT12/16和FAT32它们在结构上存在一定的不同但其BPB有效的部分都是首扇区的首512字节如果扇区大于512字节后面的也仍然填充0可以忽略</p>
<p>同时BPB需要提供的主要功能也是基本相同的主要的区别基本都在于域宽是16位还是32位。</p>
<p>如果在函数接口的上不区分FAT12/16和FAT32只在函数的实现上根据FAT12/16和FAT32的不同从512字节的数据中取出对应需要的域就可以实现功能的统一调用函数的代码就只需要关心需要使用FAT的哪种信息而不需要再关心FAT的类型。</p>
<p>例如,<code>BPB_FATSz16</code><code>BPB_FATSz32</code>实质上都是为了提供FAT表的大小它们是可以统一到一个接口<code>uint32 fatSize()</code>上的需要关心FAT表大小的代码不需要关心具体是哪种格式的FAT系统只需要调用<code>fatSize()</code>既可以取得需要的数值,具体的解析操作交给了函数本身去区分和实现。</p>
<p>阅读上表可以总结出BPB所需要提供的所有接口</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">uint16 <span class="title">bytesPerSector</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint8 <span class="title">sectorsPerCluster</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint16 <span class="title">sectorsPerTrack</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint16 <span class="title">headCount</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint16 <span class="title">reservedSectorCount</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint32 <span class="title">hiddenSectorCount</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint32 <span class="title">totalSectors</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint8 <span class="title">fatCount</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint32 <span class="title">fatSize</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint8 <span class="title">activeFAT</span><span class="params">()</span></span>; <span class="comment">//FAT32 only</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">isFATMirrored</span><span class="params">()</span></span>; <span class="comment">//FAT32only</span></span><br><span class="line"><span class="function">uint16 <span class="title">rootEntryCount</span><span class="params">()</span></span>; <span class="comment">//FAT16 only</span></span><br><span class="line"><span class="function">uint32 <span class="title">rootCluster</span><span class="params">()</span></span>; <span class="comment">//FAT32 only</span></span><br><span class="line"><span class="function"><span class="type">char</span>* <span class="title">oemName</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint8 <span class="title">mediaType</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint8 <span class="title">driveNumber</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">hasExtendedBootSignature</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint32 <span class="title">volumeSerialNumber</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function"><span class="type">char</span>* <span class="title">volumeLabel</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function"><span class="type">char</span>* <span class="title">fsTypeLabel</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint16 <span class="title">fsInfoSector</span><span class="params">()</span></span>; <span class="comment">//FAT32 only</span></span><br></pre></td></tr></table></figure>
<p>可以注意到有些函数是只有特定的FAT格式才存在的这时如果调用者强行调用了一个当前FAT格式不存在的接口实质上是不安全的因为这样并不会产生任何错误同时还会返回一个不确定的值。但这样的弊端比起这种实现的优点而言就显得微不足道了。</p>
<p>更进一步地说,由于调用接口的代码只能是操作系统的代码所调用,而操作系统代码只能由我们所完成,所以我们可以通过<strong>主动避免错误的调用来避开这个风险</strong>,抑或是,<strong>可以在函数内额外判断一次FAT类型并在不合法的调用处陷入死循环</strong>等等,有很多可以解决的方法。</p>
<p>除了解析BPB数据的接口以外由于BPB主要的作用是计算FAT文件系统各个区域的大小、偏移量等信息所以也不妨将这部分琐碎的代码纳入类的函数作为接口提供给其他程序</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">isFAT12</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">isFAT16</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">isFAT32</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint32 <span class="title">fatEntrySize</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint32 <span class="title">fatFrstSec</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint32 <span class="title">fatFrstSecAbsolute</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint32 <span class="title">fatTotalSec</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint32 <span class="title">rootDirFrstSec</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint32 <span class="title">rootDirFrstSecAbsolute</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint32 <span class="title">rootDirTotalSec</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint32 <span class="title">dataFrstSec</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint32 <span class="title">dataFrstSecAbsolute</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint32 <span class="title">dataTotalSec</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint32 <span class="title">totalClusters</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">uint32 <span class="title">secOfCluster</span><span class="params">(uint32 clus)</span></span>;</span><br><span class="line"><span class="function">uint32 <span class="title">absoluteSecOfCluster</span><span class="params">(uint32 clus)</span></span>;</span><br></pre></td></tr></table></figure>
<p>于是完整的BPB头文件<code>fatbpb.h</code>如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;types.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> _FAT_BPB_H_</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _FAT_BPB_H_</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">FATBPB</span> &#123;</span><br><span class="line"> <span class="keyword">private</span>:</span><br><span class="line"> byte _data[<span class="number">512</span>];</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">FATBPB</span>(uint32 bpbFrstSec);</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="comment">//Metadata parsing</span></span><br><span class="line"> <span class="function">uint16 <span class="title">bytesPerSector</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint8 <span class="title">sectorsPerCluster</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint16 <span class="title">sectorsPerTrack</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint16 <span class="title">headCount</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint16 <span class="title">reservedSectorCount</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">hiddenSectorCount</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">totalSectors</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint8 <span class="title">fatCount</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">fatSize</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint8 <span class="title">activeFAT</span><span class="params">()</span></span>; <span class="comment">//FAT32 only</span></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">isFATMirrored</span><span class="params">()</span></span>; <span class="comment">//FAT32only</span></span><br><span class="line"> <span class="function">uint16 <span class="title">rootEntryCount</span><span class="params">()</span></span>;<span class="comment">//FAT16 only</span></span><br><span class="line"> <span class="function">uint32 <span class="title">rootCluster</span><span class="params">()</span></span>; <span class="comment">//FAT32 only</span></span><br><span class="line"> <span class="function"><span class="type">char</span>* <span class="title">oemName</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint8 <span class="title">mediaType</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint8 <span class="title">driveNumber</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">hasExtendedBootSignature</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">volumeSerialNumber</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">char</span>* <span class="title">volumeLabel</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">char</span>* <span class="title">fsTypeLabel</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint16 <span class="title">fsInfoSector</span><span class="params">()</span></span>; <span class="comment">//FAT32 only</span></span><br><span class="line"> <span class="comment">//Basic calculation</span></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">isFAT12</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">isFAT16</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">isFAT32</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">fatEntrySize</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">fatFrstSec</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">fatFrstSecAbsolute</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">fatTotalSec</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">rootDirFrstSec</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">rootDirFrstSecAbsolute</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">rootDirTotalSec</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">dataFrstSec</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">dataFrstSecAbsolute</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">dataTotalSec</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">totalClusters</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">secOfCluster</span><span class="params">(uint32 clus)</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">absoluteSecOfCluster</span><span class="params">(uint32 clus)</span></span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure>
<p>具体的函数实现则放在<code>fatbpb.cpp</code>中如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;fatbpb.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;hdd.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//Read the first sector of the volume into buffer</span></span><br><span class="line"><span class="comment">//@param &#123;uint32&#125; bpbFrstSec : The first sector of the bpb region</span></span><br><span class="line">FATBPB::<span class="built_in">FATBPB</span>(uint32 bpbFrstSec) &#123;</span><br><span class="line"> <span class="built_in">HDDManager</span>().<span class="built_in">readSector</span>(bpbFrstSec, <span class="number">1</span>, _data);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns count of bytes per sector of the disk</span></span><br><span class="line"><span class="comment">//@return &#123;uint16&#125; : following values: 512, 1024, 2048 or 4096</span></span><br><span class="line"><span class="function">uint16 <span class="title">FATBPB::bytesPerSector</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> *(uint16*)(&amp;_data[<span class="number">11</span>]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns number of sectors per allocation unit</span></span><br><span class="line"><span class="comment">//@return &#123;uint8&#125; : A power of 2 that is greater than 0.</span></span><br><span class="line"><span class="comment">// Legal values are 1, 2, 4, 8, 16, 32, 64 and 128</span></span><br><span class="line"><span class="function">uint8 <span class="title">FATBPB::sectorsPerCluster</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> _data[<span class="number">13</span>];</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns sectors per track for interrupt 0x13.</span></span><br><span class="line"><span class="comment">//This is only relevant for media that have a geometry and are visible</span></span><br><span class="line"><span class="comment">//on interrupt 0x13</span></span><br><span class="line"><span class="comment">//@return &#123;uint16&#125; : sectors per track of the media</span></span><br><span class="line"><span class="function">uint16 <span class="title">FATBPB::sectorsPerTrack</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> *(uint16*)(&amp;_data[<span class="number">24</span>]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the number of heads of the media for interrupt 0x13.</span></span><br><span class="line"><span class="comment">//This is only relevant as discussed earlier for [sectorsPerTrack()]</span></span><br><span class="line"><span class="comment">//@return &#123;uint32&#125; : One based number for number of heads</span></span><br><span class="line"><span class="comment">// For example, on a 1.44MB 3.5-inch floppy drive this</span></span><br><span class="line"><span class="comment">// value is 2</span></span><br><span class="line"><span class="function">uint16 <span class="title">FATBPB::headCount</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> *(uint16*)(&amp;_data[<span class="number">26</span>]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the number of reserved sectors in the reserved region of the volume</span></span><br><span class="line"><span class="comment">//starting at the first sector of the volume, which is also the size of bios </span></span><br><span class="line"><span class="comment">//parameter block in sectors</span></span><br><span class="line"><span class="comment">//@return &#123;uint16&#125; : Non-zero value of the number of reserved sectors</span></span><br><span class="line"><span class="function">uint16 <span class="title">FATBPB::reservedSectorCount</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> *(uint16*)(&amp;_data[<span class="number">14</span>]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the count of hidden sectors preceding the partition that contains</span></span><br><span class="line"><span class="comment">//this FAT volume. This method is generally only relevant for media visible</span></span><br><span class="line"><span class="comment">//on interrupt 0x13</span></span><br><span class="line"><span class="comment">//@return &#123;uint32&#125; : The sectors preceding the partition</span></span><br><span class="line"><span class="function">uint32 <span class="title">FATBPB::hiddenSectorCount</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> *(uint32*)(&amp;_data[<span class="number">28</span>]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the 32-bit total count of sectors on the volume. The count includes</span></span><br><span class="line"><span class="comment">//the count of all sectors in all four regions of the volume</span></span><br><span class="line"><span class="comment">//@return &#123;uint32&#125; : The total count of sectors</span></span><br><span class="line"><span class="function">uint32 <span class="title">FATBPB::totalSectors</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">//It should be noted that for volumes that contain more than or equal to 0x10000</span></span><br><span class="line"> <span class="comment">//sectors, BPB_TotalSec32 field is used to store the total sectors count. In other</span></span><br><span class="line"> <span class="comment">//circumstances, BPB_TotalSec16 field is used to store the total sectors count</span></span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> <span class="comment">//In addition, BPB_TotalSec32 field should only be considered valid if BPB_TotalSec16</span></span><br><span class="line"> <span class="comment">//is zero</span></span><br><span class="line"> <span class="keyword">return</span> *(uint16*)(&amp;_data[<span class="number">19</span>]) == <span class="number">0</span> ? *(uint32*)(&amp;_data[<span class="number">32</span>]) : (uint32)(*(uint16*)(&amp;_data[<span class="number">19</span>]));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the count of file allocation tables (FATs) on the volume</span></span><br><span class="line"><span class="comment">//@return &#123;uint8&#125; : The count of FATs. A value of 2 is recommended although 1 is acceptable</span></span><br><span class="line"><span class="function">uint8 <span class="title">FATBPB::fatCount</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> _data[<span class="number">16</span>];</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the 32-bit count of sectors occupied by one FAT</span></span><br><span class="line"><span class="comment">//@return &#123;uint32&#125; : The sectors occupied by one FAT</span></span><br><span class="line"><span class="function">uint32 <span class="title">FATBPB::fatSize</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">//It should be noted that for FAT12/FAT16 volumes the sectors occupied by one FAT</span></span><br><span class="line"> <span class="comment">//is stored in 16-bit BPB_FATSz16 field while for FAT32 volumes the value is stored</span></span><br><span class="line"> <span class="comment">//in 32-bit BPB_FATSz32 field.</span></span><br><span class="line"> uint32 fatSz16 = (uint32)(*(uint16*)(&amp;_data[<span class="number">22</span>]));</span><br><span class="line"> <span class="keyword">if</span>(fatSz16 != <span class="number">0</span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> fatSz16;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> *(uint32*)(&amp;_data[<span class="number">36</span>]);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the active FAT on the volume</span></span><br><span class="line"><span class="comment">//It should be noted that this method is FAT32 only</span></span><br><span class="line"><span class="comment">//@return &#123;uint8&#125; : Zero-based number of active FAT, only valid if mirroring is disabled</span></span><br><span class="line"><span class="function">uint8 <span class="title">FATBPB::activeFAT</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> _data[<span class="number">40</span>] &amp; <span class="number">0xF</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns whether mirroring is disabled on the volume</span></span><br><span class="line"><span class="comment">//It should be noted that this method is FAT32 only</span></span><br><span class="line"><span class="comment">//@return &#123;bool&#125; : True means the FAT is morrored at runtime into all FATs.</span></span><br><span class="line"><span class="comment">// False means only one FAT is active, it is the one referenced by</span></span><br><span class="line"><span class="comment">// [activeFAT()]</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">FATBPB::isFATMirrored</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> (_data[<span class="number">40</span>] &amp; <span class="number">0x80</span>) != <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the count of 32-byte directory entries in the root directory</span></span><br><span class="line"><span class="comment">//@return &#123;uint16&#125; : The count of directory entries in the root directory.</span></span><br><span class="line"><span class="comment">// This value should always specify a count that when multiplied</span></span><br><span class="line"><span class="comment">// by 32 results in an even multiple of [bytesPerSecter()]</span></span><br><span class="line"><span class="function">uint16 <span class="title">FATBPB::rootEntryCount</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> *(uint16*)(&amp;_data[<span class="number">17</span>]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the cluster number of the first cluster of the root directory</span></span><br><span class="line"><span class="comment">//It should be noted that this method is FAT32 only</span></span><br><span class="line"><span class="comment">//@return &#123;uint32&#125; : the root cluster number</span></span><br><span class="line"><span class="function">uint32 <span class="title">FATBPB::rootCluster</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> *(uint32*)(&amp;_data[<span class="number">44</span>]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the OEM name identifier in the bios parameter block</span></span><br><span class="line"><span class="comment">//@return &#123;char*&#125; : The pointer to the field containing the OEM name in the buffer</span></span><br><span class="line"><span class="function"><span class="type">char</span>* <span class="title">FATBPB::oemName</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> (<span class="type">char</span>*)(&amp;_data[<span class="number">3</span>]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the media type</span></span><br><span class="line"><span class="comment">//@return &#123;uint8&#125; : Legal values are 0xF0, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE and 0xFF</span></span><br><span class="line"><span class="comment">// 0xF8 is standard value for fixed media while 0xF0 is for removable media</span></span><br><span class="line"><span class="function">uint8 <span class="title">FATBPB::mediaType</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> _data[<span class="number">21</span>];</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the drive number for interrupt 0x13</span></span><br><span class="line"><span class="comment">//@return &#123;uint8&#125; : 0x80 or 0x00</span></span><br><span class="line"><span class="function">uint8 <span class="title">FATBPB::driveNumber</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">//It should be noted that the field containing the value has different offset on FAT12/16</span></span><br><span class="line"> <span class="comment">//volumes and FAT32 volumes</span></span><br><span class="line"> <span class="comment">//On FAT12/16 volumes the field has an offset of 36 while on FAT32 volumes has an offset</span></span><br><span class="line"> <span class="comment">//of 64</span></span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">isFAT32</span>()) &#123;</span><br><span class="line"> <span class="keyword">return</span> _data[<span class="number">64</span>];</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> _data[<span class="number">36</span>];</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Check whether the volume contains the extended boot signature which indicates</span></span><br><span class="line"><span class="comment">//the BS_VolOD, BS_VolLab and BS_FilSysType fields are present</span></span><br><span class="line"><span class="comment">//@return &#123;bool&#125; : Ture for the extended boot signature value is set to 0x29</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">FATBPB::hasExtendedBootSignature</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">//It should be noted that the field containing the value has different offset on FAT12/16</span></span><br><span class="line"> <span class="comment">//volumes and FAT32 volumes</span></span><br><span class="line"> <span class="comment">//On FAT12/16 volumes the field has an offset of 38 while on FAT32 volumes has an offset</span></span><br><span class="line"> <span class="comment">//of 66</span></span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">isFAT32</span>()) &#123;</span><br><span class="line"> <span class="keyword">return</span> _data[<span class="number">66</span>] == <span class="number">0x29</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> _data[<span class="number">38</span>] == <span class="number">0x29</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the volume serial number for volume tracking on removable media.</span></span><br><span class="line"><span class="comment">//It should be noted that this value is only present when [hasExtendedBootSignature()]</span></span><br><span class="line"><span class="comment">//function returns a true value.</span></span><br><span class="line"><span class="comment">//@return &#123;uint32&#125; : Volume serial number</span></span><br><span class="line"><span class="function">uint32 <span class="title">FATBPB::volumeSerialNumber</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">//It should be noted that the field containing the value has different offset on FAT12/16</span></span><br><span class="line"> <span class="comment">//volumes and FAT32 volumes</span></span><br><span class="line"> <span class="comment">//On FAT12/16 volumes the field has an offset of 39 while on FAT32 volumes has an offset</span></span><br><span class="line"> <span class="comment">//of 67</span></span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">isFAT32</span>()) &#123;</span><br><span class="line"> <span class="keyword">return</span> *(uint32*)(&amp;_data[<span class="number">67</span>]);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> *(uint32*)(&amp;_data[<span class="number">39</span>]);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the volume label</span></span><br><span class="line"><span class="comment">//@return &#123;char*&#125; : The pointer of the volume label stored in the buffer</span></span><br><span class="line"><span class="function"><span class="type">char</span>* <span class="title">FATBPB::volumeLabel</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">//It should be noted that the field containing the value has different offset on FAT12/16</span></span><br><span class="line"> <span class="comment">//volumes and FAT32 volumes</span></span><br><span class="line"> <span class="comment">//On FAT12/16 volumes the field has an offset of 43 while on FAT32 volumes has an offset</span></span><br><span class="line"> <span class="comment">//of 71</span></span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">isFAT32</span>()) &#123;</span><br><span class="line"> <span class="keyword">return</span> (<span class="type">char</span>*)(&amp;_data[<span class="number">71</span>]);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="built_in">return</span> (<span class="type">char</span>*)(&amp;_data[<span class="number">43</span>]);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the file system type string</span></span><br><span class="line"><span class="comment">//@return &#123;char*&#125; : The pointer of the fs type string, the string should be one of the</span></span><br><span class="line"><span class="comment">// strings: &quot;FAT&quot;, &quot;FAT12&quot;, &quot;FAT16&quot; or &quot;FAT32&quot;</span></span><br><span class="line"><span class="function"><span class="type">char</span>* <span class="title">FATBPB::fsTypeLabel</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">//It should be noted that the field containing the value has different offset on FAT12/16</span></span><br><span class="line"> <span class="comment">//volumes and FAT32 volumes</span></span><br><span class="line"> <span class="comment">//On FAT12/16 volumes the field has an offset of 54 while on FAT32 volumes has an offset</span></span><br><span class="line"> <span class="comment">//of 82</span></span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">isFAT32</span>()) &#123;</span><br><span class="line"> <span class="keyword">return</span> (<span class="type">char</span>*)(&amp;_data[<span class="number">82</span>]);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="built_in">return</span> (<span class="type">char</span>*)(&amp;_data[<span class="number">54</span>]);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the sector number of FSINFO structure in the reserved area of the FAT32 volume.</span></span><br><span class="line"><span class="comment">//It should be noted that this method is only available on FAT32 volumes.</span></span><br><span class="line"><span class="comment">//@return &#123;uint16&#125; : FSINFO structure sector number, usually 1</span></span><br><span class="line"><span class="function">uint16 <span class="title">FATBPB::fsInfoSector</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> *(uint16*)(&amp;_data[<span class="number">48</span>]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Check whether the volume is FAT12</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">FATBPB::isFAT12</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> uint32 totClus = <span class="built_in">totalClusters</span>();</span><br><span class="line"> <span class="keyword">return</span> totClus &lt; <span class="number">4085</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Check whether the volume is FAT16</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">FATBPB::isFAT16</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> uint32 totClus = <span class="built_in">totalClusters</span>();</span><br><span class="line"> <span class="keyword">return</span> totClus &gt;= <span class="number">4085</span> &amp;&amp; totClus &lt; <span class="number">65525</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Check whether the volume is FAT32</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">FATBPB::isFAT32</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> uint32 totClus = <span class="built_in">totalClusters</span>();</span><br><span class="line"> <span class="keyword">return</span> totClus &gt;= <span class="number">65525</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Return the size in bytes of a fat entry</span></span><br><span class="line"><span class="comment">//@return &#123;uint32&#125; : 12 for FAT12, 16 for FAT16, 32 for FAT32</span></span><br><span class="line"><span class="function">uint32 <span class="title">FATBPB::fatEntrySize</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> uint32 totClus = <span class="built_in">totalClusters</span>();</span><br><span class="line"> <span class="keyword">if</span>(totClus &lt; <span class="number">4085</span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">12</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(totClus &lt; <span class="number">65525</span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">16</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">32</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the first sector of first FAT</span></span><br><span class="line"><span class="function">uint32 <span class="title">FATBPB::fatFrstSec</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">reservedSectorCount</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the ABSOLUTE sector number of first FAT</span></span><br><span class="line"><span class="function">uint32 <span class="title">FATBPB::fatFrstSecAbsolute</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">hiddenSectorCount</span>() + <span class="built_in">reservedSectorCount</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the total sectors of FATs</span></span><br><span class="line"><span class="function">uint32 <span class="title">FATBPB::fatTotalSec</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">fatCount</span>() * <span class="built_in">fatSize</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the first sector of the root directory region</span></span><br><span class="line"><span class="function">uint32 <span class="title">FATBPB::rootDirFrstSec</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> uint32 resvdSecCnt = <span class="built_in">reservedSectorCount</span>();</span><br><span class="line"> uint32 fatSecCnt = <span class="built_in">fatTotalSec</span>();</span><br><span class="line"> <span class="keyword">return</span> resvdSecCnt + fatSecCnt;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the ABSOLUTE sector number of the first sector of the root</span></span><br><span class="line"><span class="comment">//directory region</span></span><br><span class="line"><span class="function">uint32 <span class="title">FATBPB::rootDirFrstSecAbsolute</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> uint32 resvdSecCnt = <span class="built_in">reservedSectorCount</span>();</span><br><span class="line"> uint32 fatSecCnt = <span class="built_in">fatTotalSec</span>();</span><br><span class="line"> uint32 hiddenSec = <span class="built_in">hiddenSectorCount</span>();</span><br><span class="line"> <span class="keyword">return</span> hiddenSec + resvdSecCnt + fatSecCnt;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the total sectors of root directory region</span></span><br><span class="line"><span class="function">uint32 <span class="title">FATBPB::rootDirTotalSec</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> uint32 rootEntCnt = <span class="built_in">rootEntryCount</span>();</span><br><span class="line"> uint32 bytsPerSec = <span class="built_in">bytesPerSector</span>();</span><br><span class="line"> <span class="keyword">return</span> (rootEntCnt * <span class="number">32</span> + (bytsPerSec - <span class="number">1</span>)) / bytsPerSec;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the first sector of data region</span></span><br><span class="line"><span class="function">uint32 <span class="title">FATBPB::dataFrstSec</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> uint32 resvdSecCnt = <span class="built_in">reservedSectorCount</span>();</span><br><span class="line"> uint32 fatSecCnt = <span class="built_in">fatTotalSec</span>();</span><br><span class="line"> uint32 rootSecCnt = <span class="built_in">rootDirTotalSec</span>();</span><br><span class="line"> <span class="keyword">return</span> resvdSecCnt + fatSecCnt + rootSecCnt;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the ABSOLUTE sector number of data region</span></span><br><span class="line"><span class="function">uint32 <span class="title">FATBPB::dataFrstSecAbsolute</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> uint32 resvdSecCnt = <span class="built_in">reservedSectorCount</span>();</span><br><span class="line"> uint32 fatSecCnt = <span class="built_in">fatTotalSec</span>();</span><br><span class="line"> uint32 rootSecCnt = <span class="built_in">rootDirTotalSec</span>();</span><br><span class="line"> uint32 hiddenSec = <span class="built_in">hiddenSectorCount</span>();</span><br><span class="line"> <span class="keyword">return</span> hiddenSec + resvdSecCnt + fatSecCnt + rootSecCnt;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the total sectors of data region</span></span><br><span class="line"><span class="function">uint32 <span class="title">FATBPB::dataTotalSec</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> uint32 totSec = <span class="built_in">totalSectors</span>();</span><br><span class="line"> uint32 resvdSecCnt = <span class="built_in">reservedSectorCount</span>();</span><br><span class="line"> uint32 fatSecCnt = <span class="built_in">fatTotalSec</span>();</span><br><span class="line"> uint32 rootSecCnt = <span class="built_in">rootDirTotalSec</span>();</span><br><span class="line"> <span class="keyword">return</span> totSec - (resvdSecCnt + fatSecCnt + rootSecCnt);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the total clusters of data region</span></span><br><span class="line"><span class="function">uint32 <span class="title">FATBPB::totalClusters</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">dataTotalSec</span>() / <span class="built_in">sectorsPerCluster</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the sector of the given cluster</span></span><br><span class="line"><span class="comment">//@param &#123;uint32&#125; clus : The zero-based cluster number</span></span><br><span class="line"><span class="comment">//@return &#123;uint32&#125; : The sector number of the cluster</span></span><br><span class="line"><span class="function">uint32 <span class="title">FATBPB::secOfCluster</span><span class="params">(uint32 clus)</span> </span>&#123;</span><br><span class="line"> uint32 frstDataSec = <span class="built_in">dataFrstSec</span>();</span><br><span class="line"> uint32 clusterOffset = clus - <span class="number">2</span>;</span><br><span class="line"> uint32 secPerClus = <span class="built_in">sectorsPerCluster</span>();</span><br><span class="line"> <span class="keyword">return</span> frstDataSec + clusterOffset * secPerClus;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Returns the ABSOLUTE sector of the given cluster</span></span><br><span class="line"><span class="comment">//@param &#123;uint32&#125; clus : The zero-based cluster number</span></span><br><span class="line"><span class="comment">//@return &#123;uint32&#125; : The absolute sector number of the cluster</span></span><br><span class="line"><span class="function">uint32 <span class="title">FATBPB::absoluteSecOfCluster</span><span class="params">(uint32 clus)</span> </span>&#123;</span><br><span class="line"> uint32 frstDataSec = <span class="built_in">dataFrstSec</span>();</span><br><span class="line"> uint32 clusterOffset = clus - <span class="number">2</span>;</span><br><span class="line"> uint32 secPerClus = <span class="built_in">sectorsPerCluster</span>();</span><br><span class="line"> uint32 hiddenSec = <span class="built_in">hiddenSectorCount</span>();</span><br><span class="line"> <span class="keyword">return</span> hiddenSec + frstDataSec + clusterOffset * secPerClus;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h4 id="FAT">FAT</h4>
<div class="note blue icon-padding flat"><i class="note-icon fas fa-history"></i><p><strong>回顾:</strong><br>
如果一个文件大小超过了一个簇那么用来存储它数据的簇可能在磁盘上并不连续为了能够将这些分散的簇连起来文件系统一般会为每一个簇对应一个域用来保存关于它下一个簇的信息从而就可以如同链表那样将整个文件串联在一起。在FAT中这些域被集中存储在磁盘的一段空间内这一段空间就叫做FAT <em>(File Allocation Table)</em></p>
</div>
<p>对于不同的FAT格式FAT表中每个条目 <em>(entry)</em> 的大小不同:</p>
<ul>
<li>对于FAT12而言每个条目长12位</li>
<li>对于FAT16而言每个条目长16位</li>
<li>对于FAT32而言每个条目长32位</li>
</ul>
<p>这时候就发现了原来FAT后面的数字就是<strong>指代的FAT表中每个条目的长度</strong></p>
<p>FAT条目中可能的存储值及其含义如下其中MAX指代磁盘中合法的最大的簇号</p>
<table>
<thead>
<tr>
<th style="text-align:center">FAT12</th>
<th style="text-align:center">FAT16</th>
<th style="text-align:center">FAT32</th>
<th style="text-align:center">含义</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">0x000</td>
<td style="text-align:center">0x0000</td>
<td style="text-align:center">0x0000000</td>
<td style="text-align:center">当前条目所对应的簇空闲</td>
</tr>
<tr>
<td style="text-align:center">0x000 ~ MAX</td>
<td style="text-align:center">0x0002 ~ MAX</td>
<td style="text-align:center">0x0000002 ~ MAX</td>
<td style="text-align:center">当前条目所对应的簇存在内容,并且条目的值就是下一个簇的簇号</td>
</tr>
<tr>
<td style="text-align:center">(MAX + 1) ~ 0xFF6</td>
<td style="text-align:center">(MAX + 1) ~ 0xFFF6</td>
<td style="text-align:center">(MAX + 1) ~ 0xFFFFFF6</td>
<td style="text-align:center">保留的值,不能够使用</td>
</tr>
<tr>
<td style="text-align:center">0xFF7</td>
<td style="text-align:center">0xFFF7</td>
<td style="text-align:center">0xFFFFFF7</td>
<td style="text-align:center">当前条目所对应的簇是损坏的簇</td>
</tr>
<tr>
<td style="text-align:center">0xFF8 ~ 0xFFE</td>
<td style="text-align:center">0xFFF8 ~ 0xFFFE</td>
<td style="text-align:center">0xFFFFFF8 ~ 0xFFFFFFE</td>
<td style="text-align:center">保留的值,有时也作为指示当前条目所对应的簇是文件的最后一个簇</td>
</tr>
<tr>
<td style="text-align:center">0xFFF</td>
<td style="text-align:center">0xFFFF</td>
<td style="text-align:center">0xFFFFFFF</td>
<td style="text-align:center">当前条目所对应的簇是文件的最后一个簇</td>
</tr>
</tbody>
</table>
<div class="note red icon-padding flat"><i class="note-icon fas fa-exclamation-circle"></i><p><strong>陷阱FAT32高四位保留</strong><br>
FAT32中的每个条目高四位都是被保留的所以可以看到上表中FAT32对应的值只有7位。除了在格式化的时候在其他任何时候设置FAT条目时都不应该更改原先高四位的值。</p>
</div>
<div class="note red icon-padding flat"><i class="note-icon fas fa-exclamation-circle"></i><p><strong>陷阱FAT表首两个条目保留</strong><br>
注意到上表中有效的FAT条目值从2开始因为FAT表中的前两个条目是被保留的。<br>
这也就导致了<strong>簇号和实际的簇产生了2的偏移</strong>:对于任意簇号<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi></mrow><annotation encoding="application/x-tex">N</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.10903em;">N</span></span></span></span>,当在磁盘上访问实际的簇时,应当访问第<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi><mo></mo><mn>2</mn></mrow><annotation encoding="application/x-tex">N-2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7667em;vertical-align:-0.0833em;"></span><span class="mord mathnormal" style="margin-right:0.10903em;">N</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin"></span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">2</span></span></span></span>个簇</p>
</div>
<p>FAT表中第一个保留的条目FAT[0]包含了BPB_Media域中的内容其他的位被设置为<code>1</code>对于FAT32高四位同样不进行更改。例如如果BPB_Media的值为<code>0xF8</code>,那么</p>
<ul>
<li>对于FAT12值为<code>0xFF8</code></li>
<li>对于FAT16值为<code>0xFFF8</code></li>
<li>对于FAT32值为<code>0xFFFFFF8</code></li>
</ul>
<p>第二个保留的条目FAT[1]在格式化时会被格式化工具赋一个EOC值具体用处不明。对于FAT12而言由于空间有限所以并没有额外的标记位对于FAT16和FAT32而言Windows系统可能会使用高两位作为脏卷的标记位其中最高位为<code>ClnShutBit</code>,如果该位置位,则意味着上一次该设备没有被正常卸载,可能需要检查文件系统的完整性;次高位为<code>HrdErrBit</code>如果该位置位则标明读写功能正常如果置0则代表遇到了IO错误提示一些磁盘扇区可能发生了错误。</p>
<p>由于FAT表相当于一个数组因此其不需要特殊的抽象。</p>
<p>不过由于FAT12的特殊性12位长度的数据并不能跟字节对齐也就是说在FAT12中一个条目从某个字节的中间开始所以在根据下标从FAT从取值时可能会稍微复杂一些在文档中也给出了具体的读取的代码。</p>
<p>由于具体文件的读取在KernelLoader中进行因此这部分的内容可以留待下一个章节再详细研究。</p>
<h4 id="目录结构">目录结构</h4>
<p>到目前为止BPB以及FAT表我们都已经清楚了这样不论是FAT12、FAT16还是FAT32我们都可以顺利地找到根目录所在的区域并且读取根目录所包含的所有数据了</p>
<ul>
<li>对于FAT12/16根据保留区的大小以及FAT的大小可以计算出根目录区的首扇区位置根据根目录区首扇区位置和根目录区大小就可以读取全部根目录区数据</li>
<li>对于FAT32根据根目录区所在的簇号以及FAT表可以读取全部根目录区数据</li>
</ul>
<p>那么,接下来就要解决如何解析根目录内的文件了。更广泛地讲,由于根目录就像其他所有的文件目录一样,所以接下来就要解决如何读取一个目录,并且得到目录中各个文件的信息。</p>
<p>实际上目录同样是一个由目录条目构成的数组其中每一个目录条目都是一个32字节长度的数据结构而正是这个数据结构中存储的数据描述了一个目录中存储的文件或是一个子目录的详细信息例如它的创建日期和时间、名称或是最重要的首簇簇号等等。它的完整结构如下</p>
<table>
<thead>
<tr>
<th style="text-align:center">域名称</th>
<th style="text-align:center">偏移</th>
<th style="text-align:center">大小</th>
<th style="text-align:center">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">DIR_Name</td>
<td style="text-align:center">0</td>
<td style="text-align:center">11</td>
<td style="text-align:center">短名称格式的文件名</td>
</tr>
<tr>
<td style="text-align:center">DIR_Attr</td>
<td style="text-align:center">11</td>
<td style="text-align:center">1</td>
<td style="text-align:center">文件的属性标记</td>
</tr>
<tr>
<td style="text-align:center">DIR_NTRes</td>
<td style="text-align:center">12</td>
<td style="text-align:center">1</td>
<td style="text-align:center">保留位必须为0</td>
</tr>
<tr>
<td style="text-align:center">DIR_CrtTimeTenth</td>
<td style="text-align:center">13</td>
<td style="text-align:center">1</td>
<td style="text-align:center">文件创建时间单位为10ms</td>
</tr>
<tr>
<td style="text-align:center">DIR_CrtTime</td>
<td style="text-align:center">14</td>
<td style="text-align:center">2</td>
<td style="text-align:center">文件创建时间</td>
</tr>
<tr>
<td style="text-align:center">DIR_CrtDate</td>
<td style="text-align:center">16</td>
<td style="text-align:center">2</td>
<td style="text-align:center">文件创建日期</td>
</tr>
<tr>
<td style="text-align:center">DIR_LstAccDate</td>
<td style="text-align:center">18</td>
<td style="text-align:center">2</td>
<td style="text-align:center">文件最近访问日期</td>
</tr>
<tr>
<td style="text-align:center">DIR_FstClusHI</td>
<td style="text-align:center">20</td>
<td style="text-align:center">2</td>
<td style="text-align:center">首簇簇号高16位</td>
</tr>
<tr>
<td style="text-align:center">DIR_WrtTime</td>
<td style="text-align:center">22</td>
<td style="text-align:center">2</td>
<td style="text-align:center">文件修改时间</td>
</tr>
<tr>
<td style="text-align:center">DIR_WrtDate</td>
<td style="text-align:center">24</td>
<td style="text-align:center">2</td>
<td style="text-align:center">文件修改日期</td>
</tr>
<tr>
<td style="text-align:center">DIR_FstClusLO</td>
<td style="text-align:center">26</td>
<td style="text-align:center">2</td>
<td style="text-align:center">首簇簇号低16位</td>
</tr>
<tr>
<td style="text-align:center">DIR_FileSize</td>
<td style="text-align:center">28</td>
<td style="text-align:center">4</td>
<td style="text-align:center">文件的大小,单位为字节</td>
</tr>
</tbody>
</table>
<p>其中提到了一些概念,如短名称、文件属性以及一些仍然不明确的结构,如日期和时间的表示格式,如果继续阅读,则会发现文档也一一对它们作出了解释。</p>
<p><strong>短名称</strong>为一种表示文件名称的格式其11字节长度的域被分为8字节和3字节的空间其中11字节用来存储文件不含扩展名的部分而后3字节用来存储文件的扩展名在存储名字的时候所有的字母都会以大写字母的形式存储。</p>
<p>同时,短名称的存储方式会在文件名长度小于最大长度时在其后面填补空格,例如<code>FOO.BAR</code>在存储时由于文件名为三字节所以会在其后填补5个空格存储为<code>FOO BAR</code></p>
<p>除此之外,短名称还遵循以下规则:</p>
<ul>
<li>若首字节为<code>0xE5</code>则代表当前条目为空</li>
<li>若首字节为<code>0x00</code>则同样代表当前条目为空,并且还代表当前条目之后的所有条目都为空</li>
<li>首字节不能为空格,也就是说文件名不能以空格开头</li>
<li>目录中不能出现名称相同的两个条目</li>
<li>不能出现小写字母</li>
<li>ASCII值小于<code>0x20</code>的字符以及<code>0x22</code><code>0x2A</code><code>0x2B</code><code>0x2C</code><code>0x2E</code><code>0x2F</code><code>0x3A</code><code>0x3B</code><code>0x3C</code><code>0x3D</code><code>0x3E</code><code>0x3F</code><code>0x5B</code><code>0x5C</code><code>0x5D</code><code>0x7C</code></li>
</ul>
<p>然而如果文件名称很长的话短名称就显得不太够用了所以FAT还额外提出了长名称的解决方案并且称存储短名称的条目为 <em>SFNEntry</em>,长名称的目录为 <em>LFNEntry</em></p>
<p>在长名称的解决方案中,一个文件会对应一个短名称的条目和一系列长名称的条目,短名称条目存储文件名称的前数个字符和扩展名,而长名称则存储文件的全部名称。</p>
<p>在目录中,一个文件对应的长名称条目和短名称条目连续存储,其中地址从低到高依次存储:</p>
<ul>
<li>第N个长名称条目</li>
<li>第N-1个长名称条目</li>
<li></li>
<li>第1个长名称条目</li>
<li>短名称条目</li>
</ul>
<p>其中,长名称条目的结构如下:</p>
<table>
<thead>
<tr>
<th style="text-align:center">域名称</th>
<th style="text-align:center">偏移</th>
<th style="text-align:center">大小</th>
<th style="text-align:center">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">LDIR_Ord</td>
<td style="text-align:center">0</td>
<td style="text-align:center">1</td>
<td style="text-align:center">当前条目在当前文件所有长名称条目中的顺序从1开始计数<br/>如果是最后一个条目,则需要额外置位<code>0x40</code></td>
</tr>
<tr>
<td style="text-align:center">LDIR_Name1</td>
<td style="text-align:center">1</td>
<td style="text-align:center">10</td>
<td style="text-align:center">存储当前条目中文件名的第1~5个字符</td>
</tr>
<tr>
<td style="text-align:center">LDIR_Attr</td>
<td style="text-align:center">11</td>
<td style="text-align:center">1</td>
<td style="text-align:center">文件的属性标记,与短文件名条目的属性标记域保持相同含义<br/>对于长文件条目,所有的属性位都应被置位<br/>也即<code>ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID</code></td>
</tr>
<tr>
<td style="text-align:center">LDIR_Type</td>
<td style="text-align:center">12</td>
<td style="text-align:center">1</td>
<td style="text-align:center">必须为0</td>
</tr>
<tr>
<td style="text-align:center">LDIR_Chksum</td>
<td style="text-align:center">13</td>
<td style="text-align:center">1</td>
<td style="text-align:center">校验位,用于校验当前条目是否与文件对应的短文件名条目相匹配</td>
</tr>
<tr>
<td style="text-align:center">LDIR_Name2</td>
<td style="text-align:center">14</td>
<td style="text-align:center">12</td>
<td style="text-align:center">存储当前条目中文件名的第6~11个字符</td>
</tr>
<tr>
<td style="text-align:center">LDIR_FstClusLO</td>
<td style="text-align:center">26</td>
<td style="text-align:center">2</td>
<td style="text-align:center">必须为0</td>
</tr>
<tr>
<td style="text-align:center">LDIR_Name3</td>
<td style="text-align:center">28</td>
<td style="text-align:center">4</td>
<td style="text-align:center">存储当前条目中文件名的第12~13个字符</td>
</tr>
</tbody>
</table>
<div class="note red icon-padding flat"><i class="note-icon fas fa-exclamation-circle"></i><p><strong>陷阱LDIR_Ord从1开始</strong><br>
文件对应的第一个长文件名条目在集合中的序号为1而不是0</p>
</div>
<div class="note red icon-padding flat"><i class="note-icon fas fa-exclamation-circle"></i><p><strong>陷阱长文件名使用Unicode存储</strong><br>
长文件名中的LDIR_Name1、LDIR_Name2和LDIR_Name3中存储的都是Unicode格式的文件名<strong>一个字符占据两个字节</strong></p>
</div>
<p>对于<strong>校验码</strong>,文档也提供了算法,对应到实验中的实现为:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Calculate/Return the checksum of the entry</span></span><br><span class="line"><span class="comment">//It should be noted that this function BEHAVE DIFFERENTLY in SFN and LFN entries</span></span><br><span class="line"><span class="comment">//@return &#123;uint32&#125; : Calculate checksum out of filename in SFN entries</span></span><br><span class="line"><span class="comment">//@return &#123;uint32&#125; : Return checksum in LFN entries</span></span><br><span class="line"><span class="function">uint32 <span class="title">DirEntry::checkSum</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">isSFNEntry</span>()) &#123;</span><br><span class="line"> <span class="comment">//ChkSum()</span></span><br><span class="line"> <span class="comment">//Returns an unsigned byte checksum computed on an unsigned byte</span></span><br><span class="line"> <span class="comment">//array. The array must be 11 bytes long and is assumed to contain</span></span><br><span class="line"> <span class="comment">//a name stored in the format of a MS-DOS directory entry</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">//The following method is copied from Microsoft FAT specification</span></span><br><span class="line"> <span class="comment">//page 32, chapter 7.2 Checksum generation</span></span><br><span class="line"> byte* pName = &amp;_data[<span class="number">0</span>];</span><br><span class="line"> uint16 nameLen;</span><br><span class="line"> uint8 sum;</span><br><span class="line"> sum = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span>(nameLen = <span class="number">11</span>; nameLen != <span class="number">0</span>; nameLen--) &#123;</span><br><span class="line"> <span class="comment">//<span class="doctag">NOTE:</span> The operation is an unsigned char rotate right</span></span><br><span class="line"> sum = ((sum &amp; <span class="number">1</span>) ? <span class="number">0x80</span> : <span class="number">0</span>) + (sum &gt;&gt; <span class="number">1</span>) + *pName++;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> sum;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="comment">//According to Microsoft FAT specification:</span></span><br><span class="line"> <span class="comment">//The checksum in LFN entries is stored in LDIR_Chksum field</span></span><br><span class="line"> <span class="comment">//which has an offset of 13 and size of 1 byte</span></span><br><span class="line"> <span class="keyword">return</span> _data[<span class="number">13</span>];</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>文件的属性</strong>使用按位枚举的方式表示,其中的六个二进制位分别如下:</p>
<table>
<thead>
<tr>
<th style="text-align:center">属性</th>
<th style="text-align:center"></th>
<th style="text-align:center">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">ATTR_READ_ONLY</td>
<td style="text-align:center">1 &lt;&lt; 0</td>
<td style="text-align:center">文件只读</td>
</tr>
<tr>
<td style="text-align:center">ATTR_HIDDEN</td>
<td style="text-align:center">1 &lt;&lt; 1</td>
<td style="text-align:center">文件隐藏<br/>除非用户或程序显式声明要求访问隐藏的文件,否则不应当在文件列表中被列出</td>
</tr>
<tr>
<td style="text-align:center">ATTR_SYSTEM</td>
<td style="text-align:center">1 &lt;&lt; 2</td>
<td style="text-align:center">文件为系统文件<br/>除非用户或程序显式声明要求访问系统文件,否则不应当在文件列表中被列出</td>
</tr>
<tr>
<td style="text-align:center">ATTR_VOLUME_ID</td>
<td style="text-align:center">1 &lt;&lt; 3</td>
<td style="text-align:center">文件用来描述卷标</td>
</tr>
<tr>
<td style="text-align:center">ATTR_DIRECTORY</td>
<td style="text-align:center">1 &lt;&lt; 4</td>
<td style="text-align:center">文件实际上是一个目录</td>
</tr>
<tr>
<td style="text-align:center">ATTR_ARCHIVE</td>
<td style="text-align:center">1 &lt;&lt; 5</td>
<td style="text-align:center">当文件被创建、重命名或修改时置位,指示文件是否被修改过</td>
</tr>
</tbody>
</table>
<div class="note red icon-padding flat"><i class="note-icon fas fa-exclamation-circle"></i><p><strong>陷阱:卷标文件只能出现在根目录</strong><br>
卷标文件为根目录中描述卷标的特殊文件其DIR_NAME域全部用来存储卷标同时属性域为<code>0x8</code>也即ATTR_VOLUME_ID其他部分均为0。</p>
</div>
<p><strong>文件的日期和时间</strong>有特殊的存储格式</p>
<p>其中文件的日期包括三个部分:</p>
<ul>
<li><code>[4:0]</code>从1至31</li>
<li><code>[8:5]</code>从1至12</li>
<li><code>[9]</code>从1980年起的年份偏移从0至127</li>
</ul>
<p>文件的时间同样也包括三个部分:</p>
<ul>
<li><code>[4:0]</code>从零起经过的2s间隔数从0至29</li>
<li><code>[10:5]</code>从0至59</li>
<li><code>[15:11]</code>从0至23</li>
</ul>
<p>可以看到秒的精度为2s这也就引出了CrtTimeTenth这个域其精度为10ms范围从0至199正好填补了2s之间的空缺使得精度提高到10ms</p>
<p>在了解了目录的结构之后,就可以着手对目录进行抽象了。由于目录本身还是相当于关于目录条目的数组,所以根本在于对目录条目进行抽象。</p>
<p>因为目录条目分为SFN短文件名和LFN长文件名两种类型但其总长度一样所以可以参考对BPB进行抽象的过程将两种条目合并在一起均视为<code>DirEntry</code>类,在调用具体函数的时候再根据条目类型的不同执行不同的判断。</p>
<p>其中由于文件属性的域涉及到位枚举而C++没有原生支持位枚举,所以需要我们手动先在头文件中实现位枚举:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">DirectoryFlag</span> &#123;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="keyword">enum</span> <span class="title class_">ATTRIBUTES</span> &#123;</span><br><span class="line"> READ_ONLY = <span class="number">1</span>,</span><br><span class="line"> HIDDEN = <span class="number">1</span> &lt;&lt; <span class="number">1</span>,</span><br><span class="line"> SYSTEM = <span class="number">1</span> &lt;&lt; <span class="number">2</span>,</span><br><span class="line"> VOLUME_ID = <span class="number">1</span> &lt;&lt; <span class="number">3</span>,</span><br><span class="line"> DIRECTORY = <span class="number">1</span> &lt;&lt; <span class="number">4</span>,</span><br><span class="line"> ARCHIVE = <span class="number">1</span> &lt;&lt; <span class="number">5</span></span><br><span class="line"> &#125;;</span><br><span class="line"> <span class="keyword">private</span>:</span><br><span class="line"> uint8 _attr;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">DirectoryFlag</span>();</span><br><span class="line"> <span class="built_in">DirectoryFlag</span>(uint8 flags);</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> DirectoryFlag&amp; <span class="keyword">operator</span>=(uint8 flags);</span><br><span class="line"> DirectoryFlag&amp; <span class="keyword">operator</span>=(<span class="type">const</span> DirectoryFlag&amp; other);</span><br><span class="line"> <span class="comment">//Judge whether two flags are equal</span></span><br><span class="line"> <span class="type">bool</span> <span class="keyword">operator</span>==(uint8 flags);</span><br><span class="line"> <span class="comment">//Combine two flags</span></span><br><span class="line"> DirectoryFlag <span class="keyword">operator</span>+(uint8 flags);</span><br><span class="line"> DirectoryFlag&amp; <span class="keyword">operator</span>+=(uint8 flags);</span><br><span class="line"> <span class="comment">//Remove flags</span></span><br><span class="line"> DirectoryFlag <span class="keyword">operator</span>-(uint8 flags);</span><br><span class="line"> DirectoryFlag&amp; <span class="keyword">operator</span>-=(uint8 flags);</span><br><span class="line"> <span class="comment">//Bitwise or</span></span><br><span class="line"> DirectoryFlag <span class="keyword">operator</span>|(uint8 flags);</span><br><span class="line"> DirectoryFlag&amp; <span class="keyword">operator</span>|=(uint8 flags);</span><br><span class="line"> <span class="comment">//Bitwise and</span></span><br><span class="line"> DirectoryFlag <span class="keyword">operator</span>&amp;(uint8 flags);</span><br><span class="line"> DirectoryFlag&amp; <span class="keyword">operator</span>&amp;=(uint8 flags);</span><br><span class="line"> <span class="comment">//Reverse flags</span></span><br><span class="line"> DirectoryFlag <span class="keyword">operator</span>~();</span><br><span class="line"> <span class="comment">//Toggle flag bits</span></span><br><span class="line"> DirectoryFlag&amp; <span class="keyword">operator</span>^=(uint8 flags);</span><br><span class="line"> <span class="comment">//Judge whether flags are contained</span></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">contains</span><span class="params">(uint8 flags)</span></span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>并在<code>.cpp</code>文件中给出重载操作符的实现</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line">DirectoryFlag::<span class="built_in">DirectoryFlag</span>() &#123;</span><br><span class="line"> _attr = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line">DirectoryFlag::<span class="built_in">DirectoryFlag</span>(uint8 flags) &#123;</span><br><span class="line"> _attr = flags;</span><br><span class="line">&#125;</span><br><span class="line">DirectoryFlag&amp; DirectoryFlag::<span class="keyword">operator</span>=(uint8 flags) &#123;</span><br><span class="line"> _attr = flags;</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line">DirectoryFlag&amp; DirectoryFlag::<span class="keyword">operator</span>=(<span class="type">const</span> DirectoryFlag&amp; other) &#123;</span><br><span class="line"> _attr = other._attr;</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Judge whether two flags are equal</span></span><br><span class="line"><span class="type">bool</span> DirectoryFlag::<span class="keyword">operator</span>==(uint8 flags) &#123;</span><br><span class="line"> <span class="keyword">return</span> _attr == flags;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Combine two flags</span></span><br><span class="line">DirectoryFlag DirectoryFlag::<span class="keyword">operator</span>+(uint8 flags) &#123;</span><br><span class="line"> uint8 newAttr = _attr | flags;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">DirectoryFlag</span>(newAttr);</span><br><span class="line">&#125;</span><br><span class="line">DirectoryFlag&amp; DirectoryFlag::<span class="keyword">operator</span>+=(uint8 flags) &#123;</span><br><span class="line"> _attr |= flags;</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Remove flags</span></span><br><span class="line">DirectoryFlag DirectoryFlag::<span class="keyword">operator</span>-(uint8 flags) &#123;</span><br><span class="line"> uint8 newAttr = _attr &amp; ~(flags);</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">DirectoryFlag</span>(newAttr);</span><br><span class="line">&#125;</span><br><span class="line">DirectoryFlag&amp; DirectoryFlag::<span class="keyword">operator</span>-=(uint8 flags) &#123;</span><br><span class="line"> _attr &amp;= ~(flags);</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Bitwise or</span></span><br><span class="line">DirectoryFlag DirectoryFlag::<span class="keyword">operator</span>|(uint8 flags) &#123;</span><br><span class="line"> uint8 newAttr = _attr | flags;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">DirectoryFlag</span>(newAttr);</span><br><span class="line">&#125;</span><br><span class="line">DirectoryFlag&amp; DirectoryFlag::<span class="keyword">operator</span>|=(uint8 flags) &#123;</span><br><span class="line"> _attr |= flags;</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Bitwise and</span></span><br><span class="line">DirectoryFlag DirectoryFlag::<span class="keyword">operator</span>&amp;(uint8 flags) &#123;</span><br><span class="line"> uint8 newAttr = _attr &amp; flags;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">DirectoryFlag</span>(newAttr);</span><br><span class="line">&#125;</span><br><span class="line">DirectoryFlag&amp; DirectoryFlag::<span class="keyword">operator</span>&amp;=(uint8 flags) &#123;</span><br><span class="line"> _attr &amp;= flags;</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Reverse flags</span></span><br><span class="line">DirectoryFlag DirectoryFlag::<span class="keyword">operator</span>~() &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">DirectoryFlag</span>(_attr);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Toggle flag bits</span></span><br><span class="line">DirectoryFlag&amp; DirectoryFlag::<span class="keyword">operator</span>^=(uint8 flags) &#123;</span><br><span class="line"> _attr ^= flags;</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Judge whether flags are contained</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">DirectoryFlag::contains</span><span class="params">(uint8 flags)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> (_attr &amp; flags) == flags;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>之后就可以根据上述表格完成<code>DirEntry</code>类的定义和实现了。需要注意的是,由于要从跟日期和时间有关的域中解析出对应的年月日或是时分秒仍然需要一步操作,所以我将条目中用到的时间和日期又额外包装成<code>FATDate</code><code>FATTime</code>放在<code>fatdt.h</code>中,并在这两个类中提供解析出年月日和时分秒的接口,在<code>DirEntry</code>的接口中不在返回裸的数值,而是将其转换为这两个类的实例进行返回。</p>
<p>我所实现的<code>fatdt.h</code>文件内容如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;types.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> _FAT_DT_H_</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _FAT_DT_H_</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">FATDate</span> &#123;</span><br><span class="line"> <span class="keyword">private</span>:</span><br><span class="line"> uint16 _date;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">FATDate</span>(uint16 date);</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function">uint16 <span class="title">year</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">setYear</span><span class="params">(uint16 year)</span></span>;</span><br><span class="line"> <span class="function">uint16 <span class="title">month</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">setMonth</span><span class="params">(uint16 month)</span></span>;</span><br><span class="line"> <span class="function">uint16 <span class="title">day</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">setDay</span><span class="params">(uint16 day)</span></span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">FATTime</span> &#123;</span><br><span class="line"> <span class="keyword">private</span>:</span><br><span class="line"> uint8 _timeTenth;</span><br><span class="line"> uint16 _time;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">FATTime</span>(uint8 timeTenth, uint16 time);</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function">uint16 <span class="title">hour</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">setHour</span><span class="params">(uint16 hour)</span></span>;</span><br><span class="line"> <span class="function">uint16 <span class="title">minute</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">setMinute</span><span class="params">(uint16 minute)</span></span>;</span><br><span class="line"> <span class="function">uint16 <span class="title">second</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">setSecond</span><span class="params">(uint16 second)</span></span>;</span><br><span class="line"> <span class="function">uint16 <span class="title">millisecond</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">setMillisecond</span><span class="params">(uint16 millisecond)</span></span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure>
<div class="note info flat"><p><strong>关于具体函数的实现,就留给读者自己完成。</strong></p>
</div>
<p>综上,可以完成<code>fatdir.h</code>文件如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;types.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;fatdt.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> _FAT_DIR_H_</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _FAT_DIR_H_</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> LAST_LONG_ENTRY 0x40</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DirectoryFlag</span>;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DirEntry</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DirectoryFlag</span> &#123;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="keyword">enum</span> <span class="title class_">ATTRIBUTES</span> &#123;</span><br><span class="line"> READ_ONLY = <span class="number">1</span>,</span><br><span class="line"> HIDDEN = <span class="number">1</span> &lt;&lt; <span class="number">1</span>,</span><br><span class="line"> SYSTEM = <span class="number">1</span> &lt;&lt; <span class="number">2</span>,</span><br><span class="line"> VOLUME_ID = <span class="number">1</span> &lt;&lt; <span class="number">3</span>,</span><br><span class="line"> DIRECTORY = <span class="number">1</span> &lt;&lt; <span class="number">4</span>,</span><br><span class="line"> ARCHIVE = <span class="number">1</span> &lt;&lt; <span class="number">5</span></span><br><span class="line"> &#125;;</span><br><span class="line"> <span class="keyword">private</span>:</span><br><span class="line"> uint8 _attr;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">DirectoryFlag</span>();</span><br><span class="line"> <span class="built_in">DirectoryFlag</span>(uint8 flags);</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> DirectoryFlag&amp; <span class="keyword">operator</span>=(uint8 flags);</span><br><span class="line"> DirectoryFlag&amp; <span class="keyword">operator</span>=(<span class="type">const</span> DirectoryFlag&amp; other);</span><br><span class="line"> <span class="comment">//Judge whether two flags are equal</span></span><br><span class="line"> <span class="type">bool</span> <span class="keyword">operator</span>==(uint8 flags);</span><br><span class="line"> <span class="comment">//Combine two flags</span></span><br><span class="line"> DirectoryFlag <span class="keyword">operator</span>+(uint8 flags);</span><br><span class="line"> DirectoryFlag&amp; <span class="keyword">operator</span>+=(uint8 flags);</span><br><span class="line"> <span class="comment">//Remove flags</span></span><br><span class="line"> DirectoryFlag <span class="keyword">operator</span>-(uint8 flags);</span><br><span class="line"> DirectoryFlag&amp; <span class="keyword">operator</span>-=(uint8 flags);</span><br><span class="line"> <span class="comment">//Bitwise or</span></span><br><span class="line"> DirectoryFlag <span class="keyword">operator</span>|(uint8 flags);</span><br><span class="line"> DirectoryFlag&amp; <span class="keyword">operator</span>|=(uint8 flags);</span><br><span class="line"> <span class="comment">//Bitwise and</span></span><br><span class="line"> DirectoryFlag <span class="keyword">operator</span>&amp;(uint8 flags);</span><br><span class="line"> DirectoryFlag&amp; <span class="keyword">operator</span>&amp;=(uint8 flags);</span><br><span class="line"> <span class="comment">//Reverse flags</span></span><br><span class="line"> DirectoryFlag <span class="keyword">operator</span>~();</span><br><span class="line"> <span class="comment">//Toggle flag bits</span></span><br><span class="line"> DirectoryFlag&amp; <span class="keyword">operator</span>^=(uint8 flags);</span><br><span class="line"> <span class="comment">//Judge whether flags are contained</span></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">contains</span><span class="params">(uint8 flags)</span></span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DirEntry</span> &#123;</span><br><span class="line"> <span class="keyword">private</span>:</span><br><span class="line"> byte _data[<span class="number">32</span>];</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">isEmpty</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">isSFNEntry</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">isLFNEntry</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">isLastInSet</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">DirectoryFlag <span class="title">attributes</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">FATTime <span class="title">createTime</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">FATDate <span class="title">createDate</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">FATDate <span class="title">lastAccessedDate</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">FATTime <span class="title">lastModifiedTime</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">FATDate <span class="title">lastModifiedDate</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">firstCluster</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">fileSize</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">lfnIndex</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">parseASCIIFileName</span><span class="params">(byte* dst)</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">checkSum</span><span class="params">()</span></span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure>
<p>完整的<code>fatdir.cpp</code>则实现如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;fatdir.h&quot;</span></span></span><br><span class="line"></span><br><span class="line">DirectoryFlag::<span class="built_in">DirectoryFlag</span>() &#123;</span><br><span class="line"> _attr = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line">DirectoryFlag::<span class="built_in">DirectoryFlag</span>(uint8 flags) &#123;</span><br><span class="line"> _attr = flags;</span><br><span class="line">&#125;</span><br><span class="line">DirectoryFlag&amp; DirectoryFlag::<span class="keyword">operator</span>=(uint8 flags) &#123;</span><br><span class="line"> _attr = flags;</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line">DirectoryFlag&amp; DirectoryFlag::<span class="keyword">operator</span>=(<span class="type">const</span> DirectoryFlag&amp; other) &#123;</span><br><span class="line"> _attr = other._attr;</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Judge whether two flags are equal</span></span><br><span class="line"><span class="type">bool</span> DirectoryFlag::<span class="keyword">operator</span>==(uint8 flags) &#123;</span><br><span class="line"> <span class="keyword">return</span> _attr == flags;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Combine two flags</span></span><br><span class="line">DirectoryFlag DirectoryFlag::<span class="keyword">operator</span>+(uint8 flags) &#123;</span><br><span class="line"> uint8 newAttr = _attr | flags;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">DirectoryFlag</span>(newAttr);</span><br><span class="line">&#125;</span><br><span class="line">DirectoryFlag&amp; DirectoryFlag::<span class="keyword">operator</span>+=(uint8 flags) &#123;</span><br><span class="line"> _attr |= flags;</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Remove flags</span></span><br><span class="line">DirectoryFlag DirectoryFlag::<span class="keyword">operator</span>-(uint8 flags) &#123;</span><br><span class="line"> uint8 newAttr = _attr &amp; ~(flags);</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">DirectoryFlag</span>(newAttr);</span><br><span class="line">&#125;</span><br><span class="line">DirectoryFlag&amp; DirectoryFlag::<span class="keyword">operator</span>-=(uint8 flags) &#123;</span><br><span class="line"> _attr &amp;= ~(flags);</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Bitwise or</span></span><br><span class="line">DirectoryFlag DirectoryFlag::<span class="keyword">operator</span>|(uint8 flags) &#123;</span><br><span class="line"> uint8 newAttr = _attr | flags;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">DirectoryFlag</span>(newAttr);</span><br><span class="line">&#125;</span><br><span class="line">DirectoryFlag&amp; DirectoryFlag::<span class="keyword">operator</span>|=(uint8 flags) &#123;</span><br><span class="line"> _attr |= flags;</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Bitwise and</span></span><br><span class="line">DirectoryFlag DirectoryFlag::<span class="keyword">operator</span>&amp;(uint8 flags) &#123;</span><br><span class="line"> uint8 newAttr = _attr &amp; flags;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">DirectoryFlag</span>(newAttr);</span><br><span class="line">&#125;</span><br><span class="line">DirectoryFlag&amp; DirectoryFlag::<span class="keyword">operator</span>&amp;=(uint8 flags) &#123;</span><br><span class="line"> _attr &amp;= flags;</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Reverse flags</span></span><br><span class="line">DirectoryFlag DirectoryFlag::<span class="keyword">operator</span>~() &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">DirectoryFlag</span>(_attr);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Toggle flag bits</span></span><br><span class="line">DirectoryFlag&amp; DirectoryFlag::<span class="keyword">operator</span>^=(uint8 flags) &#123;</span><br><span class="line"> _attr ^= flags;</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Judge whether flags are contained</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">DirectoryFlag::contains</span><span class="params">(uint8 flags)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> (_attr &amp; flags) == flags;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Check whether the entry is empty</span></span><br><span class="line"><span class="comment">//@return &#123;bool&#125; : True for the entry IS empty while False for the opposite</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">DirEntry::isEmpty</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">//According to Microsoft FAT specification:</span></span><br><span class="line"> <span class="comment">//DIR_Name[0] = 0xE5 indicates the directory entry is free</span></span><br><span class="line"> <span class="comment">//DIR_Name[0] = 0x00 indicates the directory entry and all directory entries following the current free entry are free</span></span><br><span class="line"> <span class="comment">//Since 0xE5 would never appear in LFN entries, therefore 0xE5 and 0x00 are unique signatures for empty entries</span></span><br><span class="line"> <span class="keyword">return</span> _data[<span class="number">0</span>] == <span class="number">0xE5</span> || _data[<span class="number">0</span>] == <span class="number">0x00</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Check whether the entry is a short filename entry</span></span><br><span class="line"><span class="comment">//@return &#123;bool&#125; : True for the entry IS a short filename entry while False for the opposite</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">DirEntry::isSFNEntry</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">//According to Microsoft FAT specification:</span></span><br><span class="line"> <span class="comment">//DIR_Attr field indicates whether the entry is a long filename entry</span></span><br><span class="line"> <span class="comment">//READ_ONLY | HIDDEN | SYSTEM | VOLUME_ID indicates a long filename entry</span></span><br><span class="line"> <span class="keyword">return</span> !<span class="built_in">attributes</span>().<span class="built_in">contains</span>(</span><br><span class="line"> DirectoryFlag::READ_ONLY | </span><br><span class="line"> DirectoryFlag::HIDDEN | </span><br><span class="line"> DirectoryFlag::SYSTEM | </span><br><span class="line"> DirectoryFlag::VOLUME_ID</span><br><span class="line"> );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Check whether the entry is a long filename entry</span></span><br><span class="line"><span class="comment">//@return &#123;bool&#125; : True for the entry IS a long filename entry while False for the opposite</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">DirEntry::isLFNEntry</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">//According to Microsoft FAT specification:</span></span><br><span class="line"> <span class="comment">//DIR_Attr field indicates whether the entry is a long filename entry</span></span><br><span class="line"> <span class="comment">//READ_ONLY | HIDDEN | SYSTEM | VOLUME_ID indicates a long filename entry</span></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">attributes</span>().<span class="built_in">contains</span>(</span><br><span class="line"> DirectoryFlag::READ_ONLY |</span><br><span class="line"> DirectoryFlag::HIDDEN |</span><br><span class="line"> DirectoryFlag::SYSTEM |</span><br><span class="line"> DirectoryFlag::VOLUME_ID</span><br><span class="line"> );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Check whether the entry is the last member in the lfn entry set</span></span><br><span class="line"><span class="comment">//@return &#123;bool&#125; : True for the entry IS the last member in the set while False for the opposite</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">DirEntry::isLastInSet</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">//According to Microsoft FAT specification:</span></span><br><span class="line"> <span class="comment">//The last long directory name entry in the set is masked with 0x40 in the LDIR_Ord field</span></span><br><span class="line"> <span class="keyword">return</span> (_data[<span class="number">0</span>] &amp; LAST_LONG_ENTRY) != <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Return the DIR_Attr field in the entry</span></span><br><span class="line"><span class="comment">//@return &#123;DirectoryFlag&#125; : The attributes of the entry in DirectoryFlag format</span></span><br><span class="line"><span class="function">DirectoryFlag <span class="title">DirEntry::attributes</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">//According to Microsoft FAT specification:</span></span><br><span class="line"> <span class="comment">//DIR_Attr field has an offset of 11 and size of 1 byte</span></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">DirectoryFlag</span>(_data[<span class="number">11</span>]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Return the creation time of the file/directory in FATTime format</span></span><br><span class="line"><span class="comment">//It should be noted that only SFN entries hold this field</span></span><br><span class="line"><span class="comment">//@return &#123;FATTime&#125; : The creation time, including hour, minute, second and millisecond</span></span><br><span class="line"><span class="function">FATTime <span class="title">DirEntry::createTime</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">//According to Microsoft FAT specification:</span></span><br><span class="line"> <span class="comment">//DIR_CrtTimeTenth has an offset of 13 and size of 1 byte</span></span><br><span class="line"> <span class="comment">//DIR_CrtTime has an offset of 14 and size of 2 bytes</span></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">FATTime</span>(_data[<span class="number">13</span>], *(uint16*)(&amp;_data[<span class="number">14</span>]));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Return the creation date of the file/directory in FATDate format</span></span><br><span class="line"><span class="comment">//It should be noted that only SFN entries hold this field</span></span><br><span class="line"><span class="comment">//@return &#123;FATDate&#125; : The creation date, including year, month and day</span></span><br><span class="line"><span class="function">FATDate <span class="title">DirEntry::createDate</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">//According to Microsoft FAT specification</span></span><br><span class="line"> <span class="comment">//DIR_CrtDate has an offset of 16 and size of 2 bytes</span></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">FATDate</span>(*(uint16*)(&amp;_data[<span class="number">16</span>]));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Return the last accessed date of the file/directory in FATDate format</span></span><br><span class="line"><span class="comment">//It should be noted that only SFN entries hold this field</span></span><br><span class="line"><span class="comment">//@return &#123;FATDate&#125; : The last accessed date, including year, month and day</span></span><br><span class="line"><span class="function">FATDate <span class="title">DirEntry::lastAccessedDate</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">//According to Microsoft FAT specification</span></span><br><span class="line"> <span class="comment">//DIR_LstAccDate has an offset of 18 and size of 2 bytes</span></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">FATDate</span>(*(uint16*)(&amp;_data[<span class="number">18</span>]));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Return the last modification time of the file/directory in FATTime format</span></span><br><span class="line"><span class="comment">//It should be noted that only SFN entries hold this field</span></span><br><span class="line"><span class="comment">//@return &#123;FATTime&#125; : The last modification time, including hour, minute, second and millisecond</span></span><br><span class="line"><span class="function">FATTime <span class="title">DirEntry::lastModifiedTime</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">//According to Microsoft FAT specification</span></span><br><span class="line"> <span class="comment">//Last modification time is stored in DIR_WrtTime field</span></span><br><span class="line"> <span class="comment">//DIR_WrtTime has an offset of 22 and size of 2 bytes</span></span><br><span class="line"> <span class="comment">//It should be noted that last modification time does not provide granularity under 2 seconds</span></span><br><span class="line"> <span class="comment">//More specifically, there is no timetenth field for last modification time</span></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">FATTime</span>(<span class="number">0</span>, *(uint16*)(&amp;_data[<span class="number">22</span>]));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Return the last modification date of the file/directory in FATDate format</span></span><br><span class="line"><span class="comment">//It should be noted that only SFN entries hold this field</span></span><br><span class="line"><span class="comment">//@return &#123;FATDate&#125; : The last modification date, including year, month and day</span></span><br><span class="line"><span class="function">FATDate <span class="title">DirEntry::lastModifiedDate</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">//According to Microsoft FAT specification</span></span><br><span class="line"> <span class="comment">//Last modification date is stored in DIR_WrtDate field</span></span><br><span class="line"> <span class="comment">//DIR_WrtDate has an offset of 24 and size of 2 bytes</span></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">FATDate</span>(*(uint16*)(&amp;_data[<span class="number">24</span>]));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Return the first cluster of the file/directory</span></span><br><span class="line"><span class="comment">//It should be noted that only SFN entries hold this field</span></span><br><span class="line"><span class="comment">//@return &#123;uint32&#125; : The first cluster of the file/directory</span></span><br><span class="line"><span class="function">uint32 <span class="title">DirEntry::firstCluster</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">//According to Microsoft FAT specification</span></span><br><span class="line"> <span class="comment">//first cluster is defined in DIR_FstClusHI and DIR_FstClusLO</span></span><br><span class="line"> <span class="comment">//This function simply concatenates the high and low bits and returns</span></span><br><span class="line"> uint16 frstClusterHI = *(uint16*)(&amp;_data[<span class="number">20</span>]);</span><br><span class="line"> uint16 frstClusterLO = *(uint16*)(&amp;_data[<span class="number">26</span>]);</span><br><span class="line"> uint32 frstCluster = ((uint32)frstClusterHI &lt;&lt; <span class="number">16</span>) | frstClusterLO;</span><br><span class="line"> <span class="keyword">return</span> frstCluster;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Return the size of the file/directory</span></span><br><span class="line"><span class="comment">//It should be noted that only SFN entries hold this field</span></span><br><span class="line"><span class="comment">//@return &#123;uint32&#125; : The size of the file/directory in bytes</span></span><br><span class="line"><span class="function">uint32 <span class="title">DirEntry::fileSize</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">//According to Microsoft FAT specification</span></span><br><span class="line"> <span class="comment">//The DIR_FileSize field holds the 32-bit quantity containing size in bytes of file/directory described by this entry</span></span><br><span class="line"> <span class="comment">//DIR_FileSize field has an offset of 28 and size of 4 bytes</span></span><br><span class="line"> <span class="keyword">return</span> *(uint32*)(&amp;_data[<span class="number">28</span>]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Return the order of the entry in the set of LFN entries</span></span><br><span class="line"><span class="comment">//It should be noted that only LFN entries hold this field</span></span><br><span class="line"><span class="comment">//@return &#123;uint32&#125; : The order of the entry</span></span><br><span class="line"><span class="function">uint32 <span class="title">DirEntry::lfnIndex</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">//According to Microsoft FAT specification:</span></span><br><span class="line"> <span class="comment">//The order of the entry is defined in LDIR_Ord field</span></span><br><span class="line"> <span class="comment">//LDIR_Ord field has an offset of 0 and size of 1 byte</span></span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> <span class="comment">//It should also be noted that the field can be masked with 0x40 if it&#x27;s the last entry in the set</span></span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> <span class="comment">//Therefore, bit 6 should be masked out during returing</span></span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> <span class="comment">//A pretty safe way is to return only the low 6 bits of this field</span></span><br><span class="line"> <span class="comment">//since the long filename would not exceed the 255 characters limit,</span></span><br><span class="line"> <span class="comment">//which equals to 255/13 = 20 = 0x14 entries, of which bit 5 is not even used for maximum length filename</span></span><br><span class="line"> <span class="keyword">return</span> _data[<span class="number">0</span>] &amp; <span class="number">0x3F</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Extract every first bit out of two bits of the filename</span></span><br><span class="line"><span class="comment">//transforming the filename into ASCII format and copy the transformed filename to dst</span></span><br><span class="line"><span class="comment">//It should be noted that this function BEHAVE DIFFERENTLY in SFN and LFN entries</span></span><br><span class="line"><span class="comment">//- in LFN entries: the filename is transformed into ASCII and copied to param dst</span></span><br><span class="line"><span class="comment">//- in SFN entries: the short filename is copied to param dst</span></span><br><span class="line"><span class="comment">//@param &#123;byte*&#125; dst : Destination memory where the filename will be copied to</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">DirEntry::parseASCIIFileName</span><span class="params">(byte* dst)</span> </span>&#123;</span><br><span class="line"> <span class="comment">//According to Microsoft FAT specification:</span></span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">isLFNEntry</span>()) &#123;</span><br><span class="line"> <span class="comment">//The filename in LFN entry is stored in three parts</span></span><br><span class="line"> <span class="comment">//LDIR_Name1 has an offset of 1 and size of 10 bytes</span></span><br><span class="line"> <span class="comment">//LDIR_Name2 has an offset of 14 and size of 12 bytes</span></span><br><span class="line"> <span class="comment">//LDIR_Name3 has an offset of 28 and size of 4 bytes</span></span><br><span class="line"> <span class="comment">//The file name is stored in UNICODE which is 16-bit per character</span></span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> <span class="comment">//Since only English words is used in the implementation</span></span><br><span class="line"> <span class="comment">//it&#x27;s okay to extract every frist bit out of two to transform UNICODE into ASCII</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; i++) &#123;</span><br><span class="line"> dst[i] = _data[<span class="number">1</span> + <span class="number">2</span> * i];</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">6</span>; i++) &#123;</span><br><span class="line"> dst[<span class="number">5</span> + i] = _data[<span class="number">14</span> + <span class="number">2</span> * i];</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">2</span>; i++) &#123;</span><br><span class="line"> dst[<span class="number">11</span> + i] = _data[<span class="number">28</span> + <span class="number">2</span> * i];</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="comment">//The filename in SFN entry is sotred in DIR_Name field</span></span><br><span class="line"> <span class="comment">//which as an offset of 0 and size of 11 bytes</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">11</span>; i++) &#123;</span><br><span class="line"> dst[i] = _data[i];</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Calculate/Return the checksum of the entry</span></span><br><span class="line"><span class="comment">//It should be noted that this function BEHAVE DIFFERENTLY in SFN and LFN entries</span></span><br><span class="line"><span class="comment">//@return &#123;uint32&#125; : Calculate checksum out of filename in SFN entries</span></span><br><span class="line"><span class="comment">//@return &#123;uint32&#125; : Return checksum in LFN entries</span></span><br><span class="line"><span class="function">uint32 <span class="title">DirEntry::checkSum</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">isSFNEntry</span>()) &#123;</span><br><span class="line"> <span class="comment">//ChkSum()</span></span><br><span class="line"> <span class="comment">//Returns an unsigned byte checksum computed on an unsigned byte</span></span><br><span class="line"> <span class="comment">//array. The array must be 11 bytes long and is assumed to contain</span></span><br><span class="line"> <span class="comment">//a name stored in the format of a MS-DOS directory entry</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">//The following method is copied from Microsoft FAT specification</span></span><br><span class="line"> <span class="comment">//page 32, chapter 7.2 Checksum generation</span></span><br><span class="line"> byte* pName = &amp;_data[<span class="number">0</span>];</span><br><span class="line"> uint16 nameLen;</span><br><span class="line"> uint8 sum;</span><br><span class="line"> sum = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span>(nameLen = <span class="number">11</span>; nameLen != <span class="number">0</span>; nameLen--) &#123;</span><br><span class="line"> <span class="comment">//<span class="doctag">NOTE:</span> The operation is an unsigned char rotate right</span></span><br><span class="line"> sum = ((sum &amp; <span class="number">1</span>) ? <span class="number">0x80</span> : <span class="number">0</span>) + (sum &gt;&gt; <span class="number">1</span>) + *pName++;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> sum;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="comment">//According to Microsoft FAT specification:</span></span><br><span class="line"> <span class="comment">//The checksum in LFN entries is stored in LDIR_Chksum field</span></span><br><span class="line"> <span class="comment">//which has an offset of 13 and size of 1 byte</span></span><br><span class="line"> <span class="keyword">return</span> _data[<span class="number">13</span>];</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="抽象MBR结构">抽象MBR结构</h2>
<p>上一小节中我们实现了FAT文件系统的抽象为从文件系统中读取内核文件奠定了基础。对于一个仅有一个分区的硬盘而言如果其文件系统恰是FAT格式那么就可以使用我们抽象出的接口访问其中的文件。</p>
<p>但现如今磁盘中只有一个分区的电脑少之又少如果磁盘中有多个FAT格式的分区那么加载程序应该去哪个分区寻找内核文件又该如何定位到对应分区的位置呢</p>
<p>这些就要依赖于存储在MBR中的数据了。在<a href="http://blog.linloir.cn/2022/07/16/os-journal-vol-2/#%E8%AE%A1%E7%AE%97%E6%9C%BA%E6%98%AF%E5%A6%82%E4%BD%95%E5%90%AF%E5%8A%A8%E7%9A%84">第二章:计算机是如何启动的</a>一节中提到MBR在447~510这64个字节中存储了分区表信息包括了分区的引导标志、起始磁道、起始扇区、起始柱面等信息。</p>
<div class="note info flat"><p><strong>实验中使用的是MBR分区表</strong><br>
在实验中使用的是MBR分区表而不是GPT分区表所以通过MBR来解析分区信息。<br>
在MBR分区表下最多只能存在四个主分区这是由MBR存储分区表区域的空间限制所导致的更多的分区则需要使用逻辑分区来表示关于逻辑分区的内容与本次实验无关故此处不进行介绍。</p>
</div>
<p>通过这些特殊的信息,加载程序就可以判断哪个分区是活动的,以及这个分区在磁盘上的位置,以及分区上文件系统的类型。在确定了这些信息之后,加载程序就可以使用上一节中关于文件系统的接口来读取和加载内核文件了。</p>
<p>在经历了对文件系统的抽象之后此时大家应该很快就能想到为了能够方便地解析MBR中分区表的数据可以对MBR的结构进行抽象。</p>
<h3 id="MBR结构">MBR结构</h3>
<div class="note blue icon-padding flat"><i class="note-icon fas fa-external-link-square"></i><p><strong>参考资料MBR结构</strong><br>
关于MBR结构的详细信息此文中参考了 <a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Master_boot_record">Wikipedia中关于MBR的页面</a> 其中实验采用了Classical generic MBR的格式对MBR结构进行解析。</p>
</div>
<p>在最一般的MBR结构中其包含了三个主要的部分</p>
<ul>
<li><strong>计算器启动代码</strong> <em>(Bootstrap code)</em>这部分空间用来存储计算机启动后要执行的第一段操作系统代码也就是我们在第二章中实现的用来启动BootLoader的代码</li>
<li><strong>分区表</strong> <em>(Partition table)</em>:这部分空间由四个分区目录条目 <em>(Partition entry)</em> 组成,用来描述磁盘中的分区信息</li>
<li><strong>启动签名</strong> <em>(Boot signature)</em>:一般为<code>0xAA55</code>用来向BIOS表示磁盘是可启动的</li>
</ul>
<p>它们的地址和大小如下:</p>
<table>
<thead>
<tr>
<th style="text-align:center">内容</th>
<th style="text-align:center">起始地址</th>
<th style="text-align:center">大小</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><strong>启动代码</strong></td>
<td style="text-align:center">0x0000</td>
<td style="text-align:center">446B</td>
</tr>
<tr>
<td style="text-align:center"><strong>分区条目1</strong><br/><em>Partition entry №1</em></td>
<td style="text-align:center">0x01BE</td>
<td style="text-align:center">16B</td>
</tr>
<tr>
<td style="text-align:center"><strong>分区条目2</strong><br/><em>Partition entry №2</em></td>
<td style="text-align:center">0x01CE</td>
<td style="text-align:center">16B</td>
</tr>
<tr>
<td style="text-align:center"><strong>分区条目3</strong><br/><em>Partition entry №3</em></td>
<td style="text-align:center">0x01DE</td>
<td style="text-align:center">16B</td>
</tr>
<tr>
<td style="text-align:center"><strong>分区条目4</strong><br/><em>Partition entry №4</em></td>
<td style="text-align:center">0x01EE</td>
<td style="text-align:center">16B</td>
</tr>
<tr>
<td style="text-align:center"><strong>启动签名</strong></td>
<td style="text-align:center">0x01FE</td>
<td style="text-align:center">2B</td>
</tr>
</tbody>
</table>
<h3 id="Partition-Entry结构">Partition Entry结构</h3>
<p>同时,每个分区条目 <em>(Partition entry)</em> 又具有如下的结构:</p>
<table>
<thead>
<tr>
<th style="text-align:center">偏移</th>
<th style="text-align:center">大小</th>
<th style="text-align:center">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">0x00</td>
<td style="text-align:center">1</td>
<td style="text-align:center">描述<strong>分区的状态</strong><br/><code>0x80</code>用来表示活动分区<br/><code>0x00</code>表示非活动分区<br/>其他值均为无效值</td>
</tr>
<tr>
<td style="text-align:center">0x01</td>
<td style="text-align:center">3</td>
<td style="text-align:center">分区<strong>首扇区的CHS表示</strong></td>
</tr>
<tr>
<td style="text-align:center">0x04</td>
<td style="text-align:center">1</td>
<td style="text-align:center">分区的<strong>类型</strong></td>
</tr>
<tr>
<td style="text-align:center">0x05</td>
<td style="text-align:center">3</td>
<td style="text-align:center">分区<strong>末扇区的CHS表示</strong></td>
</tr>
<tr>
<td style="text-align:center">0x08</td>
<td style="text-align:center">4</td>
<td style="text-align:center">分区<strong>首扇区的LBA扇区号</strong></td>
</tr>
<tr>
<td style="text-align:center">0x0C</td>
<td style="text-align:center">4</td>
<td style="text-align:center">分区<strong>总扇区数</strong></td>
</tr>
</tbody>
</table>
<p>其中,根据 <a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Partition_type">Wikipedia上关于分区类型的页面</a> 中的描述,实验中将会用到的分区类型以及它们的值为:</p>
<table>
<thead>
<tr>
<th style="text-align:center">类型</th>
<th style="text-align:center"></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><strong>FAT12</strong></td>
<td style="text-align:center">0x01</td>
</tr>
<tr>
<td style="text-align:center"><strong>FAT16</strong></td>
<td style="text-align:center">0x04</td>
</tr>
<tr>
<td style="text-align:center"><strong>FAT32(CHS)</strong></td>
<td style="text-align:center">0x0B</td>
</tr>
<tr>
<td style="text-align:center"><strong>FAT32(LBA)</strong></td>
<td style="text-align:center">0x0C</td>
</tr>
</tbody>
</table>
<p>同时扇区号的CHS表示又具有如下的结构</p>
<table>
<thead>
<tr>
<th style="text-align:center">偏移</th>
<th style="text-align:center">大小</th>
<th style="text-align:center">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">0x00</td>
<td style="text-align:center">1</td>
<td style="text-align:center"><strong>磁头号</strong> <em>(head)</em></td>
</tr>
<tr>
<td style="text-align:center">0x01</td>
<td style="text-align:center">1</td>
<td style="text-align:center">由两部分组成:<br/><strong>高2位为柱面</strong> <em>(cylinder)</em> <strong>第8-9位</strong><br/><strong>低6位为扇区号</strong> <em>(sector)</em> <strong>第0-5位</strong></td>
</tr>
<tr>
<td style="text-align:center">0x02</td>
<td style="text-align:center">1</td>
<td style="text-align:center"><strong>柱面</strong> <em>(cylinder)</em> <strong>第0-7位</strong></td>
</tr>
</tbody>
</table>
<p>可见,对于一个分区条目而言,其主要的作用就是<strong>判断分区是否存在</strong><strong>判断分区的状态(是否是活动分区)</strong><strong>标记分区的文件系统类型</strong>以及<strong>获取分区的首扇区号和扇区数</strong>,因此其接口就主要围绕这些方面进行设计:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">isPresent</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">isActive</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">firstSectorLBA</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">sectorNum</span><span class="params">()</span></span>;</span><br></pre></td></tr></table></figure>
<h3 id="抽象Partition-Entry">抽象Partition Entry</h3>
<p>明确了分区条目的结构和需要提供的接口,可以迅速将分区条目抽象为<code>Partition</code>类型如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Partition</span> &#123;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="keyword">enum</span> <span class="title class_">Status</span> &#123;</span><br><span class="line"> INACTIVE = <span class="number">0</span>,</span><br><span class="line"> ACTIVE = <span class="number">1</span> &lt;&lt; <span class="number">7</span></span><br><span class="line"> &#125;;</span><br><span class="line"> <span class="keyword">enum</span> <span class="title class_">Type</span> &#123;</span><br><span class="line"> EMPTY = <span class="number">0</span>,</span><br><span class="line"> FAT12 = <span class="number">0x01</span>,</span><br><span class="line"> FAT16 = <span class="number">0x04</span>,</span><br><span class="line"> FAT32_CHS = <span class="number">0x0B</span>,</span><br><span class="line"> FAT32_LBA = <span class="number">0x0C</span>,</span><br><span class="line"> &#125;;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> uint8 _status; <span class="comment">//Status for bootable</span></span><br><span class="line"> CHSAddress _frstSecCHS; <span class="comment">//CHS address of first absolute sector in partition</span></span><br><span class="line"> uint8 _type; <span class="comment">//Partition type</span></span><br><span class="line"> CHSAddress _lastSecCHS; <span class="comment">//CHS address of last absolute sector in partition</span></span><br><span class="line"> byte _frstSecLBA[<span class="number">4</span>]; <span class="comment">//LBA address of first absolute sector in partition</span></span><br><span class="line"> byte _numSec[<span class="number">4</span>]; <span class="comment">//Number of sector in the partition</span></span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">isPresent</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">isActive</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">firstSectorLBA</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">sectorNum</span><span class="params">()</span></span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>上述接口则均实现在<code>partition.cpp</code>文件中:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;partition.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">Partition::isPresent</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">sectorNum</span>() != <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">Partition::isActive</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> _status == ACTIVE;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">uint32 <span class="title">Partition::firstSectorLBA</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> *(uint32*)_frstSecLBA;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">uint32 <span class="title">Partition::sectorNum</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> *(uint32*)_numSec;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>同时在分区条目的结构中涉及到的CHS扇区号由上文中提到的CHS结构抽象为<code>CHSAddress</code>类型,其只需要提供<strong>从3字节长度的数据中提取柱面、磁头和扇区号</strong>的接口:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">CHSAddress</span> &#123;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> byte address[<span class="number">3</span>];</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function">uint32 <span class="title">cylinder</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">head</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">sector</span><span class="params">()</span></span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<div class="note info flat"><p><strong>注:</strong><br>
由于在实验中实际上并不会通过CHS的地址来计算扇区号因此在实验中并没有对这三个接口进行具体的实现这部分内容就留给读者完成。</p>
</div>
<h3 id="抽象MBR">抽象MBR</h3>
<p>实现了对分区条目的抽象之后对MBR结构的抽象就易如反掌了因为其实际上就是由<strong>一段长度为446的字节数组</strong> <em>(Bootstrap code)</em><strong>四个分区条目类型的对象</strong> <em>(Partition table)</em> 以及一个<strong>两字节的数组</strong> <em>(Boot signature)</em> 组成的:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//mbr.h</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;types.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;partition.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> _MBR_H_</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _MBR_H_</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MBR</span> &#123;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> byte bootstrap[<span class="number">446</span>]; <span class="comment">//Bootstrap code</span></span><br><span class="line"> Partition partition_1; <span class="comment">//Partition entry #1</span></span><br><span class="line"> Partition partition_2; <span class="comment">//Partition entry #2</span></span><br><span class="line"> Partition partition_3; <span class="comment">//Partition entry #3</span></span><br><span class="line"> Partition partition_4; <span class="comment">//Partition entry #4</span></span><br><span class="line"> byte signature[<span class="number">2</span>]; <span class="comment">//Bootable signature</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure>
<p>由于<code>Partition</code>类型的对象已经提供了足够使用的接口因此MBR并不需要再设计额外的接口而只需要将四个<code>Partition</code>的变量暴露给程序即可。如果程序想要从MBR中获得一个分区是否是活动的以及其起始的LBA扇区号其可以进行如下的操作</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Assume there is a MBR typed variable mbr</span></span><br><span class="line"><span class="comment">//假设已经有一个MBR类型的对象mbr</span></span><br><span class="line">Partition p = mbr.partition_1;</span><br><span class="line"><span class="comment">//Check the status of the partition</span></span><br><span class="line"><span class="comment">//检查分区是否是活动的</span></span><br><span class="line"><span class="keyword">if</span>(p.<span class="built_in">isActive</span>())&#123;</span><br><span class="line"> <span class="comment">//Do something if the partition is active</span></span><br><span class="line"> <span class="comment">//如果分区是活动的,则会执行的代码</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Get the LBA of first sector</span></span><br><span class="line"><span class="comment">//获取分区第一个扇区的LBA扇区号</span></span><br><span class="line">lba = p.<span class="built_in">firstSectorLBA</span>(); </span><br></pre></td></tr></table></figure>
<h2 id="抽象页表">抽象页表</h2>
<p>实现了MBR和文件系统的抽象就意味着现在我们已经可以从硬盘中找到需要的内核文件了并且借助硬盘读取的接口将内核的文件从硬盘中读取到内存。下一步就是将内核加载到正确的地址空间中完成加载内核的全部步骤。但是在开始加载内核之前需要先了解虚拟内存的概念以及分页机制的原理并且实现对分页要用到的数据结构进行抽象后才能正确地对内核进行加载。</p>
<h3 id="简述虚拟内存与分页机制">简述虚拟内存与分页机制</h3>
<div class="note info flat"><p><strong>参考资料</strong><br>
如果想要了解关于内存管理策略以及虚拟内存的全部内容,可以阅读课本《操作系统概念》中第三部分关于内存管理的内容,我强烈建议先完成这部分的阅读再继续后面的实验。<br>
当然,在接下来的相当一部分篇幅中,我也会用我自己的理解介绍什么是虚拟内存,以及分页机制的最基本概念。但这些远不如书本全面、权威,因此再次建议大家先对课本进行一次略读后再继续后面的实验内容。</p>
</div>
<p>既然要介绍虚拟内存和分页机制,自然就要从它们出现的原因谈起,而这就绕不开 <em>“程序是如何运行起来的”</em> 这个话题。</p>
<p>在学完了『操作系统原理』这门课后想必已经对程序的运行有了一个大致的认识知道了程序运行的基础是CPU对机器指令的执行而CPU执行机器指令由是由数个流水级逐步完成的不考虑分支预测等等复杂的实现CPU对指令的执行可以抽象为以下的步骤</p>
<ol>
<li>存储指令地址的存储器可以视作PC提供了一个地址信号输入存储指令的存储器在这里就是内存了</li>
<li>内存根据输入的地址信号在下一个时钟沿输出对应地址空间上存储的指令</li>
<li>其他流水级完成指令的解析等,同时指令地址完成一次自增</li>
</ol>
<p>可见,想让程序运行起来,关键就是两步:</p>
<ol>
<li>把指令存到内存中</li>
<li>把指令的地址放置到存储指令地址的存储器PC</li>
</ol>
<p>这样在下一个时钟周期到来的时候CPU就会从想要执行的指令处开始执行了。</p>
<p>把指令的地址放置到PC中就十分简单了在汇编中常见的跳转指令都可以完成这一操作在硬件上的实现想必大家也是历历在目的小声那剩下还要解决的就是把指令存到内存中这个问题啦。</p>
<p>但偏偏就是把指令存到内存中这么一个看起来很简单的操作,背后却暗藏了各种难题,为了这么一个操作,曾经的工程师们也可谓是八仙过海,各显神通。</p>
<p>还记得我们在<a href="https://blog.linloir.cn/2022/08/08/os-journal-vol-4/#%E4%BB%8E%E4%BB%A3%E7%A0%81%E5%88%B0%E5%8F%AF%E6%89%A7%E8%A1%8C%E6%96%87%E4%BB%B6-212">第二部分:从代码到可执行文件</a>一节中提到的程序的链接步骤中提到,在链接时需要指定程序起始的地址,这个地址关系到了整个程序所有代码段中使用到的绝对地址的计算,这就为程序的加载带来了一个大难题:</p>
<div class="note gray icon-padding flat"><i class="note-icon fas fa-question-circle"></i><p><strong>如果我不知道程序要加载到哪里,那我应当如何指定程序起始的地址?</strong><br>
<strong>如果强行提前指定了程序的起始地址,那个地址被其他的程序占用了又该怎么办?</strong></p>
</div>
<p>于是,曾经伟大的工程师们就提出了一个概念:<strong>让程序视角下的地址和它真正运行着的地址分开</strong>。通俗点解释,就是令程序在执行的时候中,其指令中的各种地址都视为程序视角下的地址,通过一些手段,让这个地址和真正访问内存的地址不是同一个地址,但又存在着一定的映射关系:</p>
<p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>A</mi><mi>c</mi><mi>c</mi><mi>e</mi><mi>s</mi><mi>s</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mo>=</mo><mi>f</mi><mo stretchy="false">(</mo><mi>P</mi><mi>r</mi><mi>o</mi><mi>g</mi><mi>r</mi><mi>a</mi><mi>m</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">AccessAddress = f(ProgramAddress)
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">A</span><span class="mord mathnormal">ccess</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">ro</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">am</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mclose">)</span></span></span></span></span></p>
<p>结合才学没多久的计算机组成原理课程的知识,可以大致理解为,<strong>在从指令中分离出地址后并不是直接送入内存,而是经过一个特殊的地址变换部件 <em>(这也就是常说的MMU)</em> 之后才会送入内存</strong></p>
<p><img src="/img/os-journal-vol-4/IMG_20220817-203133597.png" alt="图 1"></p>
<div class="note info flat"><p><strong>什么是程序视角下的地址:</strong></p>
<p>就像上图当程序在运行的时候CPU会根据指令生成出一些地址的值例如<code>mov eax, dword [ebx]</code>会从内存中加载4字节数据其产生的地址是<code>ebx</code>中存储的值;或是<code>jmp 0x7E00</code>会跳转到<code>0x7E00</code>处执行CPU会产生<code>0x7E00</code>这个地址送入指令地址寄存器。</p>
<p>然而CPU所不断地产生出的地址的值还需要经过地址变换部件才能成为有意义的地址。<strong>对于CPU而言它并不知道MMU的存在它只知道指令生成了一些地址而且在运行的过程中这些地址确实产生了正确的效果看起来就像是程序真的就放在这个地址上运行一样</strong>而不知道这些地址实际上是由于MMU的地址变换作用才产生了正确的效果所以这实际上这些未经变换都可以被称作是CPU“假想”的地址也就是常说的<strong>虚拟地址</strong>,我个人喜欢将它们称为是<strong>程序视角下的地址</strong>,我认为这样说会更形象、更易于理解。</p>
<p><strong>由于程序的视角其实通俗理解就是CPU的视角所以在程序执行过程中由CPU生成的地址其实就是程序视角下的地址</strong></p>
<p><strong>这些地址是虚拟的,是假定的</strong>,因为它们并不能代表程序在内存中存放的地址,<strong>这一点很重要</strong></p>
</div>
<div class="note red icon-padding flat"><i class="note-icon fas fa-exclamation-circle"></i><p><strong>陷阱:程序某个部分在内存中的地址≠程序视角中这个部分的地址</strong><br>
要理解后面的分页制度,就一定要有<strong>程序视角的地址是“假想”的地址,是程序所认为的它在内存中的位置</strong>的概念。<br>
在出现了地址变换部件这个概念之后,<strong>程序在运行的时候由CPU生成的所有地址都不再一定是CPU想要从内存中获取的数据的地址</strong>而是要经由一次MMU的映射才能获得程序想从内存中获取的数据的地址。</p>
</div>
<p>这样,即便程序都以<code>0x0</code>为起始地址,也可以通过不同的地址变换方式来达到将程序存储在内存中不同位置的效果。</p>
<!-- Place a picture here -->
<p>但是摆在工程师面前的,还有更多的问题:</p>
<div class="note gray icon-padding flat"><i class="note-icon fas fa-question-circle"></i><p><strong>如何从空闲的空间中选择合适的位置存储需要加载的程序?</strong></p>
</div>
<p>在操作系统运行的过程中,会不断有程序需要被加载到内存运行,同时也会不断地有程序结束运行。结束运行的程序可以释放出它本身占用的空间,需要加载运行的程序则需要申请能够存放其自身的空间。如此往复的过程中,不断被申请然后被释放的程序所占用的内存空间会让内存的地址空间中出现一个个的空洞 <em>(Hole)</em>,原先连续的大片空闲区域就这样被分割成了不连续的许多小的空闲的内存片段:</p>
<!-- Place a picture here -->
<p>这听起来就不是什么好事,事实上也确实是如此,当操作系统要加载一个新的程序进入内存的时候,它几乎一定面对的是一个“满目疮痍”的内存空间,而如何从无数的空洞中选择合适的那个来存放程序,也就成了一个难题。过去的工程师提出了三种解决方案:</p>
<ul>
<li><strong>最优适应</strong>:一种强迫症式的挑选方法,非得<strong>找到所有足够装下程序的空洞中最小的那个</strong>,在欧皇时刻这样挑恰好就能放进去,但是非酋时刻则会产生一个非常小的空洞,导致最后谁都放不进去,白白浪费。</li>
<li><strong>最差适应</strong>:一种走极端的挑选方法,你说最优适应会产生小空洞让谁都放不进去,那行,我就<strong>找到所有足够装下程序的空洞中最大的那个</strong>,在这种情况下,一般剩下来的空洞还是能够再利用的。</li>
<li><strong>首次适应</strong>:一种摆烂式的挑选方法,<strong>只要找到了能装下的空洞,啥也不管了,冲就对了,奥里给</strong>!但你说妙不妙,这样的策略竟然反倒<strong>在空间和时间方面都胜最差适应一筹,和最优适应难分伯仲,甚至还更快些</strong><s>再次证明了开摆就对了!</s></li>
</ul>
<!-- Place a picture here -->
<div class="note info flat"><p><strong>碎片:</strong></p>
<p>在上面的描述的三种为程序的分配内存的方式中可以看出并不是每一次都能够为程序分配恰到好处的空间,往往一个新程序的加载会导致内存中出现小的空洞,这些空洞小到几乎不足以放下任何其他的程序,这就意味着这部分内存直到程序运行完毕被释放之前都不能够被操作系统用来为其他程序分配,<strong>这些实际上被浪费掉的内存空间就是碎片</strong></p>
<p>碎片又<strong>分为外部碎片和内部碎片</strong>,前述的这种在<strong>为程序分配的空间以外的浪费空间叫做外部碎片</strong>,而<strong>如果操作系统为一个程序分配了多余它实际要使用的内存空间,那么其内部被浪费掉的空间就叫做内部碎片</strong>,这种类型的碎片将会在后面介绍分页机制时看到。</p>
<p>碎片对于内存空间的利用而言是致命的,试想一个充满了碎片的内存,明明其空闲的空间足够大,但是由于全部都是由碎片组成,而不能放入任何一个程序。</p>
</div>
<p>然而,上面的三种分配方式都仍然是将一个程序视为一个整体进行加载的,多少有点偏执了。</p>
<p>假设内存中有 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>4</mn></mrow><annotation encoding="application/x-tex">4</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">4</span></span></span></span> 个大小为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>16</mn><mi>K</mi><mi>i</mi><mi>B</mi></mrow><annotation encoding="application/x-tex">16KiB</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">16</span><span class="mord mathnormal" style="margin-right:0.07153em;">K</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span> 大小的空洞,需要加载一个大小为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>60</mn><mi>K</mi><mi>i</mi><mi>B</mi></mrow><annotation encoding="application/x-tex">60KiB</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">60</span><span class="mord mathnormal" style="margin-right:0.07153em;">K</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span> 大小的程序,此时不论采用上述哪一种分配方式,都无法找到任何一个合适的空洞来装载新的程序。如果此时摒弃非要把一个程序连续装入内存的落后思想,而是想办法把程序剁成四块,分别为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>16</mn><mi>K</mi><mi>i</mi><mi>B</mi></mrow><annotation encoding="application/x-tex">16KiB</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">16</span><span class="mord mathnormal" style="margin-right:0.07153em;">K</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>16</mn><mi>K</mi><mi>i</mi><mi>B</mi></mrow><annotation encoding="application/x-tex">16KiB</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">16</span><span class="mord mathnormal" style="margin-right:0.07153em;">K</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>16</mn><mi>K</mi><mi>i</mi><mi>B</mi></mrow><annotation encoding="application/x-tex">16KiB</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">16</span><span class="mord mathnormal" style="margin-right:0.07153em;">K</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>12</mn><mi>K</mi><mi>i</mi><mi>B</mi></mrow><annotation encoding="application/x-tex">12KiB</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">12</span><span class="mord mathnormal" style="margin-right:0.07153em;">K</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span> ,然后装入四个空洞中,不就成了吗?</p>
<!-- Place a picture here -->
<p>聪明的工程师自然也想到了,所以<strong>分页</strong>的概念也就应运而生。</p>
<p>为了方便操作系统进行管理,工程师将内存和程序都按照一个相同的大小进行分割,例如常见的 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>4</mn><mi>K</mi><mi>i</mi><mi>B</mi></mrow><annotation encoding="application/x-tex">4KiB</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">4</span><span class="mord mathnormal" style="margin-right:0.07153em;">K</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span>,这个固定大小的空间就叫做页,将内存和程序分割的过程也就叫做分页。</p>
<p>对于内存而言,剩余不足一页的空间会被舍弃;对于程序而言,不足一页的部分也会分配一个完整的页,这时页中没有被使用的部分就是内部碎片,例如如果程序的大小为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>4</mn><mi>K</mi><mi>i</mi><mi>B</mi><mo>+</mo><mn>1</mn><mi>B</mi></mrow><annotation encoding="application/x-tex">4KiB + 1B</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7667em;vertical-align:-0.0833em;"></span><span class="mord">4</span><span class="mord mathnormal" style="margin-right:0.07153em;">K</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">1</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span>,而页的大小为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>4</mn><mi>K</mi><mi>i</mi><mi>B</mi></mrow><annotation encoding="application/x-tex">4KiB</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">4</span><span class="mord mathnormal" style="margin-right:0.07153em;">K</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span>,则程序会占用两个页,即使第二个页中只有一个字节是真正被使用的,这就产生了 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>4095</mn><mi>B</mi></mrow><annotation encoding="application/x-tex">4095B</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">4095</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span> 的内部碎片。</p>
<div class="note red icon-padding flat"><i class="note-icon fas fa-exclamation-circle"></i><p><strong>陷阱:对程序分页实质上是对程序地址空间进行分页</strong><br>
对程序分页是指的对程序视角下的地址空间进行分页,而不是对程序实际在内存中占用的地址空间进行分页,并且这之中还有一个先后的关系,程序只有先对它所用到的地址空间进行分页,才能对应地放入到内存中分好的页里。</p>
</div>
<div class="note info flat"><p><strong>物理页和虚拟页:</strong><br>
在后文中,物理页就将指代内存中的页,而虚拟页就将指代程序的虚拟地址空间中的页。</p>
</div>
<p>在内存进行了分页之后,往后内存空间的分配和释放都会以一页为最小单位,因此当需要加载新的程序进入内存时,如果内存中空闲的页不少于程序运行所需要的页,则为程序分配其所需要的数量的页,并且建立一个从程序视角下的页到实际内存中的页的映射关系</p>
<!-- Place a picture here -->
<p>有了这样的映射关系,操作系统就可以对应着将程序的内容装载进内存,至于在程序视角下的地址空间内那些空间需要写入内存、怎样写入内存等等将会留到<a href="#%E6%8A%BD%E8%B1%A1elf%E6%96%87%E4%BB%B6">下一小节</a>介绍。</p>
<p>但是这样复杂的映射关系CPU是如何记住的呢</p>
<p>这就要提到本章节的主角<strong>页表</strong>了。为了能够让MMU知道程序视角下的页与内存中页的映射关系早期的工程师创造了一个类似FAT表的表结构其中每个程序视角下的页都可以对应到一个表项而每个表项中的内容都对应到一个内存中的页这个对应关系使用内存页的首字节地址表示。</p>
<!-- Place a picture here -->
<p>不过CPU产生的地址肯定不会是页的大小的整数倍而是可能对应着一个页内的任何一个字节此时如果只有页的映射关系是否有些不够了呢</p>
<p>自然不是的。由于页在映射的时候是将一个页视为一个整体进行映射所以同一个内容在虚拟页和物理页中的偏移量是相同的在对CPU生成的地址进行变换的时候只需要将页的地址和偏移量分离开来通过页表得到物理页的地址以后再将其作为基址叠加上原先分离出的偏移量就可以得到映射后的地址了。</p>
<p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mtable rowspacing="0.25em" columnalign="right left" columnspacing="0em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>O</mi><mi>f</mi><mi>f</mi><mi>s</mi><mi>e</mi><mi>t</mi><mo>=</mo><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mtext> </mtext><mi mathvariant="normal">%</mi><mtext> </mtext><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>S</mi><mi>i</mi><mi>z</mi><mi>e</mi></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mo>=</mo><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mo></mo><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>O</mi><mi>f</mi><mi>f</mi><mi>s</mi><mi>e</mi><mi>t</mi></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mi>P</mi><mi>h</mi><mi>y</mi><mi>s</mi><mi>i</mi><mi>c</mi><mi>a</mi><mi>l</mi><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mo>=</mo><mi>f</mi><mo stretchy="false">(</mo><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mo stretchy="false">)</mo></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mi>P</mi><mi>h</mi><mi>y</mi><mi>s</mi><mi>i</mi><mi>c</mi><mi>a</mi><mi>l</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mo>=</mo><mi>P</mi><mi>h</mi><mi>y</mi><mi>s</mi><mi>i</mi><mi>c</mi><mi>a</mi><mi>l</mi><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mo>+</mo><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>O</mi><mi>f</mi><mi>f</mi><mi>s</mi><mi>e</mi><mi>t</mi></mrow></mstyle></mtd></mtr></mtable><annotation encoding="application/x-tex">\begin{align}
&amp;PageOffset = VirtualAddress \ \% \ PageSize \nonumber \\
&amp;VirtualPageAddress = VirtualAddress - PageOffset \nonumber \\
&amp;PhysicalPageAddress = f(VirtualPageAddress) \nonumber \\
&amp;PhysicalAddress = PhysicalPageAddress + PageOffset \nonumber
\end{align}
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:6em;vertical-align:-2.75em;"></span><span class="mord"><span class="mtable"><span class="col-align-r"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:3.25em;"><span style="top:-5.25em;"><span class="pstrut" style="height:2.84em;"></span><span class="mord"></span></span><span style="top:-3.75em;"><span class="pstrut" style="height:2.84em;"></span><span class="mord"></span></span><span style="top:-2.25em;"><span class="pstrut" style="height:2.84em;"></span><span class="mord"></span></span><span style="top:-0.75em;"><span class="pstrut" style="height:2.84em;"></span><span class="mord"></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:2.75em;"><span></span></span></span></span></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:3.25em;"><span style="top:-5.41em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mord mathnormal" style="margin-right:0.10764em;">ff</span><span class="mord mathnormal">se</span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mspace"> </span><span class="mord">%</span><span class="mspace"> </span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mord mathnormal">i</span><span class="mord mathnormal">ze</span></span></span><span style="top:-3.91em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.13889em;">lP</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin"></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mord mathnormal" style="margin-right:0.10764em;">ff</span><span class="mord mathnormal">se</span><span class="mord mathnormal">t</span></span></span><span style="top:-2.41em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">h</span><span class="mord mathnormal">ys</span><span class="mord mathnormal">i</span><span class="mord mathnormal">c</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.13889em;">lP</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mopen">(</span><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.13889em;">lP</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mclose">)</span></span></span><span style="top:-0.91em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">h</span><span class="mord mathnormal">ys</span><span class="mord mathnormal">i</span><span class="mord mathnormal">c</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">h</span><span class="mord mathnormal">ys</span><span class="mord mathnormal">i</span><span class="mord mathnormal">c</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.13889em;">lP</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mord mathnormal" style="margin-right:0.10764em;">ff</span><span class="mord mathnormal">se</span><span class="mord mathnormal">t</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:2.75em;"><span></span></span></span></span></span></span></span></span></span></span></span></p>
<p>不过,只是得到虚拟页的首字节地址还不够,既然要在页表中寻找对应关系,就需要知道虚拟页对应的下标,也就是虚拟页在所有页中的序号。由于页的大小都是 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mi>n</mi></msup></mrow><annotation encoding="application/x-tex">2^n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6644em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span></span></span></span></span></span></span> 形式,所以第 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span></span></span></span> 个虚拟页的地址有如下关系:</p>
<p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mtable rowspacing="0.25em" columnalign="right left" columnspacing="0em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><msub><mi>s</mi><mi>k</mi></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mi>k</mi><mo>×</mo><msup><mn>2</mn><mi>n</mi></msup></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mi>k</mi><mo>&lt;</mo><mo>&lt;</mo><mi>n</mi></mrow></mstyle></mtd></mtr></mtable><annotation encoding="application/x-tex">\begin{align}
VirtualPageAddress_k &amp;= k \times 2^n \nonumber \\
&amp;= k &lt;&lt; n \nonumber \\
\end{align}
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:3em;vertical-align:-1.25em;"></span><span class="mtable"><span class="col-align-r"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.75em;"><span style="top:-3.91em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.13889em;">lP</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">res</span><span class="mord"><span class="mord mathnormal">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span><span style="top:-2.41em;"><span class="pstrut" style="height:3em;"></span><span class="mord"></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:1.25em;"><span></span></span></span></span></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.75em;"><span style="top:-3.91em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7144em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span></span></span></span></span></span><span style="top:-2.41em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&lt;&lt;</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">n</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:1.25em;"><span></span></span></span></span></span></span></span><span class="tag"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.75em;"><span style="top:-3.75em;"><span class="pstrut" style="height:2.84em;"></span><span></span></span><span style="top:-2.25em;"><span class="pstrut" style="height:2.84em;"></span><span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:1.25em;"><span></span></span></span></span></span></span></span></span></p>
<p>所以对于一个虚拟页,其在页表中的下标就可以通过虚拟页的地址移位得到。对于大小为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mi>n</mi></msup></mrow><annotation encoding="application/x-tex">2^n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6644em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span></span></span></span></span></span></span> 的页,一个虚拟页对应的下标为</p>
<p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>I</mi><mi>n</mi><mi>d</mi><mi>e</mi><mi>x</mi><mo>=</mo><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mo>&gt;</mo><mo>&gt;</mo><mi>n</mi></mrow><annotation encoding="application/x-tex">VirtualPageIndex = VirtualPageAddress &gt;&gt; n
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.13889em;">lP</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.07847em;">I</span><span class="mord mathnormal">n</span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.13889em;">lP</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">n</span></span></span></span></span></p>
<p>代入上式可得</p>
<p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mtable rowspacing="0.25em" columnalign="right left" columnspacing="0em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>I</mi><mi>n</mi><mi>d</mi><mi>e</mi><mi>x</mi></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mo>&gt;</mo><mo>&gt;</mo><mi>n</mi></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mo stretchy="false">(</mo><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mo></mo><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>O</mi><mi>f</mi><mi>f</mi><mi>s</mi><mi>e</mi><mi>t</mi><mo stretchy="false">)</mo><mo>&gt;</mo><mo>&gt;</mo><mi>n</mi></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mo stretchy="false">(</mo><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mo></mo><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mtext> </mtext><mi mathvariant="normal">%</mi><mtext> </mtext><msup><mn>2</mn><mi>n</mi></msup><mo stretchy="false">)</mo><mo>&gt;</mo><mo>&gt;</mo><mi>n</mi></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mo stretchy="false">(</mo><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mo></mo><mo stretchy="false">(</mo><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mo></mo><mi>f</mi><mi>l</mi><mi>o</mi><mi>o</mi><mi>r</mi><mo stretchy="false">(</mo><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mo>÷</mo><msup><mn>2</mn><mi>n</mi></msup><mo stretchy="false">)</mo><mo>×</mo><msup><mn>2</mn><mi>n</mi></msup><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mo>&gt;</mo><mo>&gt;</mo><mi>n</mi></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mo stretchy="false">(</mo><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mo></mo><mo stretchy="false">(</mo><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mo></mo><mo stretchy="false">(</mo><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mo>&gt;</mo><mo>&gt;</mo><mi>n</mi><mo stretchy="false">)</mo><mo>&lt;</mo><mo>&lt;</mo><mi>n</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mo>&gt;</mo><mo>&gt;</mo><mi>n</mi></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mo>&gt;</mo><mo>&gt;</mo><mi>n</mi><mo>&lt;</mo><mo>&lt;</mo><mi>n</mi><mo>&gt;</mo><mo>&gt;</mo><mi>n</mi></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mo>&gt;</mo><mo>&gt;</mo><mi>n</mi></mrow></mstyle></mtd></mtr></mtable><annotation encoding="application/x-tex">\begin{align}
VirtualPageIndex &amp;= VirtualPageAddress &gt;&gt; n \nonumber \\
&amp;= (VirtualAddress - PageOffset) &gt;&gt; n \nonumber \\
&amp;= (VirtualAddress - VirtualAddress \ \% \ 2^n) &gt;&gt; n \nonumber \\
&amp;= (VirtualAddress - (VirtualAddress - floor(VirtualAddress \div 2^n) \times 2^n)) &gt;&gt; n \nonumber \\
&amp;= (VirtualAddress - (VirtualAddress - (VirtualAddress &gt;&gt; n) &lt;&lt; n)) &gt;&gt; n \nonumber \\
&amp;= VirtualAddress &gt;&gt; n &lt;&lt; n &gt;&gt; n \nonumber \\
&amp;= VirtualAddress &gt;&gt; n \nonumber
\end{align}
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:10.5em;vertical-align:-5em;"></span><span class="mord"><span class="mtable"><span class="col-align-r"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:5.5em;"><span style="top:-7.66em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.13889em;">lP</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.07847em;">I</span><span class="mord mathnormal">n</span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span><span class="mord mathnormal">x</span></span></span><span style="top:-6.16em;"><span class="pstrut" style="height:3em;"></span><span class="mord"></span></span><span style="top:-4.66em;"><span class="pstrut" style="height:3em;"></span><span class="mord"></span></span><span style="top:-3.16em;"><span class="pstrut" style="height:3em;"></span><span class="mord"></span></span><span style="top:-1.66em;"><span class="pstrut" style="height:3em;"></span><span class="mord"></span></span><span style="top:-0.16em;"><span class="pstrut" style="height:3em;"></span><span class="mord"></span></span><span style="top:1.34em;"><span class="pstrut" style="height:3em;"></span><span class="mord"></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:5em;"><span></span></span></span></span></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:5.5em;"><span style="top:-7.66em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.13889em;">lP</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">n</span></span></span><span style="top:-6.16em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mopen">(</span><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin"></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mord mathnormal" style="margin-right:0.10764em;">ff</span><span class="mord mathnormal">se</span><span class="mord mathnormal">t</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">n</span></span></span><span style="top:-4.66em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mopen">(</span><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin"></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mspace"> </span><span class="mord">%</span><span class="mspace"> </span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7144em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">n</span></span></span><span style="top:-3.16em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mopen">(</span><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin"></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mopen">(</span><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin"></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal" style="margin-right:0.02778em;">oor</span><span class="mopen">(</span><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">÷</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7144em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7144em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span></span></span></span><span class="mclose">))</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">n</span></span></span><span style="top:-1.66em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mopen">(</span><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin"></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mopen">(</span><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin"></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mopen">(</span><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">n</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&lt;&lt;</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">n</span><span class="mclose">))</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">n</span></span></span><span style="top:-0.16em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&lt;&lt;</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">n</span></span></span><span style="top:1.34em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">n</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:5em;"><span></span></span></span></span></span></span></span></span></span></span></span></p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>4</mn><mi>K</mi><mi>i</mi><mi>B</mi></mrow><annotation encoding="application/x-tex">4KiB</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">4</span><span class="mord mathnormal" style="margin-right:0.07153em;">K</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span> 大小的页为例,对于虚拟地址<code>0xC00002a0</code>,其<strong>高20位</strong><code>0xC0000</code>代表这个地址所在的<strong>页的序号</strong>,而<strong>低12位</strong><code>0x2a0</code>其实是代表着这个地址在<strong>页内部的偏移量</strong>。也即在程序的虚拟地址中,<code>0xC00002a0</code>地址位于第<code>0xC0000</code>页中第<code>0x2a0</code>个字节处。访问页表中下标为<code>0xC0000</code>的项,得到虚拟页对应的物理地址,例如<code>0x20000000</code>,再将物理地址和偏移量合并,得到映射后的物理地址<code>0x200002a0</code></p>
<!-- Place a picture here -->
<p>不过如果按照这样的方式映射对于一个32位的系统来说每一个虚拟页都需要4个字节来存储其对应的物理页的地址而32位的地址最多可以取到 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>4</mn><mi>G</mi><mi>i</mi><mi>B</mi></mrow><annotation encoding="application/x-tex">4GiB</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">4</span><span class="mord mathnormal">G</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span> 的地址范围,假设页的大小为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>4</mn><mi>K</mi><mi>i</mi><mi>B</mi></mrow><annotation encoding="application/x-tex">4KiB</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">4</span><span class="mord mathnormal" style="margin-right:0.07153em;">K</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span>,以 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">S(x)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span> 表示 <em><strong>x所占用的内存字节数</strong></em>,那么页表最多需要占用的内存计算如下:</p>
<p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mtable rowspacing="0.25em" columnalign="right left" columnspacing="0em"><mtr><mtd class ="mtr-glue"></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mi>S</mi><mo stretchy="false">(</mo><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>T</mi><mi>a</mi><mi>b</mi><mi>l</mi><mi>e</mi><mi>E</mi><mi>n</mi><mi>t</mi><mi>r</mi><mi>y</mi><mo stretchy="false">)</mo><mo>=</mo><mn>4</mn></mrow></mstyle></mtd><mtd class ="mtr-glue"></mtd><mtd class ="mml-eqn-num"></mtd></mtr><mtr><mtd class ="mtr-glue"></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mi>S</mi><mo stretchy="false">(</mo><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mi>S</mi><mi>p</mi><mi>a</mi><mi>c</mi><mi>e</mi><mo stretchy="false">)</mo><mo>=</mo><msup><mn>2</mn><mn>32</mn></msup></mrow></mstyle></mtd><mtd class ="mtr-glue"></mtd><mtd class ="mml-eqn-num"></mtd></mtr><mtr><mtd class ="mtr-glue"></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mi>S</mi><mo stretchy="false">(</mo><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mo stretchy="false">)</mo><mo>=</mo><msup><mn>2</mn><mn>12</mn></msup></mrow></mstyle></mtd><mtd class ="mtr-glue"></mtd><mtd class ="mml-eqn-num"></mtd></mtr><mtr><mtd class ="mtr-glue"></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo stretchy="false">(</mo><mn>2</mn><mo stretchy="false">)</mo><mo separator="true">,</mo><mo stretchy="false">(</mo><mn>3</mn><mo stretchy="false">)</mo><mo></mo><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>C</mi><mi>n</mi><mi>t</mi><mo>=</mo><mi>S</mi><mo stretchy="false">(</mo><mi>V</mi><mi>i</mi><mi>r</mi><mi>t</mi><mi>u</mi><mi>a</mi><mi>l</mi><mi>A</mi><mi>d</mi><mi>d</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi><mi>S</mi><mi>p</mi><mi>a</mi><mi>c</mi><mi>e</mi><mo stretchy="false">)</mo><mo>÷</mo><mi>S</mi><mo stretchy="false">(</mo><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mo stretchy="false">)</mo><mo>=</mo><msup><mn>2</mn><mn>20</mn></msup></mrow></mstyle></mtd><mtd class ="mtr-glue"></mtd><mtd class ="mml-eqn-num"></mtd></mtr><mtr><mtd class ="mtr-glue"></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo stretchy="false">(</mo><mn>4</mn><mo stretchy="false">)</mo><mo></mo><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>T</mi><mi>a</mi><mi>b</mi><mi>l</mi><mi>e</mi><mi>E</mi><mi>n</mi><mi>t</mi><mi>r</mi><mi>y</mi><mi>C</mi><mi>n</mi><mi>t</mi><mo>=</mo><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>C</mi><mi>n</mi><mi>t</mi><mo>=</mo><msup><mn>2</mn><mn>20</mn></msup></mrow></mstyle></mtd><mtd class ="mtr-glue"></mtd><mtd class ="mml-eqn-num"></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo><mo separator="true">,</mo><mo stretchy="false">(</mo><mn>5</mn><mo stretchy="false">)</mo><mo></mo><mi>S</mi><mo stretchy="false">(</mo><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>T</mi><mi>a</mi><mi>b</mi><mi>l</mi><mi>e</mi><mo stretchy="false">)</mo><mo>=</mo><mi>S</mi><mo stretchy="false">(</mo><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>T</mi><mi>a</mi><mi>b</mi><mi>l</mi><mi>e</mi><mi>E</mi><mi>n</mi><mi>t</mi><mi>r</mi><mi>y</mi><mo stretchy="false">)</mo><mo>×</mo><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>T</mi><mi>a</mi><mi>b</mi><mi>l</mi><mi>e</mi><mi>E</mi><mi>n</mi><mi>t</mi><mi>r</mi><mi>y</mi><mi>C</mi><mi>n</mi><mi>t</mi><mo>=</mo><msup><mn>2</mn><mn>22</mn></msup></mrow></mstyle></mtd></mtr></mtable><annotation encoding="application/x-tex">\begin{align}
&amp;S(PageTableEntry) = 4 \\
&amp;S(VirtualAddressSpace) = 2^{32} \\
&amp;S(Page) = 2^{12} \\
&amp;(2), (3) \Rightarrow PageCnt = S(VirtualAddressSpace) \div S(Page) = 2^{20} \\
&amp;(4) \Rightarrow PageTableEntryCnt = PageCnt = 2^{20} \\
&amp;(1),(5) \Rightarrow S(PageTable) = S(PageTableEntry) \times PageTableEntryCnt = 2^{22} \nonumber
\end{align}
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:9.1205em;vertical-align:-4.3103em;"></span><span class="mtable"><span class="col-align-r"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:4.8103em;"><span style="top:-6.8344em;"><span class="pstrut" style="height:2.8641em;"></span><span class="mord"></span></span><span style="top:-5.3103em;"><span class="pstrut" style="height:2.8641em;"></span><span class="mord"></span></span><span style="top:-3.7862em;"><span class="pstrut" style="height:2.8641em;"></span><span class="mord"></span></span><span style="top:-2.2621em;"><span class="pstrut" style="height:2.8641em;"></span><span class="mord"></span></span><span style="top:-0.7379em;"><span class="pstrut" style="height:2.8641em;"></span><span class="mord"></span></span><span style="top:0.7862em;"><span class="pstrut" style="height:2.8641em;"></span><span class="mord"></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:4.3103em;"><span></span></span></span></span></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:4.8103em;"><span style="top:-6.9703em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="mord mathnormal">ab</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.05764em;">E</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mord mathnormal" style="margin-right:0.03588em;">ry</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord">4</span></span></span><span style="top:-5.4462em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mopen">(</span><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mord mathnormal">Sp</span><span class="mord mathnormal">a</span><span class="mord mathnormal">ce</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">32</span></span></span></span></span></span></span></span></span></span></span><span style="top:-3.9221em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">12</span></span></span></span></span></span></span></span></span></span></span><span style="top:-2.3979em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mopen">(</span><span class="mord">2</span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mopen">(</span><span class="mord">3</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.07153em;">C</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mopen">(</span><span class="mord mathnormal">Vi</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">t</span><span class="mord mathnormal">u</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">A</span><span class="mord mathnormal">dd</span><span class="mord mathnormal">ress</span><span class="mord mathnormal">Sp</span><span class="mord mathnormal">a</span><span class="mord mathnormal">ce</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">÷</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">20</span></span></span></span></span></span></span></span></span></span></span><span style="top:-0.8738em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mopen">(</span><span class="mord">4</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="mord mathnormal">ab</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.05764em;">E</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mord mathnormal" style="margin-right:0.03588em;">ry</span><span class="mord mathnormal" style="margin-right:0.07153em;">C</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.07153em;">C</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">20</span></span></span></span></span></span></span></span></span></span></span><span style="top:0.6503em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mopen">(</span><span class="mord">5</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="mord mathnormal">ab</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">e</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="mord mathnormal">ab</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.05764em;">E</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mord mathnormal" style="margin-right:0.03588em;">ry</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="mord mathnormal">ab</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.05764em;">E</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mord mathnormal" style="margin-right:0.03588em;">ry</span><span class="mord mathnormal" style="margin-right:0.07153em;">C</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">22</span></span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:4.3103em;"><span></span></span></span></span></span></span></span><span class="tag"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:4.8103em;"><span style="top:-6.8344em;"><span class="pstrut" style="height:2.8641em;"></span><span class="eqn-num"></span></span><span style="top:-5.3103em;"><span class="pstrut" style="height:2.8641em;"></span><span class="eqn-num"></span></span><span style="top:-3.7862em;"><span class="pstrut" style="height:2.8641em;"></span><span class="eqn-num"></span></span><span style="top:-2.2621em;"><span class="pstrut" style="height:2.8641em;"></span><span class="eqn-num"></span></span><span style="top:-0.7379em;"><span class="pstrut" style="height:2.8641em;"></span><span class="eqn-num"></span></span><span style="top:0.7862em;"><span class="pstrut" style="height:2.8641em;"></span><span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:4.3103em;"><span></span></span></span></span></span></span></span></span></p>
<p>也就是说,光是一个页表结构就要占用 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>4</mn><mi>M</mi><mi>i</mi><mi>B</mi></mrow><annotation encoding="application/x-tex">4MiB</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">4</span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span> 的内存空间,对于现在动辄 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>16</mn><mi>G</mi><mi>i</mi><mi>B</mi></mrow><annotation encoding="application/x-tex">16GiB</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">16</span><span class="mord mathnormal">G</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span> 的内存来说可能微不足道,但在曾今内存空间寸土寸金的时候,<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>4</mn><mi>M</mi><mi>i</mi><mi>B</mi></mrow><annotation encoding="application/x-tex">4MiB</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">4</span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span>甚至足够跑起一个操作系统内核,这就显得页表有些过于臃肿了。于是,为了解决这个内存占用的问题,工程师们又提出了多级分页 <em>(Multi-level paging)</em> 的概念其中在32位的操作系统中使用最多的是二级分页 <em>(Two level paging)</em>而64位操作系统多使用四级分页或是更多级别的分页。尽管分页的级数会存在不同但它们的原理都是相同的而且都是为了解决一个问题减少页表自身占用的存储空间。</p>
<p>以二级分页为例实际上就是将原先页表的结构同样按照页划分为块对于上面提到的32位架构中的页表结构可以被划分为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mn>10</mn></msup></mrow><annotation encoding="application/x-tex">2^{10}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">10</span></span></span></span></span></span></span></span></span></span></span></span> 个块,每一个划分出来的块都叫做一个<strong>一级页表 <em>(Level 1 Page Table)</em></strong>。每个原先的页表项此时就可以像虚拟地址那样表示成为一级页表的序号和页表内的偏移不过此时的偏移就不是以字节为单位的偏移而是以页表项元素大小4字节为单位的偏移。</p>
<p>对于一个原先页表项的序号 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>I</mi><mi>n</mi><mi>d</mi><mi>e</mi><mi>x</mi></mrow><annotation encoding="application/x-tex">Index</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.07847em;">I</span><span class="mord mathnormal">n</span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span><span class="mord mathnormal">x</span></span></span></span>,它对应的一级页表的序号和页表内的偏移为:</p>
<p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mtable rowspacing="0.25em" columnalign="right left" columnspacing="0em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mtable rowspacing="0.25em" columnalign="right left" columnspacing="0em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mi>E</mi><mi>n</mi><mi>t</mi><mi>r</mi><mi>y</mi><mi>P</mi><mi>e</mi><mi>r</mi><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>S</mi><mi>i</mi><mi>z</mi><mi>e</mi><mo>÷</mo><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>T</mi><mi>a</mi><mi>b</mi><mi>l</mi><mi>e</mi><mi>E</mi><mi>n</mi><mi>t</mi><mi>r</mi><mi>y</mi><mi>S</mi><mi>i</mi><mi>z</mi><mi>e</mi></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><msup><mn>2</mn><mn>12</mn></msup><mo>÷</mo><msup><mn>2</mn><mn>2</mn></msup></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><msup><mn>2</mn><mn>10</mn></msup></mrow></mstyle></mtd></mtr></mtable></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mtable rowspacing="0.25em" columnalign="right left" columnspacing="0em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mi>O</mi><mi>f</mi><mi>f</mi><mi>s</mi><mi>e</mi><mi>t</mi></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mi>I</mi><mi>n</mi><mi>d</mi><mi>e</mi><mi>x</mi><mtext> </mtext><mi mathvariant="normal">%</mi><mtext> </mtext><mi>E</mi><mi>n</mi><mi>t</mi><mi>r</mi><mi>y</mi><mi>P</mi><mi>e</mi><mi>r</mi><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mi>I</mi><mi>n</mi><mi>d</mi><mi>e</mi><mi>x</mi><mtext> </mtext><mi mathvariant="normal">%</mi><mtext> </mtext><msup><mn>2</mn><mn>10</mn></msup></mrow></mstyle></mtd></mtr></mtable></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mtable rowspacing="0.25em" columnalign="right left" columnspacing="0em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>I</mi><mi>n</mi><mi>d</mi><mi>e</mi><mi>x</mi></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mi>f</mi><mi>l</mi><mi>o</mi><mi>o</mi><mi>r</mi><mo stretchy="false">(</mo><mi>I</mi><mi>n</mi><mi>d</mi><mi>e</mi><mi>x</mi><mo>÷</mo><mi>E</mi><mi>n</mi><mi>t</mi><mi>r</mi><mi>y</mi><mi>P</mi><mi>e</mi><mi>r</mi><mi>P</mi><mi>a</mi><mi>g</mi><mi>e</mi><mo stretchy="false">)</mo></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mi>f</mi><mi>l</mi><mi>o</mi><mi>o</mi><mi>r</mi><mo stretchy="false">(</mo><mi>I</mi><mi>n</mi><mi>d</mi><mi>e</mi><mi>x</mi><mo>÷</mo><msup><mn>2</mn><mn>10</mn></msup><mo stretchy="false">)</mo></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mi>I</mi><mi>n</mi><mi>d</mi><mi>e</mi><mi>x</mi><mo>&gt;</mo><mo>&gt;</mo><mn>10</mn></mrow></mstyle></mtd></mtr></mtable></mrow></mstyle></mtd></mtr></mtable><annotation encoding="application/x-tex">\begin{align}
&amp;{
\begin{align}
EntryPerPage &amp;= PageSize \div PageTableEntrySize \nonumber \\
&amp;= 2^{12} \div 2^2 \nonumber \\
&amp;= 2^{10} \nonumber
\end{align}
} \nonumber \\
&amp;{
\begin{align}
Offset &amp;= Index \ \% \ EntryPerPage \nonumber \\
&amp;= Index \ \% \ 2^{10} \nonumber
\end{align}
} \nonumber \\
&amp;{
\begin{align}
PageIndex &amp;= floor(Index \div EntryPerPage) \nonumber \\
&amp;= floor(Index \div 2^{10}) \nonumber \\
&amp;= Index &gt;&gt; 10 \nonumber
\end{align}
}
\end{align}
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:12.9964em;vertical-align:-6.2482em;"></span><span class="mord"><span class="mtable"><span class="col-align-r"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:6.7482em;"><span style="top:-8.7482em;"><span class="pstrut" style="height:4.5241em;"></span><span class="mord"></span></span><span style="top:-4.6621em;"><span class="pstrut" style="height:4.5241em;"></span><span class="mord"></span></span><span style="top:-0.5879em;"><span class="pstrut" style="height:4.5241em;"></span><span class="mord"></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:6.2482em;"><span></span></span></span></span></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:6.7482em;"><span style="top:-8.7482em;"><span class="pstrut" style="height:4.5241em;"></span><span class="mord"><span class="mord"></span><span class="mord"><span class="mord"><span class="mtable"><span class="col-align-r"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.5241em;"><span style="top:-4.6841em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.05764em;">E</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mord mathnormal" style="margin-right:0.03588em;">ry</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal" style="margin-right:0.02778em;">er</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span></span></span><span style="top:-3.16em;"><span class="pstrut" style="height:3em;"></span><span class="mord"></span></span><span style="top:-1.6359em;"><span class="pstrut" style="height:3em;"></span><span class="mord"></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:2.0241em;"><span></span></span></span></span></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.5241em;"><span style="top:-4.6841em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mord mathnormal">i</span><span class="mord mathnormal">ze</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">÷</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="mord mathnormal">ab</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.05764em;">E</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mord mathnormal" style="margin-right:0.03588em;">ry</span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mord mathnormal">i</span><span class="mord mathnormal">ze</span></span></span><span style="top:-3.16em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">12</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">÷</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span><span style="top:-1.6359em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">10</span></span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:2.0241em;"><span></span></span></span></span></span></span></span></span></span></span><span style="top:-4.6621em;"><span class="pstrut" style="height:4.5241em;"></span><span class="mord"><span class="mord"></span><span class="mord"><span class="mord"><span class="mtable"><span class="col-align-r"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.7621em;"><span style="top:-3.9221em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mord mathnormal" style="margin-right:0.10764em;">ff</span><span class="mord mathnormal">se</span><span class="mord mathnormal">t</span></span></span><span style="top:-2.3979em;"><span class="pstrut" style="height:3em;"></span><span class="mord"></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:1.2621em;"><span></span></span></span></span></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.7621em;"><span style="top:-3.9221em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.07847em;">I</span><span class="mord mathnormal">n</span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span><span class="mord mathnormal">x</span><span class="mspace"> </span><span class="mord">%</span><span class="mspace"> </span><span class="mord mathnormal" style="margin-right:0.05764em;">E</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mord mathnormal" style="margin-right:0.03588em;">ry</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal" style="margin-right:0.02778em;">er</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span></span></span><span style="top:-2.3979em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.07847em;">I</span><span class="mord mathnormal">n</span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span><span class="mord mathnormal">x</span><span class="mspace"> </span><span class="mord">%</span><span class="mspace"> </span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">10</span></span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:1.2621em;"><span></span></span></span></span></span></span></span></span></span></span><span style="top:-0.5879em;"><span class="pstrut" style="height:4.5241em;"></span><span class="mord"><span class="mord"></span><span class="mord"><span class="mord"><span class="mtable"><span class="col-align-r"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.5121em;"><span style="top:-4.6721em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.07847em;">I</span><span class="mord mathnormal">n</span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span><span class="mord mathnormal">x</span></span></span><span style="top:-3.1479em;"><span class="pstrut" style="height:3em;"></span><span class="mord"></span></span><span style="top:-1.6479em;"><span class="pstrut" style="height:3em;"></span><span class="mord"></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:2.0121em;"><span></span></span></span></span></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.5121em;"><span style="top:-4.6721em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal" style="margin-right:0.02778em;">oor</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.07847em;">I</span><span class="mord mathnormal">n</span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">÷</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">E</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mord mathnormal" style="margin-right:0.03588em;">ry</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal" style="margin-right:0.02778em;">er</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mclose">)</span></span></span><span style="top:-3.1479em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal" style="margin-right:0.02778em;">oor</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.07847em;">I</span><span class="mord mathnormal">n</span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">÷</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">10</span></span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span><span style="top:-1.6479em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.07847em;">I</span><span class="mord mathnormal">n</span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord">10</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:2.0121em;"><span></span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:6.2482em;"><span></span></span></span></span></span></span></span></span></span></span></span></p>
<p>通俗而言就是原先页表项的下标的高10位为这个页表项所在的一级页表的序号低10位则为这个页表项在其所在的一级页表中的下标。例如页表中原先位于<code>0x80200</code><code>0b1000_0000_0010_0000_0000</code>)的页表项,可以认为是在第<code>0x200</code><code>0b10_0000_0000</code>)个一级页表中的第<code>0x200</code>个页表项。</p>
<p>在划分了一级页表之后额外添加了一个页表其中的每一项是32位的数值指向这一项下标对应的一级页表所在的首字节的地址这个页表就是<strong>二级页表 <em>(Level 2 Page Table)</em></strong>。由于在32位架构的设备上最多有 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mn>1</mn></msup><mn>0</mn></mrow><annotation encoding="application/x-tex">2^10</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span></span></span></span></span><span class="mord">0</span></span></span></span> 个一级页表而一个一级页表对应的二级页表中的页表项大小为4字节所以二级页表的大小为</p>
<p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mn>2</mn><mn>10</mn></msup><mo>×</mo><mn>4</mn><mo>=</mo><msup><mn>2</mn><mn>12</mn></msup><mi>B</mi><mi>y</mi><mi>t</mi><mi>e</mi><mo>=</mo><mn>4</mn><mi>K</mi><mi>i</mi><mi>B</mi></mrow><annotation encoding="application/x-tex">2^{10} \times 4 = 2^{12} Byte = 4KiB
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9474em;vertical-align:-0.0833em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">10</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">4</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.0585em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">12</span></span></span></span></span></span></span></span></span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="mord mathnormal">t</span><span class="mord mathnormal">e</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">4</span><span class="mord mathnormal" style="margin-right:0.07153em;">K</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span></span></p>
<p>正好就是一个页的大小。</p>
<p>这样,当要将一个虚拟地址转换为物理地址时,需要</p>
<ol>
<li>取低12位为页内偏移</li>
<li>取中10位为一级页表内页表项的下标</li>
<li>取高10位位二级页表内页表项的下标</li>
<li>使用二级页表项的下标在二级页表中取得一级页表的地址</li>
<li>使用一级页表项的下标和一级页表的地址取得虚拟地址对应的物理页的地址</li>
<li>根据物理页的地址和页内偏移得到转换后的物理地址</li>
</ol>
<!-- Place a picture here -->
<p>乍一看,好像二级页表的出现是多此一举,本来可以直接用下标在页表里定位到页表项,从页表项直接取得物理页地址,现在还要多此一举,先从二级页表拿到一级页表的地址,然后才能拿到物理页地址。仔细一算的话,对于所有的虚拟页都进行映射的情况,二级分页甚至还要比不采用二级分页多出一个二级页表的大小,这哪里节约了页表占用的空间!</p>
<p>最初我也是同样对此感到十分地疑惑,一个不但增加了内存占用,复杂了流程还减慢了地址转换地速度的方案怎么就流传下来了呢?后来我意识到我忽略了一个很关键的细节:<strong>前文中讨论页表占用的内存是时始终是基于虚拟地址全部映射到物理地址这个前提进行的</strong>,然而事实上,<strong>大部分时候都不会需要将虚拟地址全部映射在内存中</strong>,甚至有时候内存根本就不够映射全部的虚拟地址!</p>
<p>虚拟地址不完全映射到内存就意味着页表中有空的页表项,对于没有二级分页的方案,由于<strong>整个页表都相当于一个巨大的数组结构,即使一个元素为空也不能舍弃它的空间,所以不论映射了多少虚拟页,页表占用的大小始终是固定的</strong>。然而对于二级分页的方案,由于二级页表记录的是一级页表的地址,这也就相当于<strong>拆散了原先巨大的页表</strong>,并且还使得拆分后得到的一级页表可以分散地存储在内存中。这样做还带来了一个巨大的好处,就是<strong>页表的内存不再需要一次性分配完毕</strong>,因为原先没有多级分页机制的时候,但凡需要页表,就需要一次性申请全部页表的空间,而现在即便一级和二级页表还是相当于数组结构,还是需要为每一个页表分配固定大小的内存,但是它们单个的体积从原先的 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>4</mn><mi>M</mi><mi>i</mi><mi>B</mi></mrow><annotation encoding="application/x-tex">4MiB</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">4</span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span> 降到了 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>4</mn><mi>K</mi><mi>i</mi><mi>B</mi></mrow><annotation encoding="application/x-tex">4KiB</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">4</span><span class="mord mathnormal" style="margin-right:0.07153em;">K</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span>,同时由于二级页表的存在,<strong>如果一整个一级页表内的表项全部为空,那么就可以不分配这个一级页表的空间</strong>,从而达到节省空间的目的。</p>
<!-- Place a picture here -->
<p>有了二级页表之后,现在摆在我们面前的只剩最后一个问题:</p>
<div class="note gray icon-padding flat"><i class="note-icon fas fa-question-circle"></i><p><strong>如果程序代码比内存还要大怎么办?</strong></p>
</div>
<p>早期的RAM相当金贵远不及如今动辄16GiB、32GiB能有个几兆几十兆都是奢侈。那如果一个程序就十分不巧大小竟然比内存还大那这个庞然大物该如何运行起来呢</p>
<p>这就要提到一个叫做 <strong>程序运行的局部性原理</strong> 的概念,它是指程序在一段时间内程序的执行只限于程序的一部分,就以我们熟悉的冒泡排序算法为例吧:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> j = <span class="number">0</span>; j &lt; n - i - <span class="number">1</span>; j++) &#123;</span><br><span class="line"> <span class="keyword">if</span>(arr[j] &lt; arr[j + <span class="number">1</span>]) &#123;</span><br><span class="line"> <span class="type">int</span> temp = arr[j];</span><br><span class="line"> arr[j] = arr[j + <span class="number">1</span>];</span><br><span class="line"> arr[j + <span class="number">1</span>] = temp;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>即便程序的其余部分无比复杂哪怕是3A巨制但只要程序运行到这里它在相当一段时间里只会在两个<code>for</code>循环里打转转,它访问的内存也只会局限在<code>arr</code>这个数组里,这就是程序的局部性,在这一段时间里程序所用到的空间就是程序所需要的<strong>工作集</strong></p>
<p>由于程序运行具有局部性的原理所以不论程序占用的虚拟地址空间有多大它在一个特定的时间长度内都不会访问全部的地址空间这时只需要将其运行需要的部分加载到内存中程序就可以正常地运行了。这就好比我们在玩Minecraft时候往往我们的视野半径只有十几个区块所以加载无尽的世界的所有区块这显然是不可能的和只加载我们视野范围内的区块在我们看来效果是一样的甚至肯定会流畅不止一点点所以为了能让我们愉快的玩上游戏游戏的设计者就不会让电脑加载视野范围外的区块或者是限制加载视野范围外的区块从而让我们体验不变的情况下让游戏能够正常地运行电脑不至于直接冒烟。</p>
<div class="note info flat"><p><strong>抖动</strong><br>
即使程序运行具有局部性,但仍可能出现工作集比内存大小大的情况。<br>
比如一个程序在某一时刻的工作集为10个页<code>ABCDEFGHIJ</code>而内存只有9个页可供程序使用。<br>
假设某一时刻程序加载了<code>ABCDEFGHI</code>页,当它需要<code>J</code>页的时候,其发现页并不在内存中,就会尝试从磁盘加载这个缺失的页进入内存。<br>
而这个操作就会顶掉<code>ABCDEFGHI</code>中的一个页,很快,程序又会遇到被顶掉的页,其又会加载并顶掉另一个页。<br>
这样的操作会如此不断重复,程序也就无法再正常地继续运行了,这就是<strong>抖动</strong>现象。</p>
</div>
<h3 id="抽象页表数据结构">抽象页表数据结构</h3>
<p>现在,我们知道了什么是虚拟地址,什么是分页,什么是多级分页。在实验中我们要用到的就是上文中提到的二级分页机制,它用到了一级页表、二级页表和其中的页表项三个主要的数据结构,而一级页表和二级页表实质都是页表项的数组,没有本质上的差异,可以视为同一个数据结构,所以我们只需要实现页表和页表项的数据结构就足够使用了。</p>
<p>由于页表中需要用到页表项,所以我们首先来对页表项 <em>(PageTableEntry)</em> 进行抽象。</p>
<p>前文中提到,页表项中存储的是一级页表或是物理页的地址,其实这个描述不是十分准确。如前文所述,由于页的大小固定,并且都是 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mi>n</mi></msup></mrow><annotation encoding="application/x-tex">2^n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6644em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span></span></span></span></span></span></span> 的大小,所以任何一个页的地址的低位其实都是相同的且都是<code>0</code>这就意味着保留着些位是毫无意义的例如对于32位的架构而言由于页的大小为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mn>12</mn></msup><mi>B</mi></mrow><annotation encoding="application/x-tex">2^{12}B</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">12</span></span></span></span></span></span></span></span></span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span>所以任何一个页的地址低12位均为0。工程师显然也发现了这一点所以它们决定利用这12个无用的位来存储更多的信息包括页的属性、权限等。</p>
<p>根据 <a target="_blank" rel="noopener" href="https://wiki.osdev.org/Paging#Page_Map_Table_Entries">OSDevWiki上关于页表项结构的描述</a>,页表项 <em>(Page Table Entry)</em> 的结构如下:</p>
<table>
<thead>
<tr>
<th style="text-align:center">简写</th>
<th style="text-align:center">全称</th>
<th style="text-align:center"></th>
<th style="text-align:center">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">P</td>
<td style="text-align:center">Present</td>
<td style="text-align:center"><code>[0]</code></td>
<td style="text-align:center"><strong>页表项存在位</strong><br/><code>0</code>:不存在<br/><code>1</code>:存在</td>
</tr>
<tr>
<td style="text-align:center">R/W</td>
<td style="text-align:center">Read/Write</td>
<td style="text-align:center"><code>[1]</code></td>
<td style="text-align:center"><strong>写权限位</strong><br/><code>0</code>:不可写<br/><code>1</code>:可写</td>
</tr>
<tr>
<td style="text-align:center">U/S</td>
<td style="text-align:center">User/Supervisor</td>
<td style="text-align:center"><code>[2]</code></td>
<td style="text-align:center"><strong>访问权限位</strong><br/><code>0</code>:用户级程序不可访问<br/><code>1</code>:用户级程序可以访问</td>
</tr>
<tr>
<td style="text-align:center">PWT</td>
<td style="text-align:center">Page Write Through</td>
<td style="text-align:center"><code>[3]</code></td>
<td style="text-align:center">写穿透位,实验中不会用到,可以不用了解</td>
</tr>
<tr>
<td style="text-align:center">PCD</td>
<td style="text-align:center">Page Cache Disable</td>
<td style="text-align:center"><code>[4]</code></td>
<td style="text-align:center">禁用缓存位,实验中不会用到,可以不用了解</td>
</tr>
<tr>
<td style="text-align:center">A</td>
<td style="text-align:center">Accessed</td>
<td style="text-align:center"><code>[5]</code></td>
<td style="text-align:center"><strong>访问位</strong><br/><code>0</code>:页面在上次清除这个位之后没有被访问过<br/><code>1</code>:页面在上次清除这个位之后被访问过</td>
</tr>
<tr>
<td style="text-align:center">D</td>
<td style="text-align:center">Dirty</td>
<td style="text-align:center"><code>[6]</code></td>
<td style="text-align:center"><strong>脏位</strong><br/><code>0</code>:页面在上次清除这个位之后没有被写过<br/><code>1</code>:页面在上次清除这个位之后被写过</td>
</tr>
<tr>
<td style="text-align:center">PAT</td>
<td style="text-align:center">Page Attribute Table</td>
<td style="text-align:center"><code>[7]</code></td>
<td style="text-align:center">用于指示内存缓存类型 <em>(Memory caching type)</em>,实验中不会用到,可以不用了解</td>
</tr>
<tr>
<td style="text-align:center">G</td>
<td style="text-align:center">Global</td>
<td style="text-align:center"><code>[8]</code></td>
<td style="text-align:center">全局页面位使得TLB缓存中的页表项不会随着CR3寄存器的更改而失效实验中不会用到可以不用了解</td>
</tr>
<tr>
<td style="text-align:center">Address</td>
<td style="text-align:center">-</td>
<td style="text-align:center"><code>[31:12]</code></td>
<td style="text-align:center">地址高20位</td>
</tr>
</tbody>
</table>
<p>尽管在前文的描述中好像一二级页表中页表项结构是相同的但实际上它们在某些位的功能上存在一部分差异这部分差异即使是我自己在实现的时候也忽略了直到写这一份日志仔细查阅Wikipedia时才发现。在二级页表中它的目录项第6位、第7位和第8位的含义有所不同其中第6、8位在二级页表的页表项中作保留位而第7位的属性描述如下</p>
<table>
<thead>
<tr>
<th style="text-align:center">简写</th>
<th style="text-align:center">全称</th>
<th style="text-align:center"></th>
<th style="text-align:center">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">PS</td>
<td style="text-align:center">Page Size</td>
<td style="text-align:center"><code>[7]</code></td>
<td style="text-align:center"><strong>页大小位</strong><br/><code>0</code>页大小为4KiB<br/><code>1</code>页大小为4MiB<br/>在实验中使用4KiB大小的页所以这一位始终为0</td>
</tr>
</tbody>
</table>
<p>为了简便起见,我在实验中采用了不那么安全的实现:我将两种不同的属性合并在同一个位枚举类当中,并在后续的使用中主动避免使用不符合类型的属性。如同在<a href="#%E7%A1%AC%E7%9B%98%E9%A9%B1%E5%8A%A8">硬盘驱动</a><a href="#fat">FAT</a>中介绍的位枚举类那样,页表项中的属性位可以抽象如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">PageFlag</span> &#123;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="keyword">enum</span> <span class="title class_">ATTRIBUTES</span> &#123; </span><br><span class="line"> _EMPTY = <span class="number">0</span>,</span><br><span class="line"> PRESENT = <span class="number">1</span>,</span><br><span class="line"> WRITABLE = <span class="number">1</span> &lt;&lt; <span class="number">1</span>,</span><br><span class="line"> USER_ACCESSIBLE = <span class="number">1</span> &lt;&lt; <span class="number">2</span>,</span><br><span class="line"> WRITE_THROUGH = <span class="number">1</span> &lt;&lt; <span class="number">3</span>,</span><br><span class="line"> NO_CACHE = <span class="number">1</span> &lt;&lt; <span class="number">4</span>,</span><br><span class="line"> ACCESSED = <span class="number">1</span> &lt;&lt; <span class="number">5</span>,</span><br><span class="line"> DIRTY = <span class="number">1</span> &lt;&lt; <span class="number">6</span>,</span><br><span class="line"> HUGE_PAGE = <span class="number">1</span> &lt;&lt; <span class="number">7</span>,</span><br><span class="line"> GLOBAL = <span class="number">1</span> &lt;&lt; <span class="number">8</span>,</span><br><span class="line"> BIT_7 = <span class="number">1</span> &lt;&lt; <span class="number">7</span>,</span><br><span class="line"> BIT_8 = <span class="number">1</span> &lt;&lt; <span class="number">8</span>,</span><br><span class="line"> BIT_9 = <span class="number">1</span> &lt;&lt; <span class="number">9</span>,</span><br><span class="line"> BIT_10 = <span class="number">1</span> &lt;&lt; <span class="number">10</span>,</span><br><span class="line"> BIT_11 = <span class="number">1</span> &lt;&lt; <span class="number">11</span>,</span><br><span class="line"> _ALL = <span class="number">0xFFF</span>,</span><br><span class="line"> &#125;;</span><br><span class="line"> <span class="keyword">private</span>:</span><br><span class="line"> uint16 _attr;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">PageFlag</span>();</span><br><span class="line"> <span class="built_in">PageFlag</span>(uint16 flags);</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> PageFlag&amp; <span class="keyword">operator</span>=(uint16 flags);</span><br><span class="line"> PageFlag&amp; <span class="keyword">operator</span>=(<span class="type">const</span> PageFlag&amp; other);</span><br><span class="line"> <span class="comment">//Judge whether two flags are equal</span></span><br><span class="line"> <span class="type">bool</span> <span class="keyword">operator</span>==(uint16 flags);</span><br><span class="line"> <span class="comment">//Combine two flags</span></span><br><span class="line"> PageFlag <span class="keyword">operator</span>+(uint16 flags);</span><br><span class="line"> PageFlag&amp; <span class="keyword">operator</span>+=(uint16 flags);</span><br><span class="line"> <span class="comment">//Remove flags</span></span><br><span class="line"> PageFlag <span class="keyword">operator</span>-(uint16 flags);</span><br><span class="line"> PageFlag&amp; <span class="keyword">operator</span>-=(uint16 flags);</span><br><span class="line"> <span class="comment">//Bitwise or</span></span><br><span class="line"> PageFlag <span class="keyword">operator</span>|(uint16 flags);</span><br><span class="line"> PageFlag&amp; <span class="keyword">operator</span>|=(uint16 flags);</span><br><span class="line"> <span class="comment">//Bitwise and</span></span><br><span class="line"> PageFlag <span class="keyword">operator</span>&amp;(uint16 flags);</span><br><span class="line"> PageFlag&amp; <span class="keyword">operator</span>&amp;=(uint16 flags);</span><br><span class="line"> <span class="comment">//Reverse flags</span></span><br><span class="line"> PageFlag <span class="keyword">operator</span>~();</span><br><span class="line"> <span class="comment">//Toggle flag bits</span></span><br><span class="line"> PageFlag&amp; <span class="keyword">operator</span>^=(uint16 flags);</span><br><span class="line"> <span class="comment">//Judge whether flags are contained</span></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">contains</span><span class="params">(uint16 flags)</span></span>;</span><br><span class="line"> <span class="function">uint16 <span class="title">val</span><span class="params">()</span></span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>对应的实现在复制之前的枚举类函数的基础上对参数类型进行修改,对应<code>_attr</code>变量的类型即可:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line">PageFlag::<span class="built_in">PageFlag</span>() &#123;</span><br><span class="line"> _attr = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line">PageFlag::<span class="built_in">PageFlag</span>(uint16 flags) &#123;</span><br><span class="line"> _attr = flags;</span><br><span class="line">&#125;</span><br><span class="line">PageFlag&amp; PageFlag::<span class="keyword">operator</span>=(uint16 flags) &#123;</span><br><span class="line"> _attr = flags;</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line">PageFlag&amp; PageFlag::<span class="keyword">operator</span>=(<span class="type">const</span> PageFlag&amp; other) &#123;</span><br><span class="line"> _attr = other._attr;</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Judge whether two flags are equal</span></span><br><span class="line"><span class="type">bool</span> PageFlag::<span class="keyword">operator</span>==(uint16 flags) &#123;</span><br><span class="line"> <span class="keyword">return</span> _attr == flags;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Combine two flags</span></span><br><span class="line">PageFlag PageFlag::<span class="keyword">operator</span>+(uint16 flags) &#123;</span><br><span class="line"> uint16 newAttr = _attr | flags;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">PageFlag</span>(newAttr);</span><br><span class="line">&#125;</span><br><span class="line">PageFlag&amp; PageFlag::<span class="keyword">operator</span>+=(uint16 flags) &#123;</span><br><span class="line"> _attr |= flags;</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Remove flags</span></span><br><span class="line">PageFlag PageFlag::<span class="keyword">operator</span>-(uint16 flags) &#123;</span><br><span class="line"> uint16 newAttr = _attr &amp; ~(flags);</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">PageFlag</span>(newAttr);</span><br><span class="line">&#125;</span><br><span class="line">PageFlag&amp; PageFlag::<span class="keyword">operator</span>-=(uint16 flags) &#123;</span><br><span class="line"> _attr &amp;= ~(flags);</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Bitwise or</span></span><br><span class="line">PageFlag PageFlag::<span class="keyword">operator</span>|(uint16 flags) &#123;</span><br><span class="line"> uint16 newAttr = _attr | flags;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">PageFlag</span>(newAttr);</span><br><span class="line">&#125;</span><br><span class="line">PageFlag&amp; PageFlag::<span class="keyword">operator</span>|=(uint16 flags) &#123;</span><br><span class="line"> _attr |= flags;</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Bitwise and</span></span><br><span class="line">PageFlag PageFlag::<span class="keyword">operator</span>&amp;(uint16 flags) &#123;</span><br><span class="line"> uint8 newAttr = _attr &amp; flags;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">PageFlag</span>(newAttr);</span><br><span class="line">&#125;</span><br><span class="line">PageFlag&amp; PageFlag::<span class="keyword">operator</span>&amp;=(uint16 flags) &#123;</span><br><span class="line"> _attr &amp;= flags;</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Reverse flags</span></span><br><span class="line">PageFlag PageFlag::<span class="keyword">operator</span>~() &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">PageFlag</span>(_attr);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Toggle flag bits</span></span><br><span class="line">PageFlag&amp; PageFlag::<span class="keyword">operator</span>^=(uint16 flags) &#123;</span><br><span class="line"> _attr ^= flags;</span><br><span class="line"> <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Judge whether flags are contained</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">PageFlag::contains</span><span class="params">(uint16 flags)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> (_attr &amp; flags) == flags;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">uint16 <span class="title">PageFlag::val</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> _attr;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>接着就可以抽象页表项的结构了由上表可以看出页表项就是由高20位的地址位和低12位的属性位组成。在实现中我选择将整个页表项的值视作一整个变量进行存储</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">PageTableEntry</span> &#123;</span><br><span class="line"> <span class="keyword">private</span>:</span><br><span class="line"> uint32 _val = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>然后再为类添加从值中解析出地址和属性或是根据参数设置值中的地址位和属性位的成员函数:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">isPresent</span><span class="params">()</span> <span class="type">const</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">address</span><span class="params">()</span> <span class="type">const</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">setAddress</span><span class="params">(uint32 addr)</span></span>;</span><br><span class="line"> <span class="function">PageFlag <span class="title">flags</span><span class="params">()</span> <span class="type">const</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">setFlags</span><span class="params">(PageFlag flags)</span></span>;</span><br></pre></td></tr></table></figure>
<p>为了方便类的实例或是指针的构造,额外添加构造函数和静态成员函数如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">static</span> PageTableEntry* <span class="title">from</span><span class="params">(uint32 addr)</span></span>;</span><br><span class="line"> <span class="built_in">PageTableEntry</span>() &#123;&#125;</span><br><span class="line"> <span class="built_in">PageTableEntry</span>(uint32 addr, PageFlag flags);</span><br></pre></td></tr></table></figure>
<div class="note info flat"><p><strong>小技巧:</strong><br>
在后面的编程实践中,由于我们往往拿到的都是某个页表项的地址,而不是一个页表项的实例。<br>
对于像页表项这样比较复杂的结构,如果直接通过地址和指针的解引用然后再对其中的内容进行解析,则会让代码的可读性变得很差,例如获得页表项中存储的地址可以用<code>uint32 address = (*ptr) &amp; 0xFFFFF000</code>实现。<br>
但由于指针具有可以随意其指向的内容的类型的特性,可以通过将一个指针转换为对应结构的类(例如<code>PageTableEntry</code>)的指针,然后就可以通过指针来调用类的成员结构,从而实现代码重用,并且也让代码的可读性得到了提高。<br>
例如,原先的操作也可以通过<code>uint32 address = (PageTableEntry*)ptr-&gt;address()</code>实现,而这种实现不但可以实现<code>address()</code>的重用,还一下就能够看出语句的目的是获得页表项中存储的地址。<br>
<code>static PageTableEntry* from(uint32 addr);</code>这样的函数其实就是为了我们能够更方便也更美观地进行指针的类型转换:<code>PageTableEntry::from(ptr)</code>肯定要比<code>(PageTableEntry*)ptr</code>看起来更合理,并且在某些特定的情形下,这样的转换函数内还可以添加额外的判断,让类型的转换更加安全。</p>
</div>
<p><code>PageTableEntry</code>类的全部定义如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">PageTableEntry</span> &#123;</span><br><span class="line"> <span class="keyword">private</span>:</span><br><span class="line"> uint32 _val = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">static</span> PageTableEntry* <span class="title">from</span><span class="params">(uint32 addr)</span></span>;</span><br><span class="line"> <span class="built_in">PageTableEntry</span>() &#123;&#125;</span><br><span class="line"> <span class="built_in">PageTableEntry</span>(uint32 addr, PageFlag flags);</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">isPresent</span><span class="params">()</span> <span class="type">const</span></span>;</span><br><span class="line"> <span class="function">uint32 <span class="title">address</span><span class="params">()</span> <span class="type">const</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">setAddress</span><span class="params">(uint32 addr)</span></span>;</span><br><span class="line"> <span class="function">PageFlag <span class="title">flags</span><span class="params">()</span> <span class="type">const</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">setFlags</span><span class="params">(PageFlag flags)</span></span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>其中函数的实现如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">PageTableEntry* <span class="title">PageTableEntry::from</span><span class="params">(uint32 addr)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> (PageTableEntry*)addr;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">PageTableEntry::<span class="built_in">PageTableEntry</span>(uint32 addr, PageFlag flags) &#123;</span><br><span class="line"> _val = addr &amp; <span class="number">0xFFFFF000</span>;</span><br><span class="line"> _val |= (flags.<span class="built_in">val</span>() &amp; <span class="number">0xFFF</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">PageTableEntry::isPresent</span><span class="params">()</span> <span class="type">const</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">flags</span>().<span class="built_in">contains</span>(PageFlag::PRESENT);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">uint32 <span class="title">PageTableEntry::address</span><span class="params">()</span> <span class="type">const</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> _val &amp; <span class="number">0xFFFFF000</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">PageTableEntry::setAddress</span><span class="params">(uint32 addr)</span> </span>&#123;</span><br><span class="line"> _val &amp;= <span class="number">0x00000FFF</span>;</span><br><span class="line"> _val |= (addr &amp; <span class="number">0xFFFFF000</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">PageFlag <span class="title">PageTableEntry::flags</span><span class="params">()</span> <span class="type">const</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> _val &amp; <span class="number">0xFFF</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">PageTableEntry::setFlags</span><span class="params">(PageFlag flags)</span> </span>&#123;</span><br><span class="line"> _val &amp;= <span class="number">0xFFFFF000</span>;</span><br><span class="line"> _val |= (flags.<span class="built_in">val</span>() &amp; <span class="number">0xFFF</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>而对于页表而言,它的实现就更加简单了,由于它在 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>4</mn><mi>K</mi><mi>i</mi><mi>B</mi></mrow><annotation encoding="application/x-tex">4KiB</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">4</span><span class="mord mathnormal" style="margin-right:0.07153em;">K</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span> 的页大小下本质就是一个长度位1024的数组所以其成员变量就是一个长度为1024的数组变量</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">PageTable</span> &#123;</span><br><span class="line"> <span class="keyword">private</span>:</span><br><span class="line"> PageTableEntry _entries[<span class="number">1024</span>];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>而由于页表本身不需要实现太多的功能,在目前实验中只需要对页表进行初始化以及取出页表中的第<code>idx</code>个页表项,所以其成员函数也十分简单:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function">PageTableEntry&amp; <span class="title">at</span><span class="params">(uint32 idx)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">init</span><span class="params">()</span></span>;</span><br></pre></td></tr></table></figure>
<p>除此之外由于页表可能是一级页表也可能是二级页表对于一级页表而言一般一级页表都是直接拿到地址所以需要添加一个由32位地址转换为页表对象指针的静态成员函数而二级页表都是要从一级页表中获得了页表项解析出地址然后再转换为二级页表对象的指针稍微有点麻烦所以不妨添加一个由<code>PageTableEntry</code>类的对象直接转换为页表对象指针的静态成员函数。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">static</span> PageTable* <span class="title">from</span><span class="params">(<span class="type">const</span> PageTableEntry&amp; entry)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">static</span> PageTable* <span class="title">from</span><span class="params">(<span class="type">const</span> uint32 addr)</span></span>;</span><br></pre></td></tr></table></figure>
<p>完整的<code>PageTable</code>类定义如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">PageTable</span> &#123;</span><br><span class="line"> <span class="keyword">private</span>:</span><br><span class="line"> PageTableEntry _entries[<span class="number">1024</span>];</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">static</span> PageTable* <span class="title">from</span><span class="params">(<span class="type">const</span> PageTableEntry&amp; entry)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">static</span> PageTable* <span class="title">from</span><span class="params">(<span class="type">const</span> uint32 addr)</span></span>;</span><br><span class="line"> <span class="function">PageTableEntry&amp; <span class="title">at</span><span class="params">(uint32 idx)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">init</span><span class="params">()</span></span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>其中的成员函数实现如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">PageTable* <span class="title">PageTable::from</span><span class="params">(<span class="type">const</span> PageTableEntry&amp; entry)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> (PageTable*)entry.<span class="built_in">address</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">PageTable* <span class="title">PageTable::from</span><span class="params">(<span class="type">const</span> uint32 addr)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> (PageTable*)addr;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">PageTableEntry&amp; <span class="title">PageTable::at</span><span class="params">(uint32 idx)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> _entries[idx];</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">PageTable::init</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1024</span>; i++) &#123;</span><br><span class="line"> _entries[i] = <span class="built_in">PageTableEntry</span>();</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<!-- ## 抽象ELF文件 -->
<!-- ### 程序怎样被加载 -->
<!-- ### Elf头 -->
<!-- ### ProgramHeader -->
<div class="note orange icon-padding flat"><i class="note-icon fas fa-medal"></i><p><strong>勇者奖章</strong><br>
恭喜你,读完了所有日志中最长最复杂的一篇,后面的实验之路将会因这一章的努力而愈发平坦。</p>
</div>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta"><i class="fas fa-circle-user fa-fw"></i>文章作者: </span><span class="post-copyright-info"><a href="https://blog.linloir.cn">Linloir</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta"><i class="fas fa-square-arrow-up-right fa-fw"></i>文章链接: </span><span class="post-copyright-info"><a href="https://blog.linloir.cn/2022/08/20/os-journal-vol-4/">https://blog.linloir.cn/2022/08/20/os-journal-vol-4/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta"><i class="fas fa-circle-exclamation fa-fw"></i>版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外,均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来源 <a href="https://blog.linloir.cn" target="_blank">時痕</a></span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/">操作系统</a><a class="post-meta__tags" href="/tags/%E5%A4%A7%E5%AD%A6/">大学</a></div><div class="post-share"><div class="social-share" data-image="/img/cover.jpg" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><a class="prev-post pull-left" href="/2024/10/11/reborn/" title="重生"><img class="cover" src="/img/cover.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">重生</div></div></a><a class="next-post pull-right" href="/2022/07/19/os-journal-vol-3/" title="操统实验日志 第三章 从实模式到保护模式"><img class="cover" src="/img/cover.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">操统实验日志 第三章 从实模式到保护模式</div></div></a></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><a href="/2022/07/15/os-journal-vol-1/" title="操统实验日志 第一章 序章"><img class="cover" src="/img/cover.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-07-15</div><div class="title">操统实验日志 第一章 序章</div></div></a><a href="/2022/07/15/os-journal-vol-2/" title="操统实验日志 第二章 万丈高楼平地起"><img class="cover" src="/img/cover.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-07-16</div><div class="title">操统实验日志 第二章 万丈高楼平地起</div></div></a><a href="/2022/07/19/os-journal-vol-3/" title="操统实验日志 第三章 从实模式到保护模式"><img class="cover" src="/img/cover.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-07-19</div><div class="title">操统实验日志 第三章 从实模式到保护模式</div></div></a></div></div></div><div class="aside-content" id="aside-content"><div class="card-widget card-info is-center"><div class="avatar-img"><img src="/img/avatar.png" onerror="this.onerror=null;this.src='/img/friend_404.gif'" alt="avatar"/></div><div class="author-info-name">Linloir</div><div class="author-info-description">我、技术、生活与值得分享的一切</div><div class="site-data"><a href="/archives/"><div class="headline">文章</div><div class="length-num">11</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">10</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">3</div></a></div><a id="card-info-btn" target="_blank" rel="noopener" href="https://github.com/xxxxxx"><i class="fab fa-github"></i><span>Follow Me</span></a><div class="card-info-social-icons"><a class="social-icon" href="https://github.com/Linloir" target="_blank" title="GitHub"><i class="fab fa-github"></i></a><a class="social-icon" href="mailto:jonathanzhang.st@gmail.com" target="_blank" title="Email"><i class="fas fa-envelope"></i></a></div></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%85%B3%E4%BA%8E%E6%9C%AC%E7%AB%A0-2"><span class="toc-number">1.</span> <span class="toc-text">关于本章</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%9C%80%E5%90%8E%E4%B8%80%E4%B8%AA%E8%B7%B3%E6%9D%BF%EF%BC%9AKernelLoader"><span class="toc-number">2.</span> <span class="toc-text">最后一个跳板KernelLoader</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%86%9F%E6%82%89%E7%9A%84%E6%9C%8B%E5%8F%8B%EF%BC%9AC-%E9%97%AA%E4%BA%AE%E7%99%BB%E5%9C%BA"><span class="toc-number">3.</span> <span class="toc-text">熟悉的朋友C++闪亮登场</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BB%8E%E4%BB%A3%E7%A0%81%E5%88%B0%E5%8F%AF%E6%89%A7%E8%A1%8C%E6%96%87%E4%BB%B6"><span class="toc-number">3.1.</span> <span class="toc-text">从代码到可执行文件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#C-%E4%B8%8E%E6%B1%87%E7%BC%96%E6%B7%B7%E7%BC%96"><span class="toc-number">3.2.</span> <span class="toc-text">C++与汇编混编</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%A9%B1%E5%8A%A8%EF%BC%9A%E6%8A%BD%E8%B1%A1%E7%A1%AC%E4%BB%B6%E4%BA%A4%E4%BA%92"><span class="toc-number">4.</span> <span class="toc-text">驱动:抽象硬件交互</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%AB%AF%E5%8F%A3%E9%A9%B1%E5%8A%A8"><span class="toc-number">4.1.</span> <span class="toc-text">端口驱动</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%A1%AC%E7%9B%98%E9%A9%B1%E5%8A%A8"><span class="toc-number">4.2.</span> <span class="toc-text">硬盘驱动</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%8A%BD%E8%B1%A1FAT%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F"><span class="toc-number">5.</span> <span class="toc-text">抽象FAT文件系统</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%AE%80%E8%BF%B0%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E4%B8%8EFAT"><span class="toc-number">5.1.</span> <span class="toc-text">简述文件系统与FAT</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%AC%AC%E4%B8%80%E7%AF%87%E6%96%87%E6%A1%A3"><span class="toc-number">5.2.</span> <span class="toc-text">第一篇文档</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AE%9A%E4%B9%89%E4%B8%8E%E6%B3%A8%E8%A7%A3"><span class="toc-number">5.2.1.</span> <span class="toc-text">定义与注解</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8D%B7%E7%BB%93%E6%9E%84"><span class="toc-number">5.2.2.</span> <span class="toc-text">卷结构</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#BPB%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84"><span class="toc-number">5.2.3.</span> <span class="toc-text">BPB数据结构</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#FAT"><span class="toc-number">5.2.4.</span> <span class="toc-text">FAT</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%9B%AE%E5%BD%95%E7%BB%93%E6%9E%84"><span class="toc-number">5.2.5.</span> <span class="toc-text">目录结构</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%8A%BD%E8%B1%A1MBR%E7%BB%93%E6%9E%84"><span class="toc-number">6.</span> <span class="toc-text">抽象MBR结构</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#MBR%E7%BB%93%E6%9E%84"><span class="toc-number">6.1.</span> <span class="toc-text">MBR结构</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Partition-Entry%E7%BB%93%E6%9E%84"><span class="toc-number">6.2.</span> <span class="toc-text">Partition Entry结构</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%8A%BD%E8%B1%A1Partition-Entry"><span class="toc-number">6.3.</span> <span class="toc-text">抽象Partition Entry</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%8A%BD%E8%B1%A1MBR"><span class="toc-number">6.4.</span> <span class="toc-text">抽象MBR</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%8A%BD%E8%B1%A1%E9%A1%B5%E8%A1%A8"><span class="toc-number">7.</span> <span class="toc-text">抽象页表</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%AE%80%E8%BF%B0%E8%99%9A%E6%8B%9F%E5%86%85%E5%AD%98%E4%B8%8E%E5%88%86%E9%A1%B5%E6%9C%BA%E5%88%B6"><span class="toc-number">7.1.</span> <span class="toc-text">简述虚拟内存与分页机制</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%8A%BD%E8%B1%A1%E9%A1%B5%E8%A1%A8%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84"><span class="toc-number">7.2.</span> <span class="toc-text">抽象页表数据结构</span></a></li></ol></li></ol></div></div><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>最新文章</span></div><div class="aside-list"><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/2024/10/20/jar-via-adb/" title="用 adb + app_process 执行 Java 代码 —— 一种无需安装 apk 的脱机代码执行方案">用 adb + app_process 执行 Java 代码 —— 一种无需安装 apk 的脱机代码执行方案</a><time datetime="2024-10-20T13:22:40.000Z" title="发表于 2024-10-20 21:22:40">2024-10-20</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/2024/10/15/tencent-new-start/" title="短文 - 转正日">短文 - 转正日</a><time datetime="2024-10-15T05:08:52.000Z" title="发表于 2024-10-15 13:08:52">2024-10-15</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/2024/10/13/blog-mig/" title="短文 - 博客迁移小记">短文 - 博客迁移小记</a><time datetime="2024-10-13T12:35:27.000Z" title="发表于 2024-10-13 20:35:27">2024-10-13</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/2024/10/13/micro-posts/" title="短文 - 关于短博文的碎碎念">短文 - 关于短博文的碎碎念</a><time datetime="2024-10-13T09:36:20.000Z" title="发表于 2024-10-13 17:36:20">2024-10-13</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/2024/10/13/host-git-at-home/" title="在 NAS 上部署自己的 Gitea 服务,无需公网服务器">在 NAS 上部署自己的 Gitea 服务,无需公网服务器</a><time datetime="2024-10-13T02:52:30.000Z" title="发表于 2024-10-13 10:52:30">2024-10-13</time></div></div></div></div></div></div></main><footer id="footer" style="background: transparent;"><div id="footer-wrap"><div class="copyright">&copy;2022 - 2024 By Linloir</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div><div class="footer_custom_text">Wirtten with Love ❤</div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="translateLink" type="button" title="简繁转换"></button><button id="darkmode" type="button" title="日间和夜间模式切换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside-config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><span class="scroll-percent"></span><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="/js/tw_cn.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox/fancybox.umd.min.js"></script><script src="https://cdn.jsdelivr.net/npm/instant.page/instantpage.min.js" type="module"></script><script src="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.js"></script><script>(() => {
const panguFn = () => {
if (typeof pangu === 'object') pangu.autoSpacingPage()
else {
btf.getScript('https://cdn.jsdelivr.net/npm/pangu/dist/browser/pangu.min.js')
.then(() => {
pangu.autoSpacingPage()
})
}
}
const panguInit = () => {
if (true){
GLOBAL_CONFIG_SITE.isPost && panguFn()
} else {
panguFn()
}
}
btf.addGlobalFn('pjaxComplete', panguInit, 'pangu')
document.addEventListener('DOMContentLoaded', panguInit)
})()</script><div class="js-pjax"><script>(async () => {
const showKatex = () => {
document.querySelectorAll('#article-container .katex').forEach(el => el.classList.add('katex-show'))
}
if (!window.katex_js_css) {
window.katex_js_css = true
await btf.getCSS('https://cdn.jsdelivr.net/npm/katex/dist/katex.min.css')
if (false) {
await btf.getScript('https://cdn.jsdelivr.net/npm/katex/dist/contrib/copy-tex.min.js')
}
}
showKatex()
})()</script><script>(() => {
const runMermaid = ele => {
window.loadMermaid = true
const theme = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'default'
ele.forEach((item, index) => {
const mermaidSrc = item.firstElementChild
const mermaidThemeConfig = `%%{init:{ 'theme':'${theme}'}}%%\n`
const mermaidID = `mermaid-${index}`
const mermaidDefinition = mermaidThemeConfig + mermaidSrc.textContent
const renderFn = mermaid.render(mermaidID, mermaidDefinition)
const renderMermaid = svg => {
mermaidSrc.insertAdjacentHTML('afterend', svg)
}
// mermaid v9 and v10 compatibility
typeof renderFn === 'string' ? renderMermaid(renderFn) : renderFn.then(({ svg }) => renderMermaid(svg))
})
}
const codeToMermaid = () => {
const codeMermaidEle = document.querySelectorAll('pre > code.mermaid')
if (codeMermaidEle.length === 0) return
codeMermaidEle.forEach(ele => {
const preEle = document.createElement('pre')
preEle.className = 'mermaid-src'
preEle.hidden = true
preEle.textContent = ele.textContent
const newEle = document.createElement('div')
newEle.className = 'mermaid-wrap'
newEle.appendChild(preEle)
ele.parentNode.replaceWith(newEle)
})
}
const loadMermaid = () => {
if (true) codeToMermaid()
const $mermaid = document.querySelectorAll('#article-container .mermaid-wrap')
if ($mermaid.length === 0) return
const runMermaidFn = () => runMermaid($mermaid)
btf.addGlobalFn('themeChange', runMermaidFn, 'mermaid')
window.loadMermaid ? runMermaidFn() : btf.getScript('https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js').then(runMermaidFn)
}
btf.addGlobalFn('encrypt', loadMermaid, 'mermaid')
window.pjax ? loadMermaid() : document.addEventListener('DOMContentLoaded', loadMermaid)
})()</script></div><script id="canvas_nest" defer="defer" color="165,165,165" opacity="0.8" zIndex="-1" count="99" mobile="false" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/dist/canvas-nest.min.js"></script><script src="https://cdn.jsdelivr.net/npm/pjax/pjax.min.js"></script><script>(() => {
const pjaxSelectors = ["head > title","#config-diff","#body-wrap","#rightside-config-hide","#rightside-config-show",".js-pjax"]
window.pjax = new Pjax({
elements: 'a:not([target="_blank"])',
selectors: pjaxSelectors,
cacheBust: false,
analytics: false,
scrollRestoration: false
})
const triggerPjaxFn = (val) => {
if (!val) return
Object.values(val).forEach(fn => fn())
}
document.addEventListener('pjax:send', () => {
// removeEventListener
btf.removeGlobalFnEvent('pjaxSendOnce')
btf.removeGlobalFnEvent('themeChange')
// reset readmode
const $bodyClassList = document.body.classList
if ($bodyClassList.contains('read-mode')) $bodyClassList.remove('read-mode')
triggerPjaxFn(window.globalFn.pjaxSend)
})
document.addEventListener('pjax:complete', () => {
btf.removeGlobalFnEvent('pjaxCompleteOnce')
document.querySelectorAll('script[data-pjax]').forEach(item => {
const newScript = document.createElement('script')
const content = item.text || item.textContent || item.innerHTML || ""
Array.from(item.attributes).forEach(attr => newScript.setAttribute(attr.name, attr.value))
newScript.appendChild(document.createTextNode(content))
item.parentNode.replaceChild(newScript, item)
})
triggerPjaxFn(window.globalFn.pjaxComplete)
})
document.addEventListener('pjax:error', e => {
if (e.request.status === 404) {
pjax.loadUrl('/404')
}
})
})()</script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div></body></html>