0 00:00:00,000 --> 00:00:06,800 1 00:00:07,000 --> 00:00:08,040 好 那我们接下来 2 00:00:08,080 --> 00:00:10,400 讲一下这个copy-on-write机制 3 00:00:10,440 --> 00:00:11,960 这个呢和我们的这个 4 00:00:12,000 --> 00:00:13,960 进程管理和内存管理紧密相连 5 00:00:14,040 --> 00:00:15,480 也是我们的challenge练习 6 00:00:15,560 --> 00:00:16,640 所以说呢这部分 7 00:00:16,720 --> 00:00:18,240 可以理解为是一个扩展内容 8 00:00:18,280 --> 00:00:19,560 大家了解一下OK 9 00:00:19,600 --> 00:00:20,960 如果感兴趣同学 10 00:00:21,000 --> 00:00:24,880 我们鼓励大家做一下尝试 11 00:00:24,920 --> 00:00:26,520 copy-on-write机制呢 12 00:00:26,560 --> 00:00:28,160 在应用方面也讲到是说 13 00:00:28,200 --> 00:00:30,120 同一种机制使得我们 14 00:00:30,160 --> 00:00:31,800 在创建子进程的时候能够 15 00:00:31,840 --> 00:00:34,200 共享父进程的内存空间 16 00:00:34,240 --> 00:00:37,080 从而可以节省内存的占用 17 00:00:37,120 --> 00:00:39,000 那我们举个例子看process 18 00:00:39,040 --> 00:00:42,320 一开始process A这是一个父进程 19 00:00:42,360 --> 00:00:43,880 它有一个vma和一个 20 00:00:43,920 --> 00:00:46,680 它的page table都是可读可写的 21 00:00:46,720 --> 00:00:47,920 内存空间可读可写 22 00:00:47,960 --> 00:00:52,120 好 通过do_fork呢我们创建了进程B 23 00:00:52,160 --> 00:00:53,880 那么进程B呢 24 00:00:53,920 --> 00:00:55,480 它可以重用 25 00:00:55,520 --> 00:00:57,920 重用这个进程A的地址空间 26 00:00:57,960 --> 00:00:59,440 跟我们前面讲的不太一样 27 00:00:59,480 --> 00:01:01,840 我们前面是说我们要复制一份 28 00:01:01,880 --> 00:01:03,840 把整个内存空间要做一次拷贝 29 00:01:03,880 --> 00:01:06,000 而且这里面呢它不做拷贝 30 00:01:06,040 --> 00:01:08,360 他们是共用一块内存空间 31 00:01:08,400 --> 00:01:09,560 从而可以使得 32 00:01:09,600 --> 00:01:12,280 空间的占用量大大减少 33 00:01:12,320 --> 00:01:17,480 那这样情况会不会出现问题呢 34 00:01:17,520 --> 00:01:20,080 如果是只读的话那没问题 35 00:01:20,120 --> 00:01:21,360 你可以看到 36 00:01:21,400 --> 00:01:23,200 它们因为共用是一块区域 37 00:01:23,240 --> 00:01:25,720 那么它都做读操作 38 00:01:25,760 --> 00:01:27,120 不在内存做改变的话 39 00:01:27,160 --> 00:01:29,360 没任何问题 40 00:01:29,400 --> 00:01:31,320 如果它是写操作就有问题了 41 00:01:31,360 --> 00:01:33,120 为什么呢 42 00:01:33,160 --> 00:01:34,320 因为一个进程 43 00:01:34,360 --> 00:01:36,080 写了一个内存地址之后呢 44 00:01:36,120 --> 00:01:38,200 另一个进程也许不希望 45 00:01:38,240 --> 00:01:40,560 这个内存地址被做修改 46 00:01:40,600 --> 00:01:41,800 也意味着在这种情况下 47 00:01:41,840 --> 00:01:43,720 我们对写要做个特殊的操作 48 00:01:43,760 --> 00:01:45,840 我们这里面做了什么特殊的处理呢 49 00:01:45,880 --> 00:01:47,840 会对这个pgdir我们页表呢 50 00:01:47,880 --> 00:01:51,480 把相应的那个内存空间设成只读 51 00:01:51,520 --> 00:01:52,680 这带来什么问题 52 00:01:52,720 --> 00:01:54,360 一旦某一个进程 53 00:01:54,400 --> 00:01:56,840 无论是进程B还是进程A 54 00:01:56,880 --> 00:01:58,800 就无论父子进程哪一个 55 00:01:58,840 --> 00:01:59,960 做了写操作 56 00:02:00,000 --> 00:02:02,040 由于你的页表里面对应的内容 57 00:02:02,080 --> 00:02:03,680 设置那个属性 58 00:02:03,720 --> 00:02:05,960 就页表下面设置属性是只读 59 00:02:06,000 --> 00:02:07,400 所以一旦做写操作会产生什么 60 00:02:07,440 --> 00:02:08,720 大家想一想 61 00:02:08,760 --> 00:02:13,400 对 我们可以看到它会产生一个page fault 62 00:02:13,440 --> 00:02:17,400 因为对只读页做了一个写操作 63 00:02:17,440 --> 00:02:19,920 违背了这个内存的管理的原则 64 00:02:19,960 --> 00:02:21,560 所以说会产生page fault 65 00:02:21,600 --> 00:02:24,480 但这个page fault正是我们需要利用的 66 00:02:24,520 --> 00:02:26,560 你可以看着 在一般情况下 67 00:02:26,600 --> 00:02:29,880 我们的vma是表明了这个进程 68 00:02:29,920 --> 00:02:31,560 它的一个合法地址空间 69 00:02:31,600 --> 00:02:33,160 是具有可读可写的属性的 70 00:02:33,200 --> 00:02:34,680 特别是对于数据段来说 71 00:02:34,720 --> 00:02:37,040 但是我们的页表是不是只读 72 00:02:37,080 --> 00:02:38,040 这其实有一个矛盾 73 00:02:38,080 --> 00:02:40,640 这个矛盾就会导致产生一个page fault 74 00:02:40,680 --> 00:02:42,520 而我们copy-on-write机制呢 75 00:02:42,560 --> 00:02:44,400 正是利用这个矛盾 76 00:02:44,440 --> 00:02:47,080 来建立了有效的这个 77 00:02:47,120 --> 00:02:49,680 省空间这么一个操作过程 78 00:02:49,720 --> 00:02:50,720 一旦出现page fault 79 00:02:50,760 --> 00:02:54,640 也意味着在这一点上这两个进程 80 00:02:54,680 --> 00:02:57,840 需要有各自独立的空间了 81 00:02:57,880 --> 00:03:00,320 如果是B process B 82 00:03:00,360 --> 00:03:02,040 对这个空间做了修改 83 00:03:02,080 --> 00:03:05,080 那我们希望是什么呢 84 00:03:05,120 --> 00:03:06,360 在这一块空间 85 00:03:06,400 --> 00:03:07,840 这个以页为单位 86 00:03:07,880 --> 00:03:09,120 这个空间 87 00:03:09,160 --> 00:03:11,720 它的空间呢它有它自己的页表项 88 00:03:11,760 --> 00:03:12,800 而同理 89 00:03:12,840 --> 00:03:16,280 这个process A保持原有的页表项不变 90 00:03:16,320 --> 00:03:18,480 这里面是被修改过的内容 91 00:03:18,520 --> 00:03:21,040 而这里面是没有被修改的内容 92 00:03:21,080 --> 00:03:24,040 可以看到这里面就完成了一个分割 93 00:03:24,080 --> 00:03:25,560 但是对于只读的那些 94 00:03:25,600 --> 00:03:27,400 内存页没有任何变化 95 00:03:27,440 --> 00:03:30,280 所以说仅仅是需要有写的时候 96 00:03:30,320 --> 00:03:32,720 内存会增加一小部分内容 97 00:03:32,760 --> 00:03:34,120 但是对那些只读的 98 00:03:34,160 --> 00:03:34,920 依然没有增加 99 00:03:34,960 --> 00:03:37,080 所以空间copy-on-write就在哪呢 100 00:03:37,120 --> 00:03:39,200 当做一个write操作的时候呢 101 00:03:39,240 --> 00:03:45,360 会完成页表内容一个复制和更新 102 00:03:45,400 --> 00:03:48,880 这从而使得我们整个进程创建的开销 103 00:03:48,920 --> 00:03:49,560 因为我们前面 104 00:03:49,600 --> 00:03:52,680 没有拷贝整个内存空间的开销 105 00:03:52,720 --> 00:03:53,640 大大减少 106 00:03:53,680 --> 00:03:56,080 且有效的节省空间 107 00:03:56,120 --> 00:03:58,320 还保证了这个整个进程 108 00:03:58,360 --> 00:04:01,280 进程A和进程B能够正确执行 109 00:04:01,320 --> 00:04:04,680 但是光做到这一步 110 00:04:04,720 --> 00:04:06,560 就是完成一次复制 111 00:04:06,600 --> 00:04:09,040 是否就够了呢 112 00:04:09,080 --> 00:04:11,480 对于简单的情况 113 00:04:11,520 --> 00:04:13,000 这样确实OK 114 00:04:13,040 --> 00:04:14,520 我们有各自的内存页 115 00:04:14,560 --> 00:04:15,960 它的出发点是在于 116 00:04:16,000 --> 00:04:17,240 你做了一次写操作 117 00:04:17,280 --> 00:04:19,280 那么它可以把这个页表项改了 118 00:04:19,320 --> 00:04:22,000 对应页表的那个物理地址空间 119 00:04:22,040 --> 00:04:23,760 做了两份不同的内容 120 00:04:23,800 --> 00:04:25,280 OK 没问题 121 00:04:25,320 --> 00:04:27,080 但如果你在深入考虑一下 122 00:04:27,120 --> 00:04:28,800 可能会出现一种新的一种现象 123 00:04:28,840 --> 00:04:31,560 就是当引入copy-on-write机制之后呢 124 00:04:31,600 --> 00:04:32,600 会出现哪种现象呢 125 00:04:32,640 --> 00:04:34,480 就是一个物理的页 126 00:04:34,520 --> 00:04:37,320 它会有多个虚拟页指向它 127 00:04:37,360 --> 00:04:38,600 因为有不同的进程 128 00:04:38,640 --> 00:04:40,960 不同的进程指向同一个物理页 129 00:04:41,000 --> 00:04:42,600 但它的虚拟地址空间是不一样 130 00:04:42,640 --> 00:04:45,120 分属于不同的进程 131 00:04:45,160 --> 00:04:47,000 那这里面我们对页的管理 132 00:04:47,040 --> 00:04:47,960 特别是物理页的管理 133 00:04:48,000 --> 00:04:49,600 就需要有新的一种机制 134 00:04:49,640 --> 00:04:51,640 因为它的reference 135 00:04:51,680 --> 00:04:52,640 就是引用这个物理页 136 00:04:52,680 --> 00:04:54,240 这个个数是不一样的 137 00:04:54,280 --> 00:04:55,480 有多少个进程引用 138 00:04:55,520 --> 00:04:56,640 也意味着它这个 139 00:04:56,680 --> 00:04:58,280 引用计数应该是多少 140 00:04:58,320 --> 00:04:59,920 那这个个数很重要 141 00:04:59,960 --> 00:05:01,840 我们会看到为什么呢 142 00:05:01,880 --> 00:05:04,360 因为在进程的运行过程中 143 00:05:04,400 --> 00:05:06,400 我们在讲lab3的时候 144 00:05:06,440 --> 00:05:08,200 给大家提到过可能会出现 145 00:05:08,240 --> 00:05:10,440 swapin swapout这个换入换出 146 00:05:10,480 --> 00:05:13,480 那当把一个页换出出去之后呢 147 00:05:13,520 --> 00:05:16,400 也许这个页和另一个进程 148 00:05:16,440 --> 00:05:17,360 所对应的虚拟页 149 00:05:17,400 --> 00:05:19,320 共享了同一个物理页 150 00:05:19,360 --> 00:05:21,720 那怎么去有效的换入换出 151 00:05:21,760 --> 00:05:23,880 且能保证放在这里面 152 00:05:23,920 --> 00:05:26,720 这个页的内容和被其它进程 153 00:05:26,760 --> 00:05:29,480 所用到的那个页的内容是一致的 154 00:05:29,520 --> 00:05:31,120 因为这里面出现一种新的状态 155 00:05:31,160 --> 00:05:34,880 有可能那个页既在这里面 156 00:05:34,920 --> 00:05:37,040 也在内存中 157 00:05:37,080 --> 00:05:39,720 那其实会导致一个问题的复杂 158 00:05:39,760 --> 00:05:41,880 当然我们在这里面呢 159 00:05:41,920 --> 00:05:44,040 可以不考虑这些情况 160 00:05:44,080 --> 00:05:45,440 我们只考虑最简单的 161 00:05:45,480 --> 00:05:47,480 没有换入换出的情况下 162 00:05:47,520 --> 00:05:49,880 那么他只需要有一个reference counter 163 00:05:49,920 --> 00:05:50,640 对于每一个物理页 164 00:05:50,680 --> 00:05:52,080 有一个reference counter就OK了 165 00:05:52,120 --> 00:05:54,000 但一旦要再进一步考虑到 166 00:05:54,040 --> 00:05:56,760 有这个我们说的swap in swap out 167 00:05:56,800 --> 00:05:58,400 这种虚存管理机制之后呢 168 00:05:58,440 --> 00:06:00,800 使得这个页共享这种特点 169 00:06:00,840 --> 00:06:01,720 那么还有一些 170 00:06:01,760 --> 00:06:04,040 更新的一些应对的一些手段 171 00:06:04,080 --> 00:06:07,240 才能保证整个这个数据是一致的 172 00:06:07,280 --> 00:06:10,000 否则会出现一系列的问题 173 00:06:10,040 --> 00:06:12,480 为此呢我们可以考虑简单一点 174 00:06:12,520 --> 00:06:13,880 如果把这个swap fs 175 00:06:13,920 --> 00:06:15,080 这一块 176 00:06:15,120 --> 00:06:16,160 关于虚存管理这一块 177 00:06:16,200 --> 00:06:18,000 稍微靠后的话我们不考虑的话 178 00:06:18,040 --> 00:06:20,640 那我们还需要去考虑什么呢 179 00:06:20,680 --> 00:06:21,840 就是引用计数 180 00:06:21,880 --> 00:06:25,920 那你会在实现copy_range do_pgfault 181 00:06:25,960 --> 00:06:28,080 还有dup_mmap 182 00:06:28,120 --> 00:06:29,560 copy_range干什么复制 183 00:06:29,600 --> 00:06:31,680 do_pgfault完成虚存管理 184 00:06:31,720 --> 00:06:33,080 dup_mmap呢 185 00:06:33,120 --> 00:06:37,360 是完成这个对内存管理的一个复制 186 00:06:37,400 --> 00:06:39,960 那这些功能里面都要有所考虑 187 00:06:40,000 --> 00:06:44,800 关于页的共享这么一个属性 188 00:06:44,840 --> 00:06:46,760 这是大家在做练习的时候 189 00:06:46,800 --> 00:06:48,640 可能需要去注意的一点 190 00:06:48,680 --> 00:06:51,240 还有呢你们在写的时候 191 00:06:51,280 --> 00:06:54,000 要另外需要注意的有足够的test case 192 00:06:54,040 --> 00:06:55,360 就是要有一些bench mark 193 00:06:55,400 --> 00:06:58,960 来测试你的代码是正确的 194 00:06:59,000 --> 00:07:00,640 这里面关于这个 195 00:07:00,680 --> 00:07:04,280 内存的页的这个属性状态 196 00:07:04,320 --> 00:07:06,200 就随着运行在不同阶段 197 00:07:06,240 --> 00:07:07,840 它的状态是有一定的变化 198 00:07:07,880 --> 00:07:10,040 就类似于我们进程管理的时候 199 00:07:10,080 --> 00:07:12,520 进程运行过程中它有状态变化 200 00:07:12,560 --> 00:07:15,360 当由于你引入这个copy-on-write机制之后 201 00:07:15,400 --> 00:07:17,080 还有虚存管理机制之后 202 00:07:17,120 --> 00:07:19,000 它这个内存的这个页的状态 203 00:07:19,040 --> 00:07:20,480 也是一个动态变化的 204 00:07:20,520 --> 00:07:22,000 那这些能否形成一个 205 00:07:22,040 --> 00:07:23,440 类似于我们进程管理 206 00:07:23,480 --> 00:07:25,120 一个生命周期一个图 207 00:07:25,160 --> 00:07:26,760 这里面有一个状态转换图 208 00:07:26,800 --> 00:07:29,600 这是我们在做这个challenge实验的时候 209 00:07:29,640 --> 00:07:30,800 需要考虑内容 210 00:07:30,840 --> 00:07:33,960 你怎么证明图的转换关系是对的 211 00:07:34,000 --> 00:07:34,960 很有挑战 212 00:07:35,000 --> 00:07:37,960 那我们鼓励同学去尝试一下 213 00:07:38,000 --> 00:07:38,720 我们这里面呢 214 00:07:38,760 --> 00:07:40,480 给大家做一个简单介绍而已 215 00:07:40,520 --> 00:07:42,280 大家需要通过实践 216 00:07:42,320 --> 00:07:43,840 才能够得到一个 217 00:07:43,880 --> 00:07:48,760 相对合理和正确一个解答 218 00:07:48,800 --> 00:07:50,480 好 那我就给大家讲完了 219 00:07:50,520 --> 00:07:52,240 就是关于lab5的情况 220 00:07:52,280 --> 00:07:53,680 这里面重点是说 221 00:07:53,720 --> 00:07:56,240 你要去理解怎么去构造出 222 00:07:56,280 --> 00:07:58,000 一个用户态的进程 223 00:07:58,040 --> 00:07:59,280 怎么去完成 224 00:07:59,320 --> 00:08:02,440 父进程到子进程一个fork 复制 225 00:08:02,480 --> 00:08:03,240 怎么去完成这个 226 00:08:03,280 --> 00:08:06,280 进程的系统调用的服务 227 00:08:06,320 --> 00:08:08,200 这部分是去阅读代码就可以 228 00:08:08,240 --> 00:08:10,600 我们这边不需要大家去coding 229 00:08:10,640 --> 00:08:11,480 还有就是challenge 230 00:08:11,520 --> 00:08:12,600 copy-on-write机制 231 00:08:12,640 --> 00:08:14,160 给大家做了介绍 232 00:08:14,200 --> 00:08:17,200 这就是我们整个lab5的一个讲解 233 00:08:17,280 --> 00:08:18,000 好 谢谢大家 234 00:08:18,120 --> 00:08:18,680 235 00:08:18,720 --> 00:08:18,760