0 00:00:00,000 --> 00:00:06,920 1 00:00:06,960 --> 00:00:10,320 下面我们来讨论I/O结构 2 00:00:10,360 --> 00:00:11,920 在I/O结构当中呢 3 00:00:11,960 --> 00:00:15,360 我们先从硬件的结构说起 4 00:00:15,400 --> 00:00:18,120 在我们计算机系统当中 5 00:00:18,160 --> 00:00:21,600 我们的CPU为了跟外界相连 6 00:00:21,640 --> 00:00:22,920 实际上在我们的主板上 7 00:00:22,960 --> 00:00:24,520 它是分成了两段 8 00:00:24,560 --> 00:00:29,480 一段是和高速的这些内存 显卡 9 00:00:29,520 --> 00:00:30,760 相连的部分 10 00:00:30,800 --> 00:00:34,520 这时候它的速度通常是若干个G 11 00:00:34,560 --> 00:00:36,560 连的是内存显卡 12 00:00:36,600 --> 00:00:39,880 然后还有一个部分是南桥 13 00:00:39,920 --> 00:00:41,520 它负责跟我们这里的 14 00:00:41,560 --> 00:00:43,320 各种各样的设备相连 15 00:00:43,360 --> 00:00:45,600 比如说像这里的PCI总线 16 00:00:45,640 --> 00:00:48,560 然后你的磁盘 17 00:00:48,600 --> 00:00:50,800 网络都是通过这底下连的 18 00:00:50,840 --> 00:00:52,480 在我们的基本原理里是说 19 00:00:52,520 --> 00:00:56,600 CPU通过总线来连内存和I/O设备 20 00:00:56,640 --> 00:00:57,440 那到这个地方 21 00:00:57,480 --> 00:01:00,480 我们就细化成北桥连高速设备 22 00:01:00,520 --> 00:01:05,040 南桥连I/O设备 23 00:01:05,080 --> 00:01:07,240 这时候在这种结构下我们还需要 24 00:01:07,280 --> 00:01:10,160 进一步来细化CPU到底 25 00:01:10,200 --> 00:01:12,040 如何来识别每一个设备 26 00:01:12,080 --> 00:01:14,120 它们的连接关系是什么样子的 27 00:01:14,160 --> 00:01:14,920 在这儿呢 28 00:01:14,960 --> 00:01:19,400 首先我们在这里头是CPU和I/O设备 29 00:01:19,440 --> 00:01:22,440 设备上面呢有设备控制器 30 00:01:22,480 --> 00:01:23,760 设备控制器的功能 31 00:01:23,800 --> 00:01:27,880 是提供CPU和设备之间的接口 32 00:01:27,920 --> 00:01:30,640 那在这里头就是总线接口 33 00:01:30,680 --> 00:01:33,920 然后有相应的一组寄存器 34 00:01:33,960 --> 00:01:36,680 可以进行数据的交互和 35 00:01:36,720 --> 00:01:39,000 状态和控制的交互 36 00:01:39,040 --> 00:01:42,000 也可以把它映射到内存当中 37 00:01:42,040 --> 00:01:43,640 给一段内存区域 38 00:01:43,680 --> 00:01:45,840 然后对于这段内存区域的访问 39 00:01:45,880 --> 00:01:48,680 对应过来就是我们的I/O设备的访问 40 00:01:48,720 --> 00:01:52,360 这就是我们这里说到的I/O地址 41 00:01:52,400 --> 00:01:56,680 I/O地址通过总线连到我们的CPU上 42 00:01:56,720 --> 00:02:01,000 总线和实际设备之间有总线适配器 43 00:02:01,040 --> 00:02:03,720 在这里头我们映射过来 44 00:02:03,760 --> 00:02:05,160 可能是内存地址 45 00:02:05,200 --> 00:02:07,880 也可能是I/O空间的端口 46 00:02:07,920 --> 00:02:09,400 那我们可以对I/O端口 47 00:02:09,440 --> 00:02:11,400 有相应的I/O指令 48 00:02:11,440 --> 00:02:12,600 如果是内存 49 00:02:12,640 --> 00:02:16,000 那我们直接是访问存储 50 00:02:16,040 --> 00:02:18,360 就对应着对I/O设备的访问 51 00:02:18,400 --> 00:02:21,240 这是从CPU到设备 52 00:02:21,280 --> 00:02:25,200 反过来还有一个从设备到CPU的通道 53 00:02:25,240 --> 00:02:28,120 这就是我们的中断控制器 54 00:02:28,160 --> 00:02:29,600 设备产生中断之后 55 00:02:29,640 --> 00:02:33,080 在中断控制器进行汇总然后送给CPU 56 00:02:33,120 --> 00:02:37,480 CPU就能对外部设备的事件做出响应 57 00:02:37,520 --> 00:02:40,680 这就是我们在这里CPU和设备之间的 58 00:02:40,720 --> 00:02:42,720 通讯的三种方式 59 00:02:42,760 --> 00:02:46,200 轮询那就不用中断控制器 60 00:02:46,240 --> 00:02:48,720 CPU直接访问I/O端口 61 00:02:48,760 --> 00:02:54,000 或者说访问设备所对应的内存地址空间 62 00:02:54,040 --> 00:02:55,560 我也可以采用中断方式 63 00:02:55,600 --> 00:03:00,000 那就是外部设备有事件要通知CPU 64 00:03:00,040 --> 00:03:04,120 就要通过中断到CPU 65 00:03:04,160 --> 00:03:05,400 如果说在这里头 66 00:03:05,440 --> 00:03:07,040 还有一种方式是说 67 00:03:07,080 --> 00:03:10,040 我外部设备需要把数据 68 00:03:10,080 --> 00:03:12,400 直接放到内存当中 69 00:03:12,440 --> 00:03:16,000 你可以通过CPU读然后放到内存 70 00:03:16,040 --> 00:03:17,920 在DMA控制器的控制下 71 00:03:17,960 --> 00:03:21,520 把数据从I/O设备直接到内存单元 72 00:03:21,560 --> 00:03:25,080 这是CPU和设备之间的连接关系 73 00:03:25,120 --> 00:03:28,080 那下面我们进一步来讨论一下 74 00:03:28,120 --> 00:03:31,560 I/O指令和内存映射I/O 75 00:03:31,600 --> 00:03:34,960 I/O指令它是通过I/O端口 76 00:03:35,000 --> 00:03:37,240 来访问设备寄存器 77 00:03:37,280 --> 00:03:39,320 实际上也就是相当于我们通过端口号 78 00:03:39,360 --> 00:03:42,760 来区别我访问的是哪个设备 79 00:03:42,800 --> 00:03:47,040 以及于哪个设备上的哪一项功能 80 00:03:47,080 --> 00:03:52,120 然后CPU上执行out in两个指令 81 00:03:52,160 --> 00:03:54,800 来完成对端口的读写 82 00:03:54,840 --> 00:03:57,160 这些读写不仅仅是数据的访问 83 00:03:57,200 --> 00:04:00,560 也还对应着相应的设备控制 84 00:04:00,600 --> 00:04:02,040 比如说我们有一些控制 85 00:04:02,080 --> 00:04:06,200 就是直接对指定的端口发出写信号 86 00:04:06,240 --> 00:04:09,040 那这个写信号来控制设备的操作 87 00:04:09,080 --> 00:04:13,120 而写的内容有的时候反倒是不重要的 88 00:04:13,160 --> 00:04:17,040 另一种呢是内存映射I/O 89 00:04:17,080 --> 00:04:22,560 它是把设备的寄存器或者存储队列 90 00:04:22,600 --> 00:04:24,600 或者说存储区域 91 00:04:24,640 --> 00:04:28,680 映射到内存的物理地址空间 92 00:04:28,720 --> 00:04:31,560 然后通过load /store 93 00:04:31,600 --> 00:04:35,200 这种存储访问命令来实现I/O操作 94 00:04:35,240 --> 00:04:36,440 在这地方呢 95 00:04:36,480 --> 00:04:38,600 它的地址到底是在哪呢 96 00:04:38,640 --> 00:04:41,120 实际上是在MMU来设置 97 00:04:41,160 --> 00:04:43,440 或者说通过硬件的跳线 98 00:04:43,480 --> 00:04:47,560 来完成相应地址的映射 99 00:04:47,600 --> 00:04:49,200 有了这些讨论之后 100 00:04:49,240 --> 00:04:50,400 那我们就可以给出 101 00:04:50,440 --> 00:04:54,360 操作系统当中I/O子系统的结构 102 00:04:54,400 --> 00:04:56,800 那我们从下往上来讨论 103 00:04:56,840 --> 00:04:59,520 最底下是我们各种各样的设备 104 00:04:59,560 --> 00:05:02,520 比如说存储的SCSI设备 105 00:05:02,560 --> 00:05:06,400 和这边的ATAPI设备 106 00:05:06,440 --> 00:05:09,120 然后还有其它的你比如说鼠标 键盘 107 00:05:09,160 --> 00:05:12,040 然后每一个设备之上 108 00:05:12,080 --> 00:05:15,680 都对应着有一个设备控制器 109 00:05:15,720 --> 00:05:19,880 那不同的设备它的控制器是不一样的 110 00:05:19,920 --> 00:05:21,960 然后在这之上呢 111 00:05:22,000 --> 00:05:24,200 对应着我们就是开始是软件 112 00:05:24,240 --> 00:05:27,280 操作系统里第一层是设备驱动 113 00:05:27,320 --> 00:05:28,360 每一个设备 114 00:05:28,400 --> 00:05:31,200 每一类设备对应着有自己的驱动 115 00:05:31,240 --> 00:05:34,360 在这上头呢是我们的I/O子系统 116 00:05:34,400 --> 00:05:37,920 它用来处理各种设备共同的一些内容 117 00:05:37,960 --> 00:05:40,120 比如说我们的I/O请求 118 00:05:40,160 --> 00:05:42,960 转换成驱动的I/O请求 119 00:05:43,000 --> 00:05:44,360 这是一种细化 120 00:05:44,400 --> 00:05:46,720 然后我也会缓存 121 00:05:46,760 --> 00:05:48,040 设备给出来的一些结果 122 00:05:48,080 --> 00:05:50,680 你比如说我要访问某一个磁盘上的 123 00:05:50,720 --> 00:05:52,080 某一个扇区的数据 124 00:05:52,120 --> 00:05:54,120 那我前面已经做过一次操作了 125 00:05:54,160 --> 00:05:56,120 I/O子系统负责缓存 126 00:05:56,160 --> 00:05:57,960 并且你第二次来访问的时候 127 00:05:58,000 --> 00:05:59,720 我直接给你结果 128 00:05:59,760 --> 00:06:03,680 然后上边是我们内核的其它部分内容 129 00:06:03,720 --> 00:06:06,440 文件系统 那这些都是依赖于I/O子系统 130 00:06:06,480 --> 00:06:09,440 来完成相应数据读写的 131 00:06:09,480 --> 00:06:11,760 在这种结构下我们来看一个 132 00:06:11,800 --> 00:06:15,800 完整的I/O请求的生命周期 133 00:06:15,840 --> 00:06:17,160 跟我们刚才那张图呢 134 00:06:17,200 --> 00:06:20,520 在这地方又有一些细化 135 00:06:20,560 --> 00:06:23,000 这地方的驱动我把它分成两段 136 00:06:23,040 --> 00:06:26,400 设备驱动和底下的中断处理例程 137 00:06:26,440 --> 00:06:28,720 那我们来看一个用户 138 00:06:28,760 --> 00:06:31,480 进行I/O请求它的过程 139 00:06:31,520 --> 00:06:35,440 用户发出I/O请求 这是时间顺序 140 00:06:35,480 --> 00:06:37,160 到这个系统调用返回 141 00:06:37,200 --> 00:06:38,760 我得到相应的数据 142 00:06:38,800 --> 00:06:41,920 或者说我输出数据完成 143 00:06:41,960 --> 00:06:44,400 操作系统里头的I/O子系统 144 00:06:44,440 --> 00:06:45,840 它会做什么事呢 145 00:06:45,880 --> 00:06:49,120 首先判断一下你要进行这个操作 146 00:06:49,160 --> 00:06:51,240 是否有现成的结果 147 00:06:51,280 --> 00:06:55,320 比如说你的磁盘数据的读操作 148 00:06:55,360 --> 00:06:57,080 如果有 那我在这儿呢 149 00:06:57,120 --> 00:07:01,840 直接把我已经缓存的结果给应用 150 00:07:01,880 --> 00:07:03,200 我这个操作就结束了 151 00:07:03,240 --> 00:07:05,080 它实际上没有对实际的设备 152 00:07:05,120 --> 00:07:06,720 进行任何的操作 153 00:07:06,760 --> 00:07:09,120 如果说这个数据没有 154 00:07:09,160 --> 00:07:13,360 它会给驱动发出I/O请求 155 00:07:13,400 --> 00:07:14,480 那这时候我就要去读 156 00:07:14,520 --> 00:07:17,240 指定的区域里的数据了 157 00:07:17,280 --> 00:07:20,160 设备驱动再把这些I/O请求 158 00:07:20,200 --> 00:07:22,680 转换成设备的控制命令 159 00:07:22,720 --> 00:07:24,600 这个设备控制命令呢 160 00:07:24,640 --> 00:07:27,680 直接给硬件进行控制硬件操作 161 00:07:27,720 --> 00:07:31,560 那么这时候设备驱动处于等待状态 162 00:07:31,600 --> 00:07:34,440 等这边处理结束之后 163 00:07:34,480 --> 00:07:36,680 它会产生中断 164 00:07:36,720 --> 00:07:39,600 中断回来我们的中断处理例程 165 00:07:39,640 --> 00:07:41,160 会对它做出响应 166 00:07:41,200 --> 00:07:43,800 这时候响应就是保存相应的结果 167 00:07:43,840 --> 00:07:48,000 并且把结果通知上面的设备驱动层 168 00:07:48,040 --> 00:07:50,160 设备驱动层负责来区分 169 00:07:50,200 --> 00:07:53,760 返回的结果和哪个请求是相对应的 170 00:07:53,800 --> 00:07:55,440 然后把这个结果 171 00:07:55,480 --> 00:07:58,120 给到相应的I/O子系统当中 172 00:07:58,160 --> 00:08:00,920 再把它送给用户的进程 173 00:08:00,960 --> 00:08:02,160 整个一个I/O请求的 174 00:08:02,200 --> 00:08:05,720 完整过程就实现了 175 00:08:05,760 --> 00:08:08,280 这是我们说到的I/O结构 176 00:08:08,320 --> 00:08:09,480 177 00:08:09,520 --> 00:08:09,560