进程间通信
进程间通信的缩写是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时间,不过这个线程并没有被阻塞。
下面看一下客户线程的示意图:
- 如果客户线程调用MsgSend(),而服务器线程还没有调用MsgReceive()函数,这时客户线程的状态就变为发送阻塞状态(SEND blocked)。一旦服务器线程调用了MsgReceive()函数,系统内核就会将客户线程的状态变为回复阻塞(REPLY blocked),这就意味着服务器线程已经接受到消息了,现在必须要回复。当服务器线程调用了MsgReply()函数,客户线程就会变为就绪状态。
- 如果客户线程调用MsgSend()函数,而服务器线程早就因为调用MsgReceive()函数而阻塞,那么客户线程就直接进入回复阻塞状态,完全跳过了发送阻塞状态。
- 如果服务器线程失败、退出或消失,客户线程就变为就绪状态,同时MsgSend()函数就会返回一个错误。
下面看一下服务器线程的示意图:
- 如果服务器线程调用了MsgReceive()函数,而没有其他线程向其发送消息,那么该线程就进入接收阻塞状态(RECEIVE blocked)。当其他线程向其发送消息之后,该服务器线程就变为就绪状态。
- 如果服务器线程调用了MsgReceive()函数,而其他线程早就想起发送了消息,那么MsgReceive()函数就会立即返回该消息。在这种情况下,服务器线程并不会进入阻塞状态。
- 如果服务器线程调用了MsgReply()函数,该线程不阻塞。
这种内置的阻塞机制通过发送数据使得发送线程进入阻塞状态并调度接收线程开始运行,从而同步发送线程的运行。这些都不用通过直接调用内核来确定哪个线程运行(而在其他的IPC形式中是需要调用内核来完成该动作的)。线程运行以及数据转移都是从一个环境到另一个环境直接进行的。
在这些消息原中,数据队列能力是被取消的,因为在必要的时候可以在接收线程内部来完成数据队列。发送线程一般都是准备接收反应的;这时队列就是过于复杂并添加额外开销的(例如,可能会减慢非队列情况下的处理速度)。这样设计的最终结果就是,发送线程不必创建一个分开、特定的块函数来等待反应(在其他形式的IPC中会用到这种方式的)。
发送与接收函数会造成线程阻塞以及同步,可是MsgReply()函数以及MsgError()函数则不会阻塞线程。由于客户线程早就因为等待回复而进入阻塞状态,就不需要额外的同步机制了,所以一个由MsgReply()产生的阻塞就没有必要了。这样就可以让服务器线程回复客户线程后继续处理,同时内核或网络代码就可以异步的将回复数据传送给发送线程并使其进入就绪运行状态。由于大多数服务器线程都要进行一下处理以准备接收下一个请求(在接收下一个请求时它们会在被阻塞),这种机制就是合适的。
MsgReplay()与MsgError()
MsgReply()函数用于向客户线程返回一个状态以及0个或多个比特的数据。MsgError()函数则只是返回一个状态。这两个函数都能够解除客户线程由于MsgSend()函数而进入的阻塞状态。
Related posts:
近期评论