0 00:00:00,000 --> 00:00:06,400 1 00:00:06,600 --> 00:00:11,400 接下来我们讲快表和多级页表 2 00:00:11,440 --> 00:00:12,600 那在刚才我们说 3 00:00:12,640 --> 00:00:14,640 快表是利用缓存的机制 4 00:00:14,680 --> 00:00:17,040 来减少对内存的访问 5 00:00:17,080 --> 00:00:20,720 而多级页表 是通过间接引用的方式 6 00:00:20,760 --> 00:00:23,360 来减少页表的长度 7 00:00:23,400 --> 00:00:25,560 那么它们具体怎么做的呢 8 00:00:25,600 --> 00:00:30,040 快表实际上就是把近期访问过的页表项 9 00:00:30,080 --> 00:00:31,720 缓存到CPU里头 10 00:00:31,760 --> 00:00:35,240 底下是我们在前面没有使用快表的时候 11 00:00:35,280 --> 00:00:39,240 正常情况下 你获取物理页号的过程 12 00:00:39,280 --> 00:00:43,520 逻辑页号在内存当中去查页表 13 00:00:43,560 --> 00:00:49,000 找到物理页号 然后得到物理地址 14 00:00:49,040 --> 00:00:51,880 那现在在这里缓存之后怎么办呢 15 00:00:51,920 --> 00:00:55,400 我在CPU里头 加上一组关联存储器 16 00:00:55,440 --> 00:00:56,840 关联存储器是什么呢 17 00:00:56,880 --> 00:00:59,160 关联存储器是说我这里有一个key 18 00:00:59,200 --> 00:01:01,800 我进来之后它可以并行的 19 00:01:01,840 --> 00:01:04,920 同时查所有的这些表项 20 00:01:04,960 --> 00:01:07,960 有匹配的 把匹配的找出来 21 00:01:08,000 --> 00:01:08,880 那你说这里 22 00:01:08,920 --> 00:01:12,120 原先在内存里访问一次你觉得费事 23 00:01:12,160 --> 00:01:14,160 到这里来你访问这么多次 24 00:01:14,200 --> 00:01:15,600 它就不费事吗 25 00:01:15,640 --> 00:01:16,440 实际上在这里头呢 26 00:01:16,480 --> 00:01:18,240 由于是在CPU里头 27 00:01:18,280 --> 00:01:19,720 它的速度会很快 28 00:01:19,760 --> 00:01:23,440 当然由于它的速度快 成本高 功耗大 29 00:01:23,480 --> 00:01:25,440 所以这个地方不能做的很大 30 00:01:25,480 --> 00:01:27,120 如果说匹配得上 31 00:01:27,160 --> 00:01:30,120 那这时候呢它直接得到它的物理页号 32 00:01:30,160 --> 00:01:32,480 也就相当于我逻辑页号作为key 33 00:01:32,520 --> 00:01:34,000 找到它的物理页号 34 00:01:34,040 --> 00:01:35,720 那我就得到你的物理页号 35 00:01:35,760 --> 00:01:38,080 这就不需要到内存当中访问了 36 00:01:38,120 --> 00:01:40,000 如果说在这里头你找的时候 37 00:01:40,040 --> 00:01:41,640 因为你这里容量很小 38 00:01:41,680 --> 00:01:43,720 肯定没有办法把整个页表 39 00:01:43,760 --> 00:01:46,600 全部装到这个CPU里头去 40 00:01:46,640 --> 00:01:48,240 好 那这些有不命中的 41 00:01:48,280 --> 00:01:49,880 那不命中的时候呢 42 00:01:49,920 --> 00:01:51,240 它就会从这儿 43 00:01:51,280 --> 00:01:54,640 你得再去找内存当中的页表 44 00:01:54,680 --> 00:01:56,560 这时候你只能是两次访问了 45 00:01:56,600 --> 00:01:58,080 好 找到这个页表之后呢 46 00:01:58,120 --> 00:02:01,240 我得到它的页帧号 47 00:02:01,280 --> 00:02:03,160 同时我把这个内容 48 00:02:03,200 --> 00:02:06,800 再缓存到CPU里的快表里头去 49 00:02:06,840 --> 00:02:10,840 下次再访问这一页里的数据的时候 50 00:02:10,880 --> 00:02:13,680 你就不必要再去访问内存了 51 00:02:13,720 --> 00:02:15,200 好 如果说我们在这里头 52 00:02:15,240 --> 00:02:19,760 99%的访问我都是在这个TLB里 53 00:02:19,800 --> 00:02:21,120 快表里命中的 54 00:02:21,160 --> 00:02:23,720 那只有一次1% 55 00:02:23,760 --> 00:02:26,640 是要到物理内存当中去查页表的 56 00:02:26,680 --> 00:02:29,560 这时我们的性能就能大幅度提高 57 00:02:29,600 --> 00:02:32,840 这是快表的基本原理 58 00:02:32,880 --> 00:02:35,680 好 接下来我们介绍多级页表 59 00:02:35,720 --> 00:02:37,880 多级页表是通过间接引用 60 00:02:37,920 --> 00:02:41,680 将页号分成若干级 61 00:02:41,720 --> 00:02:42,800 比如说在这里头 62 00:02:42,840 --> 00:02:46,400 我们原来的逻辑地址的格式 63 00:02:46,440 --> 00:02:48,400 是页号加页内偏移 64 00:02:48,440 --> 00:02:50,840 现在变成了三级页号 65 00:02:50,880 --> 00:02:54,720 P1 P2 P3 然后再加上页内偏移 66 00:02:54,760 --> 00:02:58,320 和它相对应的 我们的页表呢 67 00:02:58,360 --> 00:03:01,040 也会因此而形成一个树状结构 68 00:03:01,080 --> 00:03:05,080 比如说原来一张大的线性页表 69 00:03:05,120 --> 00:03:07,080 我把它切成若干段 70 00:03:07,120 --> 00:03:08,920 这切到段的个数呢 71 00:03:08,960 --> 00:03:13,080 和你最后一级页表的宽度是一致的 72 00:03:13,120 --> 00:03:17,160 然后它每一个子页表的起头呢 73 00:03:17,200 --> 00:03:20,960 作为上一级页表的物理页号 74 00:03:21,000 --> 00:03:22,480 填到上一级页表当中 75 00:03:22,520 --> 00:03:26,040 在第二级的页表的宽度 76 00:03:26,080 --> 00:03:29,240 和你第二级页号的宽度是一致的 77 00:03:29,280 --> 00:03:32,880 然后再一个 第二级页表的起头 78 00:03:32,920 --> 00:03:36,680 再作为第一级页表项的物理页号 79 00:03:36,720 --> 00:03:38,680 那这时候它的项数 80 00:03:38,720 --> 00:03:41,960 和你第一级页表的宽度是相一致的 81 00:03:42,000 --> 00:03:43,360 在这种情况下 82 00:03:43,400 --> 00:03:49,160 我们要访问相应的物理内存单元 83 00:03:49,200 --> 00:03:50,280 那怎么访问呢 84 00:03:50,320 --> 00:03:53,520 是从第一级查第二级 再查第三级 85 00:03:53,560 --> 00:03:58,000 那这时候我们整个访问次数就是K+1 86 00:03:58,040 --> 00:04:00,040 你这里是三级 那就是四次 87 00:04:00,080 --> 00:04:04,480 具体的访问过程是这样的 88 00:04:04,520 --> 00:04:08,080 第一级作为第一级页表的偏移 89 00:04:08,120 --> 00:04:10,320 找到第二级页表上的起始 90 00:04:10,360 --> 00:04:12,120 第二级页表项 91 00:04:12,160 --> 00:04:13,800 再作为在第二级页表当中的偏移 92 00:04:13,840 --> 00:04:17,720 加在一起找到第三级页表项的 93 00:04:17,760 --> 00:04:19,880 起始页号 物理页号 94 00:04:19,920 --> 00:04:22,680 然后这地方呢 这一页呢 95 00:04:22,720 --> 00:04:25,360 每一页这些页表都是和页相对齐的 96 00:04:25,400 --> 00:04:28,280 所以从这儿呢它就不再有页内偏移 97 00:04:28,320 --> 00:04:29,760 好 然后从这儿找到 98 00:04:29,800 --> 00:04:31,800 最后的 你要实际访问的 99 00:04:31,840 --> 00:04:35,520 那个内存单元的物理页号 100 00:04:35,560 --> 00:04:38,920 再加上最后一次物理内存的访问 101 00:04:38,960 --> 00:04:40,600 那通过这种方式 102 00:04:40,640 --> 00:04:44,880 我们可以有效地减少每一级页表的长度 103 00:04:44,920 --> 00:04:49,480 那如果说 你是所有的页表项都存在的话 104 00:04:49,520 --> 00:04:53,200 你用多级页表实际上对它的存储并没有减少 105 00:04:53,240 --> 00:04:56,080 但实际上 我们实际运行的进程呢 106 00:04:56,120 --> 00:04:59,760 多数并不会用到整个所有的页表 107 00:04:59,800 --> 00:05:03,200 所有的内存地址空间 逻辑地址空间 108 00:05:03,240 --> 00:05:04,320 在这种情况下 109 00:05:04,360 --> 00:05:07,720 我们可以通过各级页表当中的存在位 110 00:05:07,760 --> 00:05:10,480 把那些不存在的给省掉 111 00:05:10,520 --> 00:05:12,400 如果说我在第一级页表里头 112 00:05:12,440 --> 00:05:15,080 有一个下一块区域都不存在的话 113 00:05:15,120 --> 00:05:15,880 那么这样一来 114 00:05:15,920 --> 00:05:19,360 我节省出来的空间就会大幅度增加 115 00:05:19,400 --> 00:05:21,640 我使用的空间就会大幅度减少 116 00:05:21,680 --> 00:05:22,880 好 用这种方式呢 117 00:05:22,920 --> 00:05:24,400 实际上我们使用多级页表呢 118 00:05:24,440 --> 00:05:26,960 可以有效地的减少页表的大小 119 00:05:27,000 --> 00:05:28,600 下面我们通过一个简单的 120 00:05:28,640 --> 00:05:30,560 但是更具体的例子 二级页表 121 00:05:30,600 --> 00:05:32,160 我们看它是怎么做的 122 00:05:32,200 --> 00:05:37,360 这儿呢我们把20位的地址总线 123 00:05:37,400 --> 00:05:39,720 把它切成了三段 124 00:05:39,760 --> 00:05:41,720 0到10 1K 125 00:05:41,760 --> 00:05:45,400 10位作为页内偏移 126 00:05:45,440 --> 00:05:49,920 然后前面切成两个五位的页号 127 00:05:49,960 --> 00:05:51,160 第一级和第二级 128 00:05:51,200 --> 00:05:53,360 那在实际访问的时候是什么样 129 00:05:53,400 --> 00:05:56,040 这是这里头第一级 130 00:05:56,080 --> 00:05:58,280 那第一级页表的起头在哪呢 131 00:05:58,320 --> 00:05:59,920 它是写到固定寄存器里的 132 00:05:59,960 --> 00:06:01,360 在因特尔的CPU上 133 00:06:01,400 --> 00:06:03,360 有一个叫CR3的寄存器 134 00:06:03,400 --> 00:06:07,040 好 在这个寄存器里头存的起始位置 135 00:06:07,080 --> 00:06:09,000 加上你的第一级的页号 136 00:06:09,040 --> 00:06:11,240 作为它的索引 下标 137 00:06:11,280 --> 00:06:12,960 找到相应的页表项 138 00:06:13,000 --> 00:06:16,800 这是第二级页表的起始页号 139 00:06:16,840 --> 00:06:19,000 好 那第二级页表呢 140 00:06:19,040 --> 00:06:20,280 你找这个起始位置 141 00:06:20,320 --> 00:06:22,920 加上第二级的页表号 142 00:06:22,960 --> 00:06:28,240 把它俩找到你的实际的物理页号 143 00:06:28,280 --> 00:06:32,840 那这时候把偏移直接搬过来 144 00:06:32,880 --> 00:06:35,360 那就得到你的物理地址了 145 00:06:35,400 --> 00:06:36,720 有了这样一种做法呢 146 00:06:36,760 --> 00:06:38,280 我们就可以很方便地 147 00:06:38,320 --> 00:06:42,520 利用多级页表减少你整个页表的长度 148 00:06:42,560 --> 00:06:42,600