Android Binder IPC分析(精简版)

1 . binder 通信概述 

binder 通信是一种 client-server 的通信结构, 
1. 从表面上来看,是 client 通过获得一个 server 的代理接口,对 server 进行直接调用; 
2. 实际上,代理接口中定义的方法与 server 中定义的方法是一一对应的; 
3.client 调用某个代理接口中的方法时,代理接口的方法会将 client 传递的参数打包成为 Parcel 对象; 
4. 代理接口将该 Parcel 发送给内核中的 binder driver. 
5.server 会读取 binder driver 中的请求数据,如果是发送给自己的,解包 Parcel 对象,处理并将结果返回; 
6. 整个的调用过程是一个同步过程,在 server 处理的时候, client 会 block 住。

2 . service manager

Service Manager 是一个 linux 级的进程 , 顾名思义,就是 service 的管理器。这里的 service 是什么概念呢?这里的service 的概念和 init 过程中 init.rc 中的 service 是不同, init.rc 中的 service 是都是 linux 进程,但是这里的 service 它并不一定是一个进程,也就是说可能一个或多个 service 属于同一个 linux 进程。在这篇文章中不加特殊说明均指 android native 端的 service 。

任何 service 在被使用之前,均要向 SM(Service Manager) 注册,同时客户端需要访问某个 service 时,应该首先向SM 查询是否存在该服务。如果 SM 存在这个 service ,那么会将该 service 的 handle 返回给 client , handle 是每个service 的唯一标识符。 

SM 的入口函数在 service_manager.c 中,下面是 SM 的代码部分 
int main(int argc, char **argv) 

struct binder_state *bs; 
void *svcmgr = BINDER_SERVICE_MANAGER; 

bs = binder_open(128*1024); 

if (binder_become_context_manager(bs)) { 
LOGE("cannot become context manager (%s)/n", strerror(errno)); 
return -1; 


svcmgr_handle = svcmgr; 
binder_loop(bs, svcmgr_handler); 
return 0; 
}

这个进程的主要工作如下: 
1. 初始化 binder ,打开 /dev/binder 设备;在内存中为 binder 映射 128K 字节空间; 
2. 指定 SM 对应的代理 binder 的 handle 为 0 ,当 client 尝试与 SM 通信时,需要创建一个 handle 为 0 的代理 binder ,这里的代理 binder 其实就是第一节中描述的那个代理接口;

3. 通知 binder driver(BD) 使 SM 成为 BD 的 context manager ; 
4. 维护一个死循环,在这个死循环中,不停地去读内核中 binder driver ,查看是否有可读的内容;即是否有对 service的操作要求 , 如果有,则调用 svcmgr_handler 回调来处理请求的操作。

5.SM 维护了一个 svclist 列表来存储 service 的信息。



这里需要声明一下,当 service 在向 SM 注册时,该 service 就是一个 client ,而 SM 则作为了 server 。而某个进程需要与 service 通信时,此时这个进程为 client , service 才作为 server 。因此 service 不一定为 server ,有时它也是作为 client 存在的。

由于下面几节会介绍一些与 binder 通信相关的几个概念,所以将 SM 的功能介绍放在了后面的部分来讲。 

应用和 service 之间的通信会涉及到 2 次 binder 通信。 

1. 应用向 SM 查询 service 是否存在,如果存在获得该 service 的代理 binder ,此为一次 binder 通信; 
2. 应用通过代理 binder 调用 service 的方法,此为第二次 binder 通信。 

3 . ProcessState

ProcessState 是以单例模式设计的。每个进程在使用 binder 机制通信时,均需要维护一个 ProcessState 实例来描述当前进程在 binder 通信时的 binder 状态。 
ProcessState 有如下 2 个主要功能: 
1. 创建一个 thread, 该线程负责与内核中的 binder 模块进行通信,称该线程为 Pool thread ; 
2. 为指定的 handle 创建一个 BpBinder 对象,并管理该进程中所有的 BpBinder 对象。

3.1 Pool thread

在 Binder IPC 中,所有进程均会启动一个 thread 来负责与 BD 来直接通信,也就是不停的读写 BD ,这个线程的实现主体是一个 IPCThreadState 对象,下面会介绍这个类型。

下面是 Pool thread 的启动方式:

ProcessState::self()->startThreadPool();

3.2 BpBinder 获取

BpBinder 主要功能是负责 client 向 BD 发送调用请求的数据。它是 client 端 binder 通信的核心对象,通过调用transact 函数向 BD 发送调用请求的数据,它的构造函数如下:

