0 00:00:00,000 --> 00:00:06,960 1 00:00:07,000 --> 00:00:08,400 下面我们来讨论 2 00:00:08,440 --> 00:00:10,640 消息队列和共享内存 3 00:00:10,680 --> 00:00:14,000 这两种进程通讯机制 4 00:00:14,040 --> 00:00:16,480 首先是消息队列 5 00:00:16,520 --> 00:00:19,400 消息队列是由操作系统维护的 6 00:00:19,440 --> 00:00:22,160 以字节序列为基本单位的 7 00:00:22,200 --> 00:00:23,720 间接通讯机制 8 00:00:23,760 --> 00:00:26,000 也就是说它是一种间接通讯机制 9 00:00:26,040 --> 00:00:27,520 肯定是由操作系统维护的 10 00:00:27,560 --> 00:00:31,040 它通讯的基本单位是字节序列 11 00:00:31,080 --> 00:00:33,840 这是消息队列的特征 12 00:00:33,880 --> 00:00:35,200 在这个图示当中 13 00:00:35,240 --> 00:00:37,680 我们会看到若干个进程 14 00:00:37,720 --> 00:00:42,400 可以往内核的消息队列里发送消息 15 00:00:42,440 --> 00:00:44,880 然后另外的进程 16 00:00:44,920 --> 00:00:47,960 可以从这消息队列里读出消息 17 00:00:48,000 --> 00:00:52,160 消息是一个最基本的字节序列 18 00:00:52,200 --> 00:00:55,880 多个消息按先进先出顺序 19 00:00:55,920 --> 00:00:59,160 构成一个消息队列 标识相同 20 00:00:59,200 --> 00:01:00,880 实际上是我这个消息队列 21 00:01:00,920 --> 00:01:03,720 可以构成若干个子队列的特征 22 00:01:03,760 --> 00:01:05,320 与消息队列相关的 23 00:01:05,360 --> 00:01:07,120 系统调用有这样几个 24 00:01:07,160 --> 00:01:10,280 也就是说我消息队列需要有一个创建 25 00:01:10,320 --> 00:01:12,440 这就是依据你的标识 26 00:01:12,480 --> 00:01:16,560 来创建或者获取相应的消息队列 27 00:01:16,600 --> 00:01:18,360 然后有了消息队列之后 28 00:01:18,400 --> 00:01:22,520 我有两个操作 发送消息和接收消息 29 00:01:22,560 --> 00:01:24,480 对于消息队列来说 它后边有 30 00:01:24,520 --> 00:01:27,280 缓冲区的起始位置 缓冲区的大小 31 00:01:27,320 --> 00:01:30,520 以及我在写的时候它的标识 32 00:01:30,560 --> 00:01:34,440 而另外一个接收跟它是类似的 33 00:01:34,480 --> 00:01:36,040 前边的内容都一样 34 00:01:36,080 --> 00:01:37,760 这多了一个type 35 00:01:37,800 --> 00:01:39,640 详细的这些参数的含义 36 00:01:39,680 --> 00:01:43,840 大家可以下去查相关的手册 37 00:01:43,880 --> 00:01:46,760 还有一个是消息队列的控制 38 00:01:46,800 --> 00:01:48,040 这指什么意思呢 39 00:01:48,080 --> 00:01:51,120 我们进程创建和结束的时候 40 00:01:51,160 --> 00:01:54,360 它所占用的资源都会被释放掉 41 00:01:54,400 --> 00:01:56,960 进程从创建获取各种资源 42 00:01:57,000 --> 00:01:58,080 到结束的时候 43 00:01:58,120 --> 00:02:00,000 这些资源都会被释放掉 44 00:02:00,040 --> 00:02:01,760 但是消息队列 45 00:02:01,800 --> 00:02:05,440 是独立于创建它的进程的 46 00:02:05,480 --> 00:02:08,800 所以一个进程可以创建一个消息队列 47 00:02:08,840 --> 00:02:10,480 然后这个进程结束了 48 00:02:10,520 --> 00:02:11,520 但这个系队列 49 00:02:11,560 --> 00:02:13,840 还可以继续存在下去 50 00:02:13,880 --> 00:02:17,080 从而你在后续的创建的进程 51 00:02:17,120 --> 00:02:19,440 可以去读取消息队列的内容 52 00:02:19,480 --> 00:02:22,480 实现两个生命周期不同的 53 00:02:22,520 --> 00:02:24,560 进程之间的通讯 54 00:02:24,600 --> 00:02:27,440 所以在这需要有专门的系统调用 55 00:02:27,480 --> 00:02:31,840 来完成对消息队列的创建和删除 56 00:02:31,880 --> 00:02:35,160 57 00:02:35,200 --> 00:02:38,800 接下来的第二种机制呢 是共享内存 58 00:02:38,840 --> 00:02:43,400 共享内存它是把同一段物理内存区域 59 00:02:43,440 --> 00:02:46,840 映射到多个进程的地址空间里的 60 00:02:46,880 --> 00:02:48,640 一种通讯机制 61 00:02:48,680 --> 00:02:49,520 某种角度上来说 62 00:02:49,560 --> 00:02:53,040 它应该是一种内存的共享机制 63 00:02:53,080 --> 00:02:54,400 但我们这里头是 64 00:02:54,440 --> 00:02:56,800 把它作为通讯来使用的 65 00:02:56,840 --> 00:03:00,320 这种机制在进程和线程里的情况 66 00:03:00,360 --> 00:03:01,440 是不一样的 67 00:03:01,480 --> 00:03:04,040 在线程里头由于同一个进程 68 00:03:04,080 --> 00:03:06,080 共享相同的地址空间 69 00:03:06,120 --> 00:03:07,880 如果说你的通讯双方 70 00:03:07,920 --> 00:03:10,120 是一个进程里的两个线程 71 00:03:10,160 --> 00:03:10,920 那么这时候呢 72 00:03:10,960 --> 00:03:14,080 这种共享内存是天然的 73 00:03:14,120 --> 00:03:17,280 你不需要额外的机制就已经可以了 74 00:03:17,320 --> 00:03:18,920 而如果是进程 75 00:03:18,960 --> 00:03:21,800 因为每一个进程有自己的地址空间 76 00:03:21,840 --> 00:03:24,040 所以在不同的进程之间 77 00:03:24,080 --> 00:03:25,720 要想做共享内存的话 78 00:03:25,760 --> 00:03:30,040 你必须显示的去设置一个共享内存段 79 00:03:30,080 --> 00:03:33,560 然后你才能实现内存的共享 80 00:03:33,600 --> 00:03:37,400 这种做法它的特点是快速方便 81 00:03:37,440 --> 00:03:39,440 因为我一个进程写进去 82 00:03:39,480 --> 00:03:41,400 另一个进程马上就能看得见 83 00:03:41,440 --> 00:03:43,240 也没有系统调用在这里 84 00:03:43,280 --> 00:03:46,480 进行用户和内核之间的切换 85 00:03:46,520 --> 00:03:49,360 它的不足是仅靠共享内存 86 00:03:49,400 --> 00:03:51,720 你没有办法实现完整的通讯 87 00:03:51,760 --> 00:03:54,440 你需要加同步机制 88 00:03:54,480 --> 00:03:57,920 以避免一个进程在写的过程当中 89 00:03:57,960 --> 00:03:59,280 还没有写完之前 90 00:03:59,320 --> 00:04:02,440 另一个进程从里头读 91 00:04:02,480 --> 00:04:05,920 这是共享内存和它相对应的 92 00:04:05,960 --> 00:04:08,080 共享内存的实现机制 93 00:04:08,120 --> 00:04:10,280 我们可以用这个图来描述 94 00:04:10,320 --> 00:04:13,720 两个进程的地址空间各自不同 95 00:04:13,760 --> 00:04:15,720 中间是物理内存 96 00:04:15,760 --> 00:04:18,000 我们把一块物理内存区域 97 00:04:18,040 --> 00:04:19,280 映射到两个进程 98 00:04:19,320 --> 00:04:23,120 怎么映射呢 就靠两边的页表项 99 00:04:23,160 --> 00:04:24,800 不同的页表项 100 00:04:24,840 --> 00:04:27,760 它是在进程地址空间里可以有相同 101 00:04:27,800 --> 00:04:30,320 或者不同的逻辑地址 102 00:04:30,360 --> 00:04:32,560 但是它们映射过来的时候 103 00:04:32,600 --> 00:04:33,880 这一页对应的 104 00:04:33,920 --> 00:04:36,320 物理内存的地址是相同的 105 00:04:36,360 --> 00:04:37,000 那这时候呢 106 00:04:37,040 --> 00:04:38,840 它们就映射到同一页里头了 107 00:04:38,880 --> 00:04:40,800 这时候在一个进程里写 108 00:04:40,840 --> 00:04:42,520 在另一个进程里我就可以读 109 00:04:42,560 --> 00:04:43,880 就可以看到相应内容 110 00:04:43,920 --> 00:04:45,600 从而实现这个通讯 111 00:04:45,640 --> 00:04:49,240 所以这种通讯呢 它的速度是最快的 112 00:04:49,280 --> 00:04:52,440 一头写进去 另一头马上就能看得到 113 00:04:52,480 --> 00:04:53,480 这种通讯呢 114 00:04:53,520 --> 00:04:55,640 它也没有系统调用的干预 115 00:04:55,680 --> 00:04:59,400 不需要数据复制 这也是它快的原因 116 00:04:59,440 --> 00:05:02,520 它的麻烦是不提供同步 117 00:05:02,560 --> 00:05:04,000 你需要用其他的机制 118 00:05:04,040 --> 00:05:06,720 来实现进程之间的 119 00:05:06,760 --> 00:05:10,280 对共享内存访问的协调 120 00:05:10,320 --> 00:05:13,320 与共享内存设置相关的 121 00:05:13,360 --> 00:05:16,360 系统调用有这样几个 122 00:05:16,400 --> 00:05:20,200 创建共享段 123 00:05:20,240 --> 00:05:22,000 把共享段映射到 124 00:05:22,040 --> 00:05:26,120 指定的进程地址空间当中 125 00:05:26,160 --> 00:05:30,240 取消共享段到进程地址空间的映射 126 00:05:30,280 --> 00:05:32,640 和共享段的控制 127 00:05:32,680 --> 00:05:33,960 这几个系统调用 128 00:05:34,000 --> 00:05:37,240 主要是完成共享关系的建立 129 00:05:37,280 --> 00:05:40,280 而正常的共享数据的访问 130 00:05:40,320 --> 00:05:42,360 只需要我们的读写指令就行了 131 00:05:42,400 --> 00:05:46,600 不需要专门的系统调用 132 00:05:46,640 --> 00:05:49,080 有了这几个映射关系之后 133 00:05:49,120 --> 00:05:51,160 我们就可以通过正常的读写指令 134 00:05:51,200 --> 00:05:53,640 完成两个进程之间的通讯了 135 00:05:53,680 --> 00:05:56,240 但是为了保证数据的完整性 136 00:05:56,280 --> 00:05:59,320 我们还需要采用信号量等 137 00:05:59,360 --> 00:06:02,320 其他的同步机制来协调 138 00:06:02,360 --> 00:06:05,720 不同进程对共享内存的访问冲突 139 00:06:05,760 --> 00:06:08,480 避免一个进程往里写的数据还没写完 140 00:06:08,520 --> 00:06:10,440 另一个进程从里读数据 141 00:06:10,480 --> 00:06:12,640 从而读到的数据是错误的 142 00:06:12,680 --> 00:06:14,120 有了这个协调机制之后 143 00:06:14,160 --> 00:06:16,640 我们就可以完成两个进程之间的 144 00:06:16,680 --> 00:06:19,720 最快速的信息交流了 145 00:06:19,760 --> 00:06:20,960 到这个地方为止呢 146 00:06:21,000 --> 00:06:24,960 我们就讲了四种进程之间的通讯机制 147 00:06:25,000 --> 00:06:30,520 信号、管道、消息队列和共享内存 148 00:06:30,560 --> 00:06:33,400 它们都有不同的适用场景 149 00:06:33,440 --> 00:06:35,000 也有不同的特征 150 00:06:35,040 --> 00:06:36,920 你比如说有的信息量存的比较少 151 00:06:36,960 --> 00:06:38,160 但是速度比较快 152 00:06:38,200 --> 00:06:39,960 有的信息量比较大 153 00:06:40,000 --> 00:06:42,600 但是前后的协调机制比较复杂 154 00:06:42,640 --> 00:06:46,520 希望大家下去之后通过实际的例子 155 00:06:46,560 --> 00:06:48,360 来尝试几种不同通讯机制 156 00:06:48,400 --> 00:06:50,760 看它们各有什么样的特征 157 00:06:50,800 --> 00:06:53,480 更有兴趣的同学可以在我们的ucore里 158 00:06:53,520 --> 00:06:56,560 对这些通讯机制进行实现和完善 159 00:06:56,600 --> 00:06:58,200 今天的课就上到这里 160 00:06:58,240 --> 00:06:59,160 下课 161 00:06:59,200 --> 00:06:59,240