共享内存提供了进程间通信所能实现的最高带宽。一个共享内存对象创建之后,可以访问这个对象的进程就能够使用指针直接对其读写。这就意味着,共享内存访问本身就是非同步的。如果一个进程更新共享内存的一个区域,就必须特别小心不要让其他进程读取或更新同一块区域。即使是最简单的读取操作时,其他进程仍然有可能读到变化与不稳定的数据。
为了解决这个问题,共享内存就常与其他同步原(synchronization primitives)结合在一起使用以使进程之间的内存更新原子化。如果更新的间距很小,同步原就会限制自己固有的使用共享内存的高带宽。共享内存用于以块的模式更新大量的数据是最有效的。
信号量(semaphores)与互斥体(mutexes)都是适用与共享内存结合使用的同步原。信号量是在创建进程间同步的POSIX实时标准时引入的。互斥体则是在创建线程同步的POSIX标准是引入的。互斥体也可以在不同进程中的线程之间使用。POSIX将其作为一个可选的能力,我们在这里则是支持的。一般来说,互斥体要比信号量效率更高。
用于消息传递的共享内存
共享内存与消息传递可以结合起来以提供支持以下功能的IPC:
- 非常高的性能(共享内存)
- 同步(消息传递)
- 网络透明(消息传递)
阅读全文…
通过使用发/收/回复将共同合作的线程与进程作为一个团队来构建一个UNIX应用程序的架构就会得到一个同步通知的系统。进程间通信就为这个系统的特殊转型而产生的,而不是在其后。
异步系统的一个重要问题就是事件通知需要依靠信号处理器才能运行。异步的进程间通信将难于进行彻底的系统运行测试,并不能够确保不论信号处理器怎样,处理工作能够如预想的那样运行。应用程序一般都是依赖一个确定的起始点的“窗口”,在这个窗口中信号的延迟是可以忍受的,来避免出现这种情况。
阅读全文…
在UNIX系统中,消息传递是沿着通道与连接传送的,而不是直接的从线程到线程。一个线程要接收消息就先要创建一个通道,另外一个线程如果想要向这个线程传递消息就需要创建一个连接附加到该通道上。
通道是消息内核调用所必须的,服务器线程在调用MsgReceive()函数就是使用这个通道来接收消息的。连接由客户线程创建并用于“连接”服务器线程创建的可用通道。一旦连接确定,客户线程就可以使用MsgSend()函数发送消息了。如果一个进程中有多个线程连接到同一个通道,为了效率所有的连接就被映射到同一个内核对象。在一个进程中的通道与连接是使用一个小整数进行命名的。客户连接直接映射为文件描述符。
从架构上来说,这是一个关键点。通过将客户连接直接映射为文件描述符,就消除了使用另一层变换的必要。我们不必“指出”根据文件描述符向哪里发送消息。相反,我们可以很简单的将消息直接发送给“文件描述符”(也就是连接ID)。
这里可以使用的函数如下:
阅读全文…
在UNIX中,消息服务直接从一个线程的地址空间复制消息到另一个线程的地址空间,没有也不通过中间的缓存,这样的话消息传递的性能就接近了底层硬件所支持的最大内存带宽了。对于消息的内容,系统内核没有赋予其特别的含义,只有发送者与接收者才相互的为其约定了特殊的意义。尽管如此,系统也提供了“良好定义”的消息类型以便用户编写的进程或线程可以用来扩充或替换系统提供的服务。
消息原(message primitives)支持多段传输,也就是说从一个线程的地址空间传送到另一个线程的消息不必一定位于单独、连续的缓存上。发送与接收线程都可以指定一个向量表来记录发送以及接收的消息片断在内存中的地址。发送与接收者的消息的每个片断的大小可以是不同的。
消息的多段传输可以让消息头块与消息数据块分离的消息在传输时没有必要执行数据拷贝操作以使其变为一个连续的消息,从而就避免了浪费性能的拷贝操作。另外,如果底层数据结构是环形缓存的话,指定为三段的消息就能够让在这个环形缓存中一个头与两个不相交范围的数据可以在一个元消息(atomic message)中完成传输。硬件上与其等同的就是DMA的发散/聚集功能。
多段传送的示意图如下: 阅读全文…
进程间通信的缩写是IPC,不缩写的就是Interprocess Communication。进程间通信是将实时操作系统微内核转换为全面的POSIX系统中的基本元素之一。当多种服务进程添加到微内核的时候,进程间通信就是将这些元件结合为一个整体的“胶水”。
在UNIX操作系统中消息传递是IPC的主要形式,除此之外还有其他形式的IPC。除非特别声明,这些其他形式的IPC都是建立在本地的消息传递机制之上的。这里使用的策略就是创建一个简单、强健的IPC服务,这个服务可以在微内核里面通过简化代码就可以调整其性能,之后功能更多的IPC服务就可以以此为基础加以完成。
通过将高级的IPC服务(例如基于消息的管道(pipes)以及先进先出(FIFO))与大内核的同类服务进行性能测试比较得到的结果是其性能是相当的。
UNIX提供的IPC形式包括了基于内核的消息传递(Message-passing)、基于内核的信号(Signals)、基于外部线程的POSIX消息队列(POSIX message queues)、基于线程管理器的共享内存(Shared memory)、基于外部线程的管道(Pipes)以及基于外部线程的先进先出(FIFOs)。系统设计师可以基于带宽需求、队列需求、网络透明性等因素来从这些服务中挑选合适的形式。选择某种形式的副作用是复杂的,不过灵活性是可以保证的。
作为定义UNIX系统内核的工程努力的一部分,将消息传递作为IPC的基础元素是经过深思熟虑的。作为进程间消息传递的一种形式,消息传递是用来同步与复制数据的。
同步信息传递
一个线程向可能位于其他进程的线程使用MsgSend()函数后,在目标线程使用MsgReceive()函数,处理消息并执行MsgReply()函数这些动作完成之前,该发送消息的线程将被阻塞。如果一个线程在没有收到消息之前就执行了MsgReceive()函数,那么它就会一直处于阻塞状态直到其他的线程执行了MsgSend()函数。
在UNIX系统中,一个服务器线程会一直循环,等待接收从客户线程发来的消息。一个线程在可以使用CPU的时候就是处于就绪(READY)状态。在实际中,它可能由于它与其他线程的优先级或是调度算法的原因而得不到任何CPU时间,不过这个线程并没有被阻塞。
阅读全文…
最近评论