BpBinder(int32_t handle);
通过 BpBinder 的构造函数发现, BpBinder 会将当前通信中 server 的 handle 记录下来,当有数据发送时,会通知 BD 数据的发送目标。

ProcessState 通过如下方式来获取 BpBinder 对象:

ProcessState::self()->getContextObject(handle);

在这个过程中, ProcessState 会维护一个 BpBinder 的 vector mHandleToObject ,每当 ProcessState 创建一个BpBinder 的实例时,回去查询 mHandleToObject ,如果对应的 handle 已经有 binder 指针,那么不再创建,否则创建binder 并插入到 mHandleToObject 中。 
ProcessState 创建的 BpBinder 实例,一般情况下会作为参数构建一个 client 端的代理接口,这个代理接口的形式为BpINTERFACE , 例如在与 SM 通信时, client 会创建一个代理接口 BpServiceManager .

4 . IPCThreadState

IPCThreadState 也是以单例模式设计的。由于每个进程只维护了一个 ProcessState 实例,同时 ProcessState 只启动一个 Pool thread ,也就是说每一个进程只会启动一个 Pool thread ,因此每个进程则只需要一个 IPCThreadState 即可。 
Pool thread 的实际内容则为: 
IPCThreadState::self()->joinThreadPool();

ProcessState 中有 2 个 Parcel 成员, mIn 和 mOut , Pool thread 会不停的查询 BD 中是否有数据可读,如果有将其读出并保存到 mIn ,同时不停的检查 mOut 是否有数据需要向 BD 发送,如果有,则将其内容写入到 BD 中,总而言之,从 BD 中读出的数据保存到 mIn ,待写入到 BD 中的数据保存在了 mOut 中。

ProcessState 中生成的 BpBinder 实例通过调用 IPCThreadState 的 transact 函数来向 mOut 中写入数据,这样的话这个 binder IPC 过程的 client 端的调用请求的发送过程就明了了 。

IPCThreadState 有两个重要的函数, talkWithDriver 函数负责从 BD 读写数据, executeCommand 函数负责解析并执行 mIn 中的数据。

5. 主要基类

5.1 基类 IInterface

为 server 端提供接口,它的子类声明了 service 能够实现的所有的方法;


5.2 基类 IBinder 
BBinder 与 BpBinder 均为 IBinder 的子类,因此可以看出 IBinder 定义了 binder IPC 的通信协议, BBinder 与 BpBinder在这个协议框架内进行的收和发操作,构建了基本的 binder IPC 机制。 
5.3 基类 BpRefBase 
client 端在查询 SM 获得所需的的 BpBinder 后, BpRefBase 负责管理当前获得的 BpBinder 实例。

6. 两个接口类

6.1 BpINTERFACE

如果 client 想要使用 binder IPC 来通信,那么首先会从 SM 出查询并获得 server 端 service 的 BpBinder ,在 client端,这个对象被认为是 server 端的远程代理。为了能够使 client 能够想本地调用一样调用一个远程 server , server 端需要向 client 提供一个接口, client 在在这个接口的基础上创建一个 BpINTERFACE ,使用这个对象, client 的应用能够想本地调用一样直接调用 server 端的方法。而不用去关心具体的 binder IPC 实现。 
下面看一下 BpINTERFACE 的原型: 
class BpINTERFACE : public BpInterface<IINTERFACE> 

顺着继承关系再往上看 
template<typename INTERFACE> 
class BpInterface : public INTERFACE, public BpRefBase 

BpINTERFACE 分别继承自 INTERFACE ,和 BpRefBase ; 
● BpINTERFACE 既实现了 service 中各方法的本地操作,将每个方法的参数以 Parcel 的形式发送给 BD 。 
例如 BpServiceManager 的 
virtual status_t addService(const String16& name, const sp<IBinder>& service) 

Parcel data, reply; 
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); 
data.writeString16(name); 
data.writeStrongBinder(service); 
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); 
return err == NO_ERROR ? reply.readExceptionCode() : err; 

● 同时又将 BpBinder 作为了自己的成员来管理,将 BpBinder 存储在 mRemote 中, BpServiceManager 通过调用BpRefBase 的 remote() 来获得 BpBinder 指针。

6.2 BnINTERFACE

在定义 android native 端的 service 时,每个 service 均继承自 BnINTERFACE(INTERFACE 为 service name) 。BnINTERFACE 类型定义了一个 onTransact 函数,这个函数负责解包收到的 Parcel 并执行 client 端的请求的方法。 

顺着 BnINTERFACE 的继承关系再往上看, 
class BnINTERFACE: public BnInterface<IINTERFACE> 

