RPC机制是现代系统的重要的一种机制,和IPC有关,却又没有必然关系。 RPC是更广义的,更高层的,为解决异构/非本地对象/过程调用而形成的一套解决方案,其目的重在解决如何让远程对象/过程的访问可以如同本地对象/过程那般,将真正的差异性向上层客户隐藏。 
而IPC仅仅是解决多个进程间的通信,仅此而已,至于具体通信的语义,那属于特定的领域。 
当远程对象/过程位于本机的另一个进程时,RPC跨越的媒介就是IPC了。 
其实RPC从高层思想而言,基本上都是基于Proxy模式。 
 
先来看Android中的Binder机制,它既是一种IPC,也是一种RPC, 因为通过它,可以跨进程访问(IPC),同时这种跨进程访问的语义是基于对象/过程的访问(RPC)。 在他的实现中,将Binder置于驱动层面,从而可以运行于内核态,然后基于共享内存传递序列化后的对象数据并阻塞,唤醒相应的调用线程/服务线程(Binder 线程)。 同时,在Native 和Jave层分别封装了相应的类以便相应客户快速使用。 
通常,Service运行于某一个进程宿主上,然后将自身实例注册到Service Manager进程中。 Service Manager其实相当于一个Naming Server,用于客户可以基于友好的String Name来查询获得一个远程对象的引用(IBinder)。 当然,如果你能直接得到一个远程对象的引用,那么也是可以绕过Service Manager的,比如远程对象支持异步操作时,其实Client传入的一个对象本身也是支持RPC的,此时远程Service就直接得到了Client传入的对象的IBinder。 
当Service开始Run时,他默认会先开启一个额外的线程作为Binder线程,Binder线程会在后台监听Request并处理。 当进行监听时(Read),如果没有请求到来,则Binder线程就会被阻塞。当请求到来时,Binder线程就会被唤醒以便处理远程对象方法,处理完毕后,Binder线程会Reply the Result。此时的Reply是 Write操作,然后Binder线程再次等待下一次请求(Read操作),再次被阻塞。 
我们再看Client端,Client中的某个线程(Main线程,或者其他的线程)调用远程对象引用的方法时,其实调用的是其Proxy的方法,Proxy在当前进程的当前线程中会将参数序列化,然后最终调用Binder驱动进行Write,即把请求发出。 调用完Write后,紧接着调用Read来获取方法直接的结果(返回值或者输出参数),由于是Read,支持Caller线程被阻塞。 当远程对象所在的Binder线程被唤醒并处理完毕请求并Reply后,Caller线程被唤醒以获得结果。此后Caller线程继续执行其他操作。 
 
而Binder驱动,完成的操作主要是接收Caller线程的序列化数据,并从用户空间copy到内核空间,然后通过Binder查找远程对象所在的进程中的线程,并将其唤醒,然后将数据从内核空间copy到用户空间,最终交给对象处理。 反之也类似。 
 
 
 
 
 
我们再来看看BMP中的RPC机制: 
 
当我们创建一个Service对象时,其实IEnv_CreateInstance返回给我们的,直接就是一个Stub实例(Proxy),至于如何找到并创建,就是BMP中的Naming Service的实现了。 当我们得到一个Stub后,所有的方法调用其实也是序列化后丢给远程对象的,但是这里有两种不同的“丢”的方法: 
 
1. 一种是通过线程间通信的方式来丢的。Service所在的Dispatch线程在一个For循环中无限执行,每次都是取一个Signal然后执行。 其中包括RPC的Signal。 Caller线程的Stub将序列化的对象参数最终基于Signal的方式丢到远程对象所在的Dispatch线程,当期下一次调度时,就会执行之。 
2. 另外一种是基于线程迁移的模式,即Caller线程 准备好序列化后的对象参数后,直接迁移到目标线程中的对象的Skeleton处执行对象方法。 
 
当然了,对BMP和Android的RPC机制的理解,仅仅是停留在整体的一个级别,具体细节方面,尚有待深究。