0 00:00:00,000 --> 00:00:07,880 1 00:00:07,920 --> 00:00:09,720 刚才我们通过一个 2 00:00:09,760 --> 00:00:11,920 生活当中的同步例子 3 00:00:11,960 --> 00:00:15,160 说明了同步所会遇到的问题 4 00:00:15,200 --> 00:00:17,320 和可能的解决方案 5 00:00:17,360 --> 00:00:20,680 那么下面呢我们就把在计算机当中 6 00:00:20,720 --> 00:00:22,160 同步问题的解决方案呢 7 00:00:22,200 --> 00:00:23,840 给一个规范的描述 8 00:00:23,880 --> 00:00:26,080 这就是我们这里说到的临界区 9 00:00:26,120 --> 00:00:29,080 10 00:00:29,120 --> 00:00:31,400 临界区是指进程访问 11 00:00:31,440 --> 00:00:33,560 临界资源的一段 12 00:00:33,600 --> 00:00:35,480 需要互斥执行的代码 13 00:00:35,520 --> 00:00:36,640 在我们这里头呢 14 00:00:36,680 --> 00:00:38,720 这是我们要保护的代码 15 00:00:38,760 --> 00:00:39,920 在这段代码里头呢 16 00:00:39,960 --> 00:00:42,320 任何时刻只允许一个进程 17 00:00:42,360 --> 00:00:44,160 在这段区域里头执行 18 00:00:44,200 --> 00:00:45,640 为了做到这一点 19 00:00:45,680 --> 00:00:47,720 我们在临界区之前呢 20 00:00:47,760 --> 00:00:49,120 有一个进入区 21 00:00:49,160 --> 00:00:50,600 那在这里头呢 22 00:00:50,640 --> 00:00:53,040 需要去检查进程 23 00:00:53,080 --> 00:00:56,000 进入临界区的条件是否成立 24 00:00:56,040 --> 00:00:58,480 如果成立 那就进入 25 00:00:58,520 --> 00:01:00,800 那进入之前呢需要设置一个标志 26 00:01:00,840 --> 00:01:02,680 这个标志说当前进程 27 00:01:02,720 --> 00:01:04,520 正在访问临界区 28 00:01:04,560 --> 00:01:06,520 好 那有了这个进入区 29 00:01:06,560 --> 00:01:07,920 条件检查成立之后 30 00:01:07,960 --> 00:01:08,920 那我就会使用 31 00:01:08,960 --> 00:01:10,880 使用结束之后呢有一个退出区 32 00:01:10,920 --> 00:01:12,400 退出区的主要功能呢 33 00:01:12,440 --> 00:01:15,920 就是清除你所设的 34 00:01:15,960 --> 00:01:18,560 进程正在访问临界区的标志 35 00:01:18,600 --> 00:01:20,920 还有一个部分呢叫剩余区 36 00:01:20,960 --> 00:01:21,600 这一部分呢 37 00:01:21,640 --> 00:01:25,240 就跟我们同步互斥没关系的代码 38 00:01:25,280 --> 00:01:26,880 那这是我们在这里对 39 00:01:26,920 --> 00:01:28,480 临界区的标准的 40 00:01:28,520 --> 00:01:31,160 访问模式一个约定 41 00:01:31,200 --> 00:01:32,520 有了这个约定之后 42 00:01:32,560 --> 00:01:35,920 我们来描述一下临界区的访问规则 43 00:01:35,960 --> 00:01:37,920 在刚才前面例子当中我们说 44 00:01:37,960 --> 00:01:42,520 我们任何时候临界区的资源 45 00:01:42,560 --> 00:01:45,160 我需要是互斥的访问 46 00:01:45,200 --> 00:01:46,640 那也就说如果有一个在访问 47 00:01:46,680 --> 00:01:48,760 另外一个进程不能再访问 48 00:01:48,800 --> 00:01:50,880 如果说没有进程访问 49 00:01:50,920 --> 00:01:53,280 那这时候呢 你就可以进去 50 00:01:53,320 --> 00:01:54,960 好 在这里头我们把这些呢 51 00:01:55,000 --> 00:01:56,440 描述这样几条规则 52 00:01:56,480 --> 00:02:00,120 一条呢叫空闲则入 53 00:02:00,160 --> 00:02:02,720 也就说没有进程在临界区时 54 00:02:02,760 --> 00:02:04,520 任何进程可以进入 55 00:02:04,560 --> 00:02:06,360 你不能像我们前面举的 56 00:02:06,400 --> 00:02:07,840 生活当中例子 57 00:02:07,880 --> 00:02:10,160 谁都进不去这是不对的 58 00:02:10,200 --> 00:02:13,440 第二个叫忙则等待 59 00:02:13,480 --> 00:02:16,200 如果已经有进程在临界区了 60 00:02:16,240 --> 00:02:19,000 其它进程均不能进入临界区 61 00:02:19,040 --> 00:02:20,480 那如果说这一条不满足的话 62 00:02:20,520 --> 00:02:21,400 就会有两个进程 63 00:02:21,440 --> 00:02:22,960 同时进到临界区里头 64 00:02:23,000 --> 00:02:25,880 比如说我们面包买重的情况 65 00:02:25,920 --> 00:02:30,280 还有一个规则是有限等待 66 00:02:30,320 --> 00:02:32,760 也就是说在等待区里的进程 67 00:02:32,800 --> 00:02:35,200 不能无限制的等待下去 68 00:02:35,240 --> 00:02:36,440 好 那这时候呢 69 00:02:36,480 --> 00:02:41,520 我需要对等待的时间有一个约定 70 00:02:41,560 --> 00:02:43,880 最后一条叫让权等待 71 00:02:43,920 --> 00:02:46,360 也就说不能进入临界区的进程 72 00:02:46,400 --> 00:02:50,200 需要释放CPU进入等待状态 73 00:02:50,240 --> 00:02:52,400 而不是说像我们在前面例子当中 74 00:02:52,440 --> 00:02:55,320 我需要频繁去查 75 00:02:55,360 --> 00:02:56,520 当然这一条呢 76 00:02:56,560 --> 00:02:59,000 它是需要有相应的一些支持 77 00:02:59,040 --> 00:03:02,000 所以在这里呢它是可选的 78 00:03:02,040 --> 00:03:05,640 有了这几条临界区的访问规则 79 00:03:05,680 --> 00:03:07,560 那么下面呢我们就来看 80 00:03:07,600 --> 00:03:10,760 临界区的访问的实现方法 81 00:03:10,800 --> 00:03:13,080 那在这里呢我们介绍三类 82 00:03:13,120 --> 00:03:15,480 一类呢是禁用中断 83 00:03:15,520 --> 00:03:16,800 那禁止中断之后 84 00:03:16,840 --> 00:03:18,640 那其他的进程就没有办法 85 00:03:18,680 --> 00:03:20,200 对当前进程的执行呢 86 00:03:20,240 --> 00:03:22,240 进行任何的打扰了 87 00:03:22,280 --> 00:03:23,200 那这时候呢 88 00:03:23,240 --> 00:03:26,360 我当前进程对临界区资源的访问 89 00:03:26,400 --> 00:03:28,840 也就不会有任何问题 90 00:03:28,880 --> 00:03:29,720 但这时候他对 91 00:03:29,760 --> 00:03:32,760 系统的中断的响应会有影响 92 00:03:32,800 --> 00:03:34,080 因为你禁止之后 93 00:03:34,120 --> 00:03:35,920 中断就没办法响应 94 00:03:35,960 --> 00:03:38,760 然后是第二类办法呢 95 00:03:38,800 --> 00:03:40,720 是用软件的办法 96 00:03:40,760 --> 00:03:43,440 也就说在硬件还没有支持的时候 97 00:03:43,480 --> 00:03:45,520 那这时候呢我们先尝试用 98 00:03:45,560 --> 00:03:48,440 共享变量协调的方式来做这件事情 99 00:03:48,480 --> 00:03:51,480 那这时候呢它是比较复杂的 100 00:03:51,520 --> 00:03:52,680 第三类办法呢 101 00:03:52,720 --> 00:03:55,320 我们是借用操作系统的支持 102 00:03:55,360 --> 00:03:58,640 来对应用提供同步的服务 103 00:03:58,680 --> 00:04:01,480 那在这里头呢由于引入管理者 104 00:04:01,520 --> 00:04:03,560 那么我们在这里就不像软件的办法 105 00:04:03,600 --> 00:04:04,440 完全是大家 106 00:04:04,480 --> 00:04:07,240 对等协调的方式来完成的 107 00:04:07,280 --> 00:04:08,080 那这几种办法 108 00:04:08,120 --> 00:04:09,720 它的衡量标准是什么呢 109 00:04:09,760 --> 00:04:11,240 那我们在做比较的时候 110 00:04:11,280 --> 00:04:13,240 是比较它的性能 111 00:04:13,280 --> 00:04:16,400 和它的并发的级别 112 00:04:16,440 --> 00:04:17,360 也就说到底我是 113 00:04:17,400 --> 00:04:19,240 允许什么样的并发执行 114 00:04:19,280 --> 00:04:22,640 115 00:04:22,680 --> 00:04:25,880 接下来我们说禁用中断 116 00:04:25,920 --> 00:04:27,040 禁用中断呢 117 00:04:27,080 --> 00:04:28,080 顾名思义它就是 118 00:04:28,120 --> 00:04:32,040 禁止硬件中断的响应 119 00:04:32,080 --> 00:04:32,840 那也就相当于 120 00:04:32,880 --> 00:04:35,360 我把中断使能给关掉了 121 00:04:35,400 --> 00:04:36,480 那这时候就没有中断 122 00:04:36,520 --> 00:04:38,120 那也就没有了时钟中断 123 00:04:38,160 --> 00:04:40,040 也就没有了上下文切换 124 00:04:40,080 --> 00:04:42,840 好 这时候呢也就没有了并发执行 125 00:04:42,880 --> 00:04:44,480 所以在这种情况下 126 00:04:44,520 --> 00:04:47,920 整个系统呢是由当前进程独占 127 00:04:47,960 --> 00:04:51,160 硬件的响应处理 被延迟到 128 00:04:51,200 --> 00:04:52,960 你中断启用之后 129 00:04:53,000 --> 00:04:54,440 那这时候对于紧急的事情呢 130 00:04:54,480 --> 00:04:56,120 它没有办法做响应 131 00:04:56,160 --> 00:04:57,360 好 那在这种做法里头呢 132 00:04:57,400 --> 00:04:59,080 我们现在的计算机系统呢 133 00:04:59,120 --> 00:05:00,440 都有相应的一些指令 134 00:05:00,480 --> 00:05:03,760 来实现禁用中断 135 00:05:03,800 --> 00:05:07,880 下面呢是我们基于中断禁用 136 00:05:07,920 --> 00:05:10,960 所给出的临界区域访问代码 137 00:05:11,000 --> 00:05:11,920 在这段代码里头呢 138 00:05:11,960 --> 00:05:13,400 通常情况下我们用一个宏 139 00:05:13,440 --> 00:05:18,200 来实现把当前的CPU的状态 140 00:05:18,240 --> 00:05:21,400 保存到存储单元当中 141 00:05:21,440 --> 00:05:24,320 同时把中断禁止掉 142 00:05:24,360 --> 00:05:26,320 因为如果说你不保存的话 143 00:05:26,360 --> 00:05:28,680 等你中断恢复之后 144 00:05:28,720 --> 00:05:31,800 那么整个系统状态呢会发生变化 145 00:05:31,840 --> 00:05:34,040 然后是临界区的访问 146 00:05:34,080 --> 00:05:37,280 访问结束之后我是恢复系统的状态 147 00:05:37,320 --> 00:05:39,440 并且使能中断 148 00:05:39,480 --> 00:05:40,840 那这时候对应过来呢 149 00:05:40,880 --> 00:05:44,600 进入区就是我们这里的关中断 150 00:05:44,640 --> 00:05:46,280 而退出区呢 151 00:05:46,320 --> 00:05:50,200 就是我们这里的使能中断 152 00:05:50,240 --> 00:05:53,480 好 这种办法呢它有很大的局限性 153 00:05:53,520 --> 00:05:54,920 也就说关中断之后 154 00:05:54,960 --> 00:05:56,560 进程就没有办法停止了 155 00:05:56,600 --> 00:05:59,280 如果这时候你的进程执行出了问题 156 00:05:59,320 --> 00:06:02,280 那整个系统就没有办法回来了 157 00:06:02,320 --> 00:06:03,760 还有一个问题是 158 00:06:03,800 --> 00:06:06,560 它可能导致其它进程处于饥饿状态 159 00:06:06,600 --> 00:06:10,800 也就说它们没有得到执行的机会 160 00:06:10,840 --> 00:06:12,840 再有一个呢是 我们在这里头 161 00:06:12,880 --> 00:06:15,520 它的执行时间可能会很长 162 00:06:15,560 --> 00:06:16,560 从而呢使得 163 00:06:16,600 --> 00:06:18,800 我中断要求及时响应这一条 164 00:06:18,840 --> 00:06:21,440 没有得到支持 165 00:06:21,480 --> 00:06:24,200 166 00:06:24,240 --> 00:06:25,560 所以在这里头呢 167 00:06:25,600 --> 00:06:28,360 匝的使用必须很小心谨慎的来用 168 00:06:28,400 --> 00:06:29,720 那在我们的计算机系统里呢 169 00:06:29,760 --> 00:06:31,160 通常情况下只是在 170 00:06:31,200 --> 00:06:33,400 不能不用它的时候我才会去用它 171 00:06:33,440 --> 00:06:33,480