0 00:00:00,000 --> 00:00:06,640 1 00:00:06,760 --> 00:00:07,280 接下来 2 00:00:07,320 --> 00:00:11,360 我们对页表进行进一步的介绍 3 00:00:11,480 --> 00:00:12,800 4 00:00:12,880 --> 00:00:15,440 那么在页表里头呢 我们刚才说 5 00:00:15,480 --> 00:00:17,720 页表是负责从逻辑页号 6 00:00:17,760 --> 00:00:21,400 到物理页号之间的转换 7 00:00:21,440 --> 00:00:22,400 那这个转换 8 00:00:22,440 --> 00:00:25,000 它到底在里头是如何进行的 9 00:00:25,040 --> 00:00:28,520 在前面我们讲到过有一个页表 10 00:00:28,560 --> 00:00:30,360 页表负责完成这个转换 11 00:00:30,400 --> 00:00:32,080 那这里我们想说的是 12 00:00:32,120 --> 00:00:34,280 每一个逻辑页面 13 00:00:34,320 --> 00:00:35,880 对应着在页表里头呢 14 00:00:35,920 --> 00:00:37,400 有一个页表项 15 00:00:37,440 --> 00:00:40,120 这个页表项完成这个逻辑页号 16 00:00:40,160 --> 00:00:44,240 到物理帧号之间的转换 17 00:00:44,280 --> 00:00:46,080 那在这个转换过程当中呢 18 00:00:46,120 --> 00:00:48,400 我们还有一条需要说明的就是 19 00:00:48,440 --> 00:00:49,600 页表里的内容 20 00:00:49,640 --> 00:00:53,560 会随着程序的运行而发生变化 21 00:00:53,600 --> 00:00:56,440 这种变化呢 就使得我们有可能 22 00:00:56,480 --> 00:01:01,040 动态的去调整我分配给一个进程的 23 00:01:01,080 --> 00:01:03,320 内存空间的大小 24 00:01:03,360 --> 00:01:05,080 好 再有一个呢就是 25 00:01:05,120 --> 00:01:06,560 这个表放在哪儿呢 26 00:01:06,600 --> 00:01:09,760 这个表它在一个寄存器里 27 00:01:09,800 --> 00:01:11,240 页表基址寄存器 28 00:01:11,280 --> 00:01:13,080 在这个寄存器里 告诉你 29 00:01:13,120 --> 00:01:15,200 这个页表它的起始位置在哪 30 00:01:15,240 --> 00:01:17,760 好 有了你的逻辑页号之后 31 00:01:17,800 --> 00:01:20,200 那我就可以找到相应的表项 32 00:01:20,240 --> 00:01:21,600 那这个表项里头 33 00:01:21,640 --> 00:01:23,040 还有一些啥内容 34 00:01:23,080 --> 00:01:25,560 我们在前面的讨论里头呢是说 35 00:01:25,600 --> 00:01:28,320 里头有页帧号 36 00:01:28,360 --> 00:01:31,000 它的页帧在哪 37 00:01:31,040 --> 00:01:33,400 但是这个页表项里呢不止这些 38 00:01:33,440 --> 00:01:36,800 我们刚才说了 这里有一条动态 39 00:01:36,840 --> 00:01:39,840 我们可以在程序运行的过程当中 40 00:01:39,880 --> 00:01:44,360 来给它分配新的物理帧 41 00:01:44,400 --> 00:01:47,520 放到这个 它的进程地址空间里头 42 00:01:47,560 --> 00:01:50,400 那这时候呢 这种新加入的状态的改变 43 00:01:50,440 --> 00:01:52,880 就是靠这里的这些标志 44 00:01:52,920 --> 00:01:55,440 这里的状态来修改的 45 00:01:55,480 --> 00:01:57,840 那这里头呢是我们常用到的 46 00:01:57,880 --> 00:02:02,640 几个标志位 存在位 修改位 和引用位 47 00:02:02,680 --> 00:02:07,560 存在位是指我们有一个逻辑页号 48 00:02:07,600 --> 00:02:09,880 是否有一个物理页面 49 00:02:09,920 --> 00:02:11,800 物理帧和它相对应 50 00:02:11,840 --> 00:02:14,600 如果有 这个存在位就是1 51 00:02:14,640 --> 00:02:16,960 好 这一条是表示我们这个页 52 00:02:17,000 --> 00:02:19,440 对应的分配的动态性 53 00:02:19,480 --> 00:02:21,480 然后修改位和引用位 54 00:02:21,520 --> 00:02:25,880 是在我们后续的课程内容当中会用到的 55 00:02:25,920 --> 00:02:28,320 修改位是说我对应的这个页面 56 00:02:28,360 --> 00:02:30,400 里面内容是否修改了 57 00:02:30,440 --> 00:02:34,800 然后引用位 是指这个页面 58 00:02:34,840 --> 00:02:36,760 在过去一段时间里 59 00:02:36,800 --> 00:02:39,080 是否有过对它的引用 60 00:02:39,120 --> 00:02:41,000 是否访问过这个页面里的 61 00:02:41,040 --> 00:02:42,920 某一个存储单元 62 00:02:42,960 --> 00:02:48,040 刚才我们在页表中加了几个标志位 63 00:02:48,080 --> 00:02:49,560 那下面呢 我们就来通过 64 00:02:49,600 --> 00:02:51,400 一个实际的过程 65 00:02:51,440 --> 00:02:54,000 来看这几个标志位当中的 66 00:02:54,040 --> 00:02:56,480 存在位在里头作用 67 00:02:56,520 --> 00:03:01,160 好 这是一个实际的系统当中的示例 68 00:03:01,200 --> 00:03:04,480 这是逻辑地址空间 物理地址空间 69 00:03:04,520 --> 00:03:08,240 分页之后 每一页上页的结束页号 70 00:03:08,280 --> 00:03:09,960 页内偏移最后单位 71 00:03:10,000 --> 00:03:12,080 这地方呢是它的假设前提 72 00:03:12,120 --> 00:03:15,320 每页有2的10次方 73 00:03:15,360 --> 00:03:16,440 1024个字节 74 00:03:16,480 --> 00:03:18,600 总共是一个16位的系统 75 00:03:18,640 --> 00:03:21,400 这是每一K算一页 每一K算一页 76 00:03:21,440 --> 00:03:22,560 这是一个示意 77 00:03:22,600 --> 00:03:24,520 好 我们再看在这个 78 00:03:24,560 --> 00:03:26,440 我们前面讲的里头 79 00:03:26,480 --> 00:03:28,280 是我有一个逻辑地址 80 00:03:28,320 --> 00:03:29,880 CPU在执行指令的时候 81 00:03:29,920 --> 00:03:31,680 它转换成一个物理地址 82 00:03:31,720 --> 00:03:33,400 这是32K的 83 00:03:33,440 --> 00:03:34,440 那在这儿呢 84 00:03:34,480 --> 00:03:37,800 16位地址是从0到15 85 00:03:37,840 --> 00:03:40,240 那0到9是页内偏移 86 00:03:40,280 --> 00:03:43,080 10到15是页号 87 00:03:43,120 --> 00:03:46,480 那页表呢 在这里完成这个转换 88 00:03:46,520 --> 00:03:50,080 这个转换 如果说在我们 89 00:03:50,120 --> 00:03:51,840 没有标志位的情况下呢 90 00:03:51,880 --> 00:03:55,320 我是每一个逻辑页号都对应过来 91 00:03:55,360 --> 00:03:58,080 有一个帧号组合起来 92 00:03:58,120 --> 00:03:59,640 找到实际的位置 93 00:03:59,680 --> 00:04:01,920 那现在有了存在位之后呢 94 00:04:01,960 --> 00:04:03,560 我们就可能有些没有 95 00:04:03,600 --> 00:04:05,400 那这个存在位在哪呢 96 00:04:05,440 --> 00:04:07,680 就在这里 那你找过来的时候 97 00:04:07,720 --> 00:04:09,080 有可能某一页找不到 98 00:04:09,120 --> 00:04:10,760 那实际上就说这一页对应过来 99 00:04:10,800 --> 00:04:13,640 没有对应的物理帧号 100 00:04:13,680 --> 00:04:15,160 那这时候 相当于 101 00:04:15,200 --> 00:04:17,560 并没有给它分配相应的存储 102 00:04:17,600 --> 00:04:18,920 这使得我们可以在这里呢 103 00:04:18,960 --> 00:04:20,680 可以有动态的变化 104 00:04:20,720 --> 00:04:23,000 页式存储管理可以让我们 105 00:04:23,040 --> 00:04:25,600 不连续地分配内存空间 106 00:04:25,640 --> 00:04:28,240 但是它也会带来很多的问题 107 00:04:28,280 --> 00:04:29,920 那这儿列的第一个问题 108 00:04:29,960 --> 00:04:32,160 是它的访问性能的问题 109 00:04:32,200 --> 00:04:34,720 我们在没有页表的时候 110 00:04:34,760 --> 00:04:37,480 我要访问一个存储单元 111 00:04:37,520 --> 00:04:39,520 那我给你的是物理地址 112 00:04:39,560 --> 00:04:40,920 物理地址呢 直接去访问 113 00:04:40,960 --> 00:04:42,720 就能拿到你要的东西 114 00:04:42,760 --> 00:04:45,680 但现在因为我们为了实现 115 00:04:45,720 --> 00:04:49,080 非连续内存分配 116 00:04:49,120 --> 00:04:50,520 好 那这时候呢 117 00:04:50,560 --> 00:04:53,560 我们就会加了一个页表在中间 118 00:04:53,600 --> 00:04:55,360 那我每访问一个存储单元的时候呢 119 00:04:55,400 --> 00:04:56,720 我都需要先知道 120 00:04:56,760 --> 00:05:00,160 它逻辑页号对应的物理页帧号是多少 121 00:05:00,200 --> 00:05:02,520 那这个转换都要求我去访问页表 122 00:05:02,560 --> 00:05:03,400 那这样一来的话 123 00:05:03,440 --> 00:05:05,160 我的访问就变成是两次了 124 00:05:05,200 --> 00:05:06,880 先读页表项 125 00:05:06,920 --> 00:05:10,600 看看那个对应的物理页帧号是多少 126 00:05:10,640 --> 00:05:13,040 然后再依据这个页帧号 127 00:05:13,080 --> 00:05:15,880 和页内的偏移合到一起得到物理地址 128 00:05:15,920 --> 00:05:18,640 再去访问实际的内容 129 00:05:18,680 --> 00:05:23,720 这样的话它的读写性能就会大幅度下降 130 00:05:23,760 --> 00:05:25,960 读写量也会大幅度增加 131 00:05:26,000 --> 00:05:28,560 第二个问题是说我们有了一个页表 132 00:05:28,600 --> 00:05:31,640 如果说这时候我的内存地址空间很大 133 00:05:31,680 --> 00:05:35,240 这个页表的存储容量也是不能忽视的 134 00:05:35,280 --> 00:05:37,120 它可能会很大 135 00:05:37,160 --> 00:05:39,160 像我们刚才说那个示例系统里头 136 00:05:39,200 --> 00:05:41,680 32K的物理内存 137 00:05:41,720 --> 00:05:46,080 那 1K占一项 那我就32项 138 00:05:46,120 --> 00:05:48,280 如果每一项占4字节的话 139 00:05:48,320 --> 00:05:49,920 那就是128字节 140 00:05:49,960 --> 00:05:52,680 这个量呢相对来说很小 141 00:05:52,720 --> 00:05:56,880 而我们现在实际系统已经达到了64位 142 00:05:56,920 --> 00:05:58,200 也就是说 我的地址总线 143 00:05:58,240 --> 00:06:00,440 可能会是64位地址总线 144 00:06:00,480 --> 00:06:02,240 如果这时候 你仍然使用 145 00:06:02,280 --> 00:06:05,760 1K作为它的页面的大小 146 00:06:05,800 --> 00:06:08,520 那这个时候你会有多少个页面 147 00:06:08,560 --> 00:06:11,320 2的64次方 148 00:06:11,360 --> 00:06:13,240 页的大小是2的10次方 149 00:06:13,280 --> 00:06:18,680 那就是2的54次方个页面 150 00:06:18,720 --> 00:06:21,280 2的54次方个页面 151 00:06:21,320 --> 00:06:28,080 如果每一个页表项占64位的地址 152 00:06:28,120 --> 00:06:29,800 光地址的话那就是8 8 64 153 00:06:29,840 --> 00:06:30,880 那就要占8个字节 154 00:06:30,920 --> 00:06:33,280 那这样的话你的一个页表项 155 00:06:33,320 --> 00:06:35,080 至少是8个字节 156 00:06:35,120 --> 00:06:37,360 好 实际上如果再加上标志位 157 00:06:37,400 --> 00:06:38,360 它仍然是不够的 158 00:06:38,400 --> 00:06:39,640 假定它就是8个 159 00:06:39,680 --> 00:06:41,720 那这时候2的54次方 160 00:06:41,760 --> 00:06:43,960 每一个占8字节 那 161 00:06:44,000 --> 00:06:46,600 就是2的57次方 162 00:06:46,640 --> 00:06:50,320 那2的57次方的存储区域来存你的页表 163 00:06:50,360 --> 00:06:52,760 这个空间也是足够大的 164 00:06:52,800 --> 00:06:56,960 好 那针对这种页表引入的好处之外 165 00:06:57,000 --> 00:06:58,360 它又带来的麻烦 166 00:06:58,400 --> 00:07:01,080 这些麻烦我们怎么处理呢 167 00:07:01,120 --> 00:07:03,840 那这是给出的两种做法 168 00:07:03,880 --> 00:07:06,320 第一种呢是缓存 169 00:07:06,360 --> 00:07:09,360 那由于我们在程序执行的时候 170 00:07:09,400 --> 00:07:10,880 访问的数据 访问的代码 171 00:07:10,920 --> 00:07:14,080 它都具有一定的相邻性 172 00:07:14,120 --> 00:07:15,680 我访问执行一条指令 173 00:07:15,720 --> 00:07:18,040 我接下来执行的是它的下一条指令 174 00:07:18,080 --> 00:07:19,720 我访问数组的第一个元素 175 00:07:19,760 --> 00:07:22,880 我接下来可能访问是它第二个元素 176 00:07:22,920 --> 00:07:25,480 这时候它们可能都会在一页里头 177 00:07:25,520 --> 00:07:26,640 这种可能性比较大 178 00:07:26,680 --> 00:07:27,680 好 这样一来的话 179 00:07:27,720 --> 00:07:30,160 把你得到的页表项缓存下来 180 00:07:30,200 --> 00:07:31,880 我下一次的时候利用这缓存 181 00:07:31,920 --> 00:07:32,800 极大的可能性 182 00:07:32,840 --> 00:07:34,600 我是可以直接访问到物理内存的 183 00:07:34,640 --> 00:07:36,000 这样的话 就可以把你的 184 00:07:36,040 --> 00:07:39,000 这个访问次数减下来 185 00:07:39,040 --> 00:07:41,920 第二个是说我的页表很大 186 00:07:41,960 --> 00:07:44,920 一个很长的表 那很麻烦 187 00:07:44,960 --> 00:07:46,800 那我们对付这种长的问题 188 00:07:46,840 --> 00:07:47,840 的做法是什么呢 189 00:07:47,880 --> 00:07:49,840 我把它切段 好 间接访问 190 00:07:49,880 --> 00:07:51,600 先找它是在哪个子表里头 191 00:07:51,640 --> 00:07:53,160 然后在子表里再去找 192 00:07:53,200 --> 00:07:55,520 好 这就是我们说的间接访问 193 00:07:55,560 --> 00:07:56,440 这种间接访问 194 00:07:56,480 --> 00:07:59,440 对应过来呢就是多级页表 195 00:07:59,480 --> 00:08:00,760 那我们下面呢 196 00:08:00,800 --> 00:08:04,760 会深入再讨论快表和多级页表 197 00:08:04,800 --> 00:08:04,840