IINTERFACE 为 client 端的代理接口 BpINTERFACE 和 server 端的 BnINTERFACE 的共同接口类,这个共同接口类的目的就是保证 service 方法在 C-S 两端的一致性。 

再往上看 
class BnInterface : public INTERFACE, public BBinder 

同时我们发现了 BBinder 类型,这个类型又是干什么用的呢?既然每个 service 均可视为一个 binder ,那么真正的 server端的 binder 的操作及状态的维护就是通过继承自 BBinder 来实现的。可见 BBinder 是 service 作为 binder 的本质所在。 

那么 BBinder 与 BpBinder 的区别又是什么呢? 

其实它们的区别很简单, BpBinder 是 client 端创建的用于消息发送的代理,而 BBinder 是 server 端用于接收消息的通道。查看各自的代码就会发现,虽然两个类型均有 transact 的方法,但是两者的作用不同, BpBinder 的 transact 方法是向 IPCThreadState 实例发送消息,通知其有消息要发送给 BD ;而 BBinder 则是当 IPCThreadState 实例收到 BD 消息时,通过 BBinder 的 transact 的方法将其传递给它的子类 BnSERVICE 的 onTransact 函数执行 server 端的操作。

7. Parcel

Parcel 是 binder IPC 中的最基本的通信单元,它存储 C-S 间函数调用的参数 . 但是 Parcel 只能存储基本的数据类型,如果是复杂的数据类型的话,在存储时,需要将其拆分为基本的数据类型来存储。 

简单的 Parcel 读写不再介绍,下面着重介绍一下 2 个函数

7.1 writeStrongBinder

当 client 需要将一个 binder 向 server 发送时,可以调用此函数。例如 
virtual status_t addService(const String16& name, const sp<IBinder>& service) 

Parcel data, reply; 
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); 
data.writeString16(name); 
data.writeStrongBinder(service); 
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); 
return err == NO_ERROR ? reply.readExceptionCode() : err; 



看一下 writeStrongBinder 的实体 
status_t Parcel::writeStrongBinder(const sp<IBinder>& val) 

return flatten_binder(ProcessState::self(), val, this); 


接着往里看 flatten_binder 
status_t flatten_binder(const sp<ProcessState>& proc, 
const sp<IBinder>& binder, Parcel* out) 

flat_binder_object obj; 

obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; 
if (binder != NULL) { 
IBinder *local = binder->localBinder(); 
if (!local) { 
BpBinder *proxy = binder->remoteBinder(); 
if (proxy == NULL) { 
LOGE("null proxy"); 

const int32_t handle = proxy ? proxy->handle() : 0; 
obj.type = BINDER_TYPE_HANDLE; 
obj.handle = handle; 
obj.cookie = NULL; 
} else { 
obj.type = BINDER_TYPE_BINDER; 
obj.binder = local->getWeakRefs(); 
obj.cookie = local; 

} else { 
obj.type = BINDER_TYPE_BINDER; 
obj.binder = NULL; 
obj.cookie = NULL; 


return finish_flatten_binder(binder, obj, out); 


还是拿 addService 为例,它的参数为一个 BnINTERFACE 类型指针, BnINTERFACE 又继承自 BBinder , 
BBinder* BBinder::localBinder() 

return this; 

所以写入到 Parcel 的 binder 类型为 BINDER_TYPE_BINDER ,同时你在阅读 SM 的代码时会发现如果 SM 收到的service 的 binder 类型不为 BINDER_TYPE_HANDLE 时, SM 将不会将此 service 添加到 svclist ,但是很显然每个service 的添加都是成功的, addService 在开始传递的 binder 类型为 BINDER_TYPE_BINDER , SM 收到的 binder 类型为 BINDER_TYPE_HANDLE ,那么这个过程当中究竟发生了什么? 
为了搞明白这个问题,花费我很多的事件,最终发现了问题的所在,原来在 BD 中做了如下操作(drivers/staging/android/Binder.c) :


static void binder_transaction(struct binder_proc *proc, 
struct binder_thread *thread, 
struct binder_transaction_data *tr, int reply) 

.......................................... 

if (fp->type == BINDER_TYPE_BINDER) 
fp->type = BINDER_TYPE_HANDLE; 
else 
fp->type = BINDER_TYPE_WEAK_HANDLE; 
fp->handle = ref->desc; 
.......................................... 



阅读完 addService 的代码,你会发现 SM 只是保存了 service binder 的 handle 和 service 的 name ,那么当 client 需要和某个 service 通信了,如何获得 service 的 binder 呢?看下一个函数

7.2 readStrongBinder

当 server 端收到 client 的调用请求之后,如果需要返回一个 binder 时,可以向 BD 发送这个 binder ,当IPCThreadState 实例收到这个返回的 Parcel 时, client 可以通过这个函数将这个被 server 返回的 binder 读出。


sp<IBinder> Parcel::readStrongBinder() const 

sp<IBinder> val; 
unflatten_binder(ProcessState::self(), *this, &val); 
return val; 
}


往里查看 unflatten_binder


status_t unflatten_binder(const sp<ProcessState>& proc, 
const Parcel& in, sp<IBinder>* out) 

const flat_binder_object* flat = in.readObject(false); 

if (flat) { 
switch (flat->type) { 
case BINDER_TYPE_BINDER: 
*out = static_cast<IBinder*>(flat->cookie); 
return finish_unflatten_binder(NULL, *flat, in); 
case BINDER_TYPE_HANDLE: 
*out = proc->getStrongProxyForHandle(flat->handle); 
return finish_unflatten_binder( 
static_cast<BpBinder*>(out->get()), *flat, in); 


return BAD_TYPE; 
}


发现如果 server 返回的 binder 类型为 BINDER_TYPE_BINDER 的话,也就是返回一个 binder 引用的话,直接获取这个binder ;如果 server 返回的 binder 类型为 BINDER_TYPE_HANDLE 时,也就是 server 返回的仅仅是 binder 的 handle,那么需要重新创建一个 BpBinder 返回给 client 。 


有上面的代码可以看出, SM 保存的 service 的 binder 仅仅是一个 handle ,而 client 则是通过向 SM 获得这个 handle ,从而重新构建代理 binder 与 server 通信。 


这里顺带提一下一种特殊的情况, binder 通信的双方即可作为 client ,也可以作为 server. 也就是说此时的 binder 通信是一个半双工的通信。那么在这种情况下,操作的过程会比单工的情况复杂,但是基本的原理是一样的,有兴趣可以分析一下 MediaPlayer 和 MediaPlayerService 的例子。

8. 经典桥段分析

main_ mediaserver.cpp 
int main(int argc, char** argv) 
{

// 创建进程 mediaserver 的 ProcessState 实例 
sp<ProcessState> proc(ProcessState::self());

// 获得 SM 的 BpServiceManager 
sp<IServiceManager> sm = defaultServiceManager(); 
LOGI("ServiceManager: %p", sm.get());

// 添加 mediaserver 中支持的 service 。 
AudioFlinger::instantiate(); 
MediaPlayerService::instantiate(); 
CameraService::instantiate(); 
AudioPolicyService::instantiate();

// 启动 ProcessState 的 pool thread 
ProcessState::self()->startThreadPool();

// 这一步有重复之嫌,加不加无关紧要。 
IPCThreadState::self()->joinThreadPool(); 
}

9. Java 层的 binder 机制

了解了 native 通信机制后,再去分析 JAVA 层的 binder 机制,就会很好理解了。它只是对 native 的 binder 做了一个封装。这一部分基本上没有太复杂的过程,这里不再赘述了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
千里马8年Android系统及应用开发经验,曾担任过美国unokiwi公司移动端技术总监兼架构师,对系统开发,性能优化,应用高级开发有深入的研究,Android开源定制ROM Lineage的贡献者之一,国内首家线下开辟培训Android Framework课程,拥有2年的Android系统培训经验。成为腾讯课堂专业负责android framework课程分享第一人,致力于提高国内android Framework水平Android Framework领域内是国内各大手机终端科技公司需要的人才,应用开发者都对Android系统充满着好奇,其中的binder是重中之重,都说无binderAndroidbinde是Android系统的任督二脉。课程水平循序渐进,由中级再到高级,满足各个层次水平的android开发者。1、灵活使用binder跨进程通信,在app端对它的任何api方法等使用自如2、可以单独分析android系统源码中任何binder部分,分析再也没有难度3、掌握binder驱动本质原理,及对应binder驱动怎么进行跨进程通信,及内存等拷贝方式数据等4、对binder从上层的java app端一直到最底层的内核binder驱动,都可以顺利理通5、针对系统开发过程中遇到的binder报错等分析方法,及binder bug案例学习6、针对面试官任何的binder问题都可以对答自如7、socket这种跨进程通信实战使用8、针对android源码中使用的socket源码轻松掌握9、android系统源码中最常见的socketpair中双向跨进程通信10、使用socket实现一个可以让app执行shell命令的程序

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值