您的位置:首页 > 移动开发 > Android开发

老罗的ANDROID之旅---IPC学习笔记

2013-10-11 10:10 471 查看
下面所涉及到的内容基本上都来自---老罗的Android之旅

在Android系统的Binder机制中,由一系统组件组成,分别是Client、Server、Service Manager和Binder驱动程序,其中Client、Server和Service Manager运行在用户空间,Binder驱动程序运行内核空间。Binder就是一种把这四个组件粘合在一起的粘结剂了,其中,核心组件便是Binder驱动程序了,Service
Manager提供了辅助管理的功能,Client和Server正是在Binder驱动和Service Manager提供的基础设施上,进行Client-Server之间的通信。Service Manager和Binder驱动已经在Android平台中实现好,开发者只要按照规范实现自己的Client和Server组件就可以了。说起来简单,做起难,对初学者来说,Android系统的Binder机制是最难理解的了,而Binder机制无论从系统开发还是应用开发的角度来看,都是Android系统中最重要的组成,因此,很有必要深入了解Binder的工作方式。

Android系统Binder机制中的四个组件Client、Server、Service Manager和Binder驱动程序的关系如下图所示:



说明;

1. Client、Server和Service Manager实现在用户空间中,Binder驱动程序实现在内核空间中

        2. Binder驱动程序和Service Manager在Android平台中已经实现,开发者只需要在用户空间实现自己的Client和Server

        3. Binder驱动程序提供设备文件/dev/binder与用户空间交互,Client、Server和Service Manager通过open和ioctl文件操作函数与Binder驱动程序进行通信

        4. Client和Server之间的进程间通信通过Binder驱动程序间接实现

        5. Service Manager是一个守护进程,用来管理Server,并向Client提供查询Server接口的能力

接下来我们通过以下几个情景来进一步对Binder源码进行分析,深入了解Binder机制

一:浅谈Service
Manager成为Android进程间通信(IPC)机制Binder守护进程之路

实在是太复杂,涉及的数据结构特别多,要理解好必须得对Linux内核,驱动等要好好掌握。。。。
下面用一时序图进行简要的梳理一下相关的流程。



总结一下,Service Manager是成为Android进程间通信(IPC)机制Binder守护进程的过程是这样的:

        1. 打开/dev/binder文件:open("/dev/binder", O_RDWR);

        2. 建立128K内存映射:mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);

        3. 通知Binder驱动程序它是守护进程:binder_become_context_manager(bs);

        4. 进入循环等待请求的到来:binder_loop(bs, svcmgr_handler);

        在这个过程中,在Binder驱动程序中建立了一个struct binder_proc结构、一个struct  binder_thread结构和一个struct binder_node结构,这样,Service Manager就在Android系统的进程间通信机制Binder担负起守护进程的职责了。

二:浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service
Manager接口之路

上面介绍了Service
Manager是如何成为Binder机制的守护进程的。既然作为守护进程,Service Manager的职责当然就是为Server和Client服务了。那么,Server和Client如何获得Service Manager接口,进而享受它提供的服务呢?下面将简要分析Server和Client获得Service Manager的过程。

首先上一相关类图



说明:其中BpInterface是一个模板类,IServiceManager类继承了IInterface类,而IInterface类和BpRefBase类又分别继承了RefBase类。在BpRefBase类中,有一个成员变量mRemote,它的类型是IBinder*,实现类为BpBinder,它表示一个Binder引用,引用句柄值保存在BpBinder类的mHandle成员变量中。BpBinder类通过IPCThreadState类来和Binder驱动程序并互,而IPCThreadState又通过它的成员变量mProcess来打开/dev/binder设备文件,mProcess成员变量的类型为ProcessState。ProcessState类打开设备/dev/binder之后,将打开文件描述符保存在mDriverFD成员变量中,以供后续使用。

在了解了相关类之间的关系之后,我们从defaultServiceManager()开始进行代码的跟中,下面是其相关时序图:



也就是说在defaultServiceManager()中:最终的gDefaultServiceManager
= new BpServiceManager(new BpBinder(0)。

这样,Service
Manager远程接口就创建完成了,它本质上是一个BpServiceManager,包含了一个句柄值为0的Binder引用。

        在Android系统的Binder机制中,Server和Client拿到这个Service Manager远程接口之后怎么用呢?

        对Server来说,就是调用IServiceManager::addService这个接口来和Binder驱动程序交互了,即调用BpServiceManager::addService 。而BpServiceManager::addService又会调用通过其基类BpRefBase的成员函数remote获得原先创建的BpBinder实例,接着调用BpBinder::transact成员函数。在BpBinder::transact函数中,又会调用IPCThreadState::transact成员函数,这里就是最终与Binder驱动程序交互的地方了。回忆一下前面的类图,IPCThreadState有一个PorcessState类型的成中变量mProcess,而mProcess有一个成员变量mDriverFD,它是设备文件/dev/binder的打开文件描述符,因此,IPCThreadState就相当于间接在拥有了设备文件/dev/binder的打开文件描述符,于是,便可以与Binder驱动程序交互了。

       对Client来说,就是调用IServiceManager::getService这个接口来和Binder驱动程序交互了。具体过程上述Server使用Service Manager的方法是一样的。

下面就深入到Binder驱动程序这一层,进行详细的源代码分析,以便更好地理解Binder进程间通信机制。

三:Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析

以MediaPlayerService为例,看其Server的启动过程:



在IServiceManager::addService过程中。对IServiceManager::addService过程中MediaPlayerService、ServiceManager和BinderDriver之间的交互不是很了解,附上老罗的一张它们三者之间的交互图



至此,Server就启动起来了,Server启动起来之后,就会在一个无穷循环中等待Client的请求了,下面我们将介绍Client如何通过Service
Manager远程接口来获得Server远程接口,进而调用Server远程接口来使用Server提供的服务

四:Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析

以Android系统中自带的多媒体播放器为例子来说明Client是如何通过IServiceManager::getService接口来获得MediaPlayerService这个Server的远程接口的:下面是相关序列图



以上3、4的说明(来自评论)

首先server和client都调用了ProcessState的构造函数从而都向驱动申请了一块物理内存用于通信数据的存放,然后:
1.server调用SM的addService函数传递一个字符串和实际Binder对象在自己虚拟地址空间的地址到Binder驱动,Binder驱动记录下该地址值,在SM申请的物理内存中分配一个虚拟地址并连同字符串一起传递给SM,而且Binder驱动会记录下两个地址之间的对应关系.
2.client调用SM的getService函数传递一个字符串给SM,SM将相对应的虚拟地址值传递给Binder驱动,Binder驱动查询出实际对象的地址,在client申请的物理内存中分配一个虚拟地址并传递给client,而且Binder驱动会记录下这个地址和实际Binder对象地址之间的对应关系,client这里得到的就是实际Binder的引用了.
到了这一步,真正的Binder对象就拥有两个引用,一个在SM,一个client.
3.client通过得到的Binder引用调用server中的函数,驱动会根据传递过来引用值找到应该处理该请求的进程,并唤醒其中的Binder线程,调用BBinder对象的onTransaction函数,最终调用到实际的函数。

5、Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析



package android.os;
interface IHelloService
{
void setVal(int val);
int getVal();
}

public interface IHelloService extends android.os.IInterface
{
public void setVal(int val) throws android.os.RemoteException;
public int getVal() throws android.os.RemoteException;

public static abstract class Stub extends android.os.Binder implements android.os.IHelloService
{
static final int TRANSACTION_setVal = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getVal = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

public Stub(){}
public static android.os.IHelloService asInterface(android.os.IBinder obj){
...
return new android.os.IHelloService.Stub.Proxy(obj);
}
public android.os.IBinder asBinder(){}
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException  {
...
case TRANSACTION_setVal:
...
case TRANSACTION_getVal:
...
}

private static class Proxy implements android.os.IHelloService
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {}
public android.os.IBinder asBinder() {}
public java.lang.String getInterfaceDescriptor() {}
public void setVal(int val) throws android.os.RemoteException {
...
mRemote.transact(Stub.TRANSACTION_setVal, _data, _reply, 0);
...
}
public int getVal() throws android.os.RemoteException {
...
mRemote.transact(Stub.TRANSACTION_getVal, _data, _reply, 0);
...
}
}
}

}

这里我们可以看到IHelloService.aidl这个文件编译后的真面目,原来就是根据IHelloService接口的
定义生成相应的Stub和Proxy类,这个就是我们熟悉的Binder机制的内容了,即实现这个HelloService
的Server必须继续于这里的IHelloService.Stub类,而这个HelloService的远程接口就是这里的
IHelloService.Stub.Proxy对象获得的IHelloService接口

public class HelloService extends IHelloService.Stub {
private static final String TAG = "HelloService";

HelloService() {
init_native();
}

public void setVal(int val) {
setVal_native(val);
}

public int getVal() {
return getVal_native();
}

private static native boolean init_native();
private static native void setVal_native(int val);
private static native int getVal_native();
}

有了HelloService这个Server类后,下一步就是考虑怎么样把它启动起来了。
在frameworks/base/services/java/com/android/server/SystemServer.java文件中,定义了SystemServer类。
SystemServer对象是在系统启动的时候创建的,它被创建的时候会启动一个线程来创建HelloService,并且把它
添加到Service Manager中去。

class ServerThread extends Thread {
......
@Override
public void run() {
......
Looper.prepare();
......
try {
Slog.i(TAG, "Hello Service");
ServiceManager.addService("hello", new HelloService());  //ServiceManagerProxy.addService()
} catch (Throwable e) {
Slog.e(TAG, "Failure starting Hello Service", e);
}
......
Looper.loop();
......
}
}

......

public class SystemServer
{
native public static void init1(String[] args);
......
public static final void init2() {
Slog.i(TAG, "Entered the Android system server!");
Thread thr = new ServerThread();
thr.setName("android.server.ServerThread");
thr.start();
}
......
}

而Client端就是借助Service Manager这个Java远程接口来获得HelloService的远程接口的:
public class Hello extends Activity implements OnClickListener {
......
private IHelloService helloService = null;
......
@Override
public void onCreate(Bundle savedInstanceState) {
helloService = IHelloService.Stub.asInterface(ServiceManager.getService("hello"));
//====》
//helloService = IHelloService.Stub.asInterface(ServiceManagerProxy.getService("hello"));
//====》
//helloService = IHelloService.Stub.asInterface(new BinderProxy()));
//====>>
//helloService = android.os.IHelloService.Stub.Proxy(new BinderProxy());
}
......
}

这样,我们就获得了HelloService的远程接口了,它实质上是一个实现了IHelloService接口的IHelloService.Stub.Proxy对象。

然后通过client端的helloService.getVal以及setVal来调用android.os.IHelloService.Stub.Proxy的setVal、getVal进而调用
mRemote.transact来请求HelloService执行TRANSACTION_getVal操作。经过一大圈又回到android.os.IHelloService.Stub的onTransact
来执行相关的调用......函数最终又调用了HelloService.getVal函数,最终,经过层层返回,就回到IHelloService.Stub.Proxy.getVal函数中来




呵呵,胡乱瞎凑完了。。。。

----------------------------------------------------谢谢 老罗的Android之旅-------------------------------------------------------------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Android ipc 学习笔记