Binder 学习笔记

1、 Binder原理

Binder通信采用C/S架构,从组件视角来说,包含Client、Server、ServiceManager以及binder驱动,其中ServiceManager用于管理系统中的各种服务。架构图如下所示:

图中Client/Server/ServiceManage由于都处在不同的进程中,他们之间的相互通信都是基于Binder机制。既然基于Binder机制通信,那么同样也是C/S架构,则图中的3大步骤都有相应的Client端与Server端。

1) .注册服务(addService):Server进程要先注册Service到ServiceManager。该过程:Server是客户端,ServiceManager是服务端。

2).获取服务(getService):Client进程使用某个Service前,须先向ServiceManager中获取相应的Service。该过程:Client是客户端,ServiceManager是服务端。

3) .使用服务:Client根据得到的Service信息建立与Service所在的Server进程通信的通路,然后就可以直接与Service交互。该过程:client是客户端,server是服务端。

2、 ServiceManager工作流程

ServiceManager是Binder IPC通信过程中的守护进程,本身工作相对简单,其功能:查询和注册服务,但并没有采用libbinder中的多线程模型来与Binder驱动通信,而是自行编写了binder.c直接和Binder驱动来通信,并且只有一个循环binder_loop进入无限循环来进行读取和处理事务

1).打开binder驱动,并调用mmap()方法分配128k的内存映射空间:binder_open();

2).通知binder驱动使其成为守护进程:binder_become_context_manager();

3).验证selinux权限,判断进程是否有权注册或查看指定服务;

  所有进程的查询和注册服务都必须要到ServiceManager中来,是因为Android的安全管理机制相关,在Android中并不允许任意service都能够在serviceManager中注册binder服务,一般只有root进程才可以。

4).进入循环状态,等待Client端的请求:binder_loop()。

5).注册服务的过程,根据服务名称,但同一个服务已注册,重新注册前会先移除之前的注册信息;

6).死亡通知: 当binder所在进程死亡后,会调用binder_release方法,然后调用binder_node_release.这个过程便会发出死亡通知的回调.

3、 Binder驱动的通信模型

通信流程图:

 

 

各个步骤流程:

1) service 运行,阻塞于 ioctl,等待 client 发起请求

service 进程运行起来,然后通过调用 IPCThreadState 的 joinThreadLoop 在本线程中开始等待客户端请求的到来。两个前提条件:第一个,system service 必须向 service manager 注册自己;第二个,服务端的多线程支持问题。

2) client 通过 ioctl 发起 IPC 请求,等待 service 结果

- client send BC_TRANSACTION —> kernel

客户端从SM那边获取所需要的服务,再发起IPC调用Bp的transaction 函数。然后将调用writeTransactionData向service写入请求数据。

-        kernel return BR_TRANSACTION_COMPLETE —> client

-  client 阻塞于 ioctl,等待 service 返回结果

调用waitForResponse()函数等待返回结果.

3) service 被唤醒,完成业务,返回结果

service被唤醒后,调用talkWithDriver() 去kernel的binder设备那里读取数据,这个数据是之前client在发起IPC请求的时候写入的。然后调用executeCommand()处理获取的数据。

-        kernel return BR_TRANSACTION —> service

-        service impl IPC call

-        service send BC_REPLY —> kernel

-        kernel return BR_TRANSACTION_COMPLETE —> service

4) client 被唤醒,读取 service 返回结果, IPC 结束

-        kernel return BR_REPLY —> client

-        IPC call end

4、 使用binder进行进程通信原因:

1)      能够在手机系统资源有限的前提下满足进程间通信需求

传统的Linux进程通信方式有传统的管道,System V IPC,即消息队列/共享内存/信号量,以及socket中只有socket支持Client-Server的通信方式。当然也可以在这些底层机制上架设一套协议来实现Client-Server通信,但这样增加了系统的复杂性,在手机这种条件复杂,资源稀缺的环境下可靠性也难以保证。

2)      传输性能

socket主要用于跨网络的进程间通信和本机上进程间的通信,但传输效率低,开销大。消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的一块缓存区中,然后从内核缓存区拷贝到接收方缓存区,其过程至少有两次拷贝。虽然共享内存无需拷贝,但控制复杂。

    在binder进程通信机制中只需要一次拷贝,数据就能从client端到server端。具体见下面binder间的数据传递。

3)      安全性问题

首先传统IPC的接收方无法获得对方进程可靠的UID/PID(用户ID/进程ID),从而无法鉴别对方身份。Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志,提高应用的安全性。

5、 Binder间的数据传递

1) 小数据类型采用parcel 进行传递,数据从用户空间copy到kernel空间即binder驱动中,再通过binder驱动传递给服务空间。

虚拟进程地址空间(vm_area_struct)和虚拟内核地址空间(vm_struct)都映射到同一块物理内存空间。当Client端与Server端发送数据时,Client(作为数据发送端)先从自己的进程空间把IPC通信数据copy_from_user拷贝到内核空间,而Server端(作为数据接收端)与内核共享数据,不再需要拷贝数据,而是通过内存地址空间的偏移量,即可获悉内存地址,整个过程只发生一次内存拷贝。一般地做法,需要Client端进程空间拷贝到内核空间,再由内核空间拷贝到Server进程空间,会发生两次拷贝。

 

 

2) 大数据类型采用匿名共享内存(Ashmen)进行传递

3) Binder对象 采用kernel binder驱动专门处理。

6、 系统服务(system service)服务绑定

系统级别的服务system service需要先将service add到ServiceManager中去,通过函数addService() 来实现。然后等待client去getService获得服务。所有系统级别的服务都是有SM(Service Manager)来统一管理。

 

 

7、 普通服务实现服务绑定

由于系统服务都是通过ServiceManager来统一管理,service将服务注册到SM中,client再从SM中取出服务。而对于普通的service是没有权限将自己注册到SM中,而是通过ActivityManager来中转。普通服务将自己挂靠在AM中,而AM是系统服务将自己注册到了SM中,这样通铺用户也可以通过binder机制来实现进程间的调用。

实现过程:首先有个一个普通的服务叫 SA(Service A,它在 Proc A 中),另一个普通应用 AppB(它在 Porc B 中)。现在 SA 要提供一些 IPC 接口,它首先得继承 Service ,然后实现 IBinder 接口,并且在 onBind 函数中返回自己实现的 IBinder 对象。 AppB 要调用 SA 的某个接口,那么它就得调用 Context 的 bindService,并且自己实现 ServiceConnection,在 onServiceConnected 中取得 SA 的 IBiner 对象,然后就可以通过取得的 IBinder 对象调用 SA 的 IPC 接口了。

由于被绑定的服务是普通服务,在client去调用服务时不一定已经启动,所以存在这三种情况:

-        要绑定的服务所在的进程已经在运行,并且服务代码也已经执行了,这个时候只要请求绑定服务就行了。

-        要绑定的服务所在的进程已经在运行,但是服务代码没有执行,这个时候需要执行服务代码,然后再绑定服务。

-        要绑定的服务的进程还没运行,要先启动服务所在的进程,然后执行服务代码,最后再绑定服务。

posted on 2017-03-28 15:35  kma  阅读(186)  评论(0编辑  收藏  举报

导航