binder实现原理分析

binder整体架构

使用binder进行跨进程通信/调用的基本架构如下图所示:

 

Server提供binder服务,Client通过binder远程访问Server提供的服务。对Client而言,从binder的用户角度看 client访问server提供的服务就如同client调用本地的API一样方便。

 

Client和server之间传送消息是通过binder驱动实现的,其中的大多数访问驱动是通过ioctl系统调用实现。

在实现方面,binder主要有几个部分:

  1. Binder驱动:实现在linux内核中,这部分实现真正的跨进程通信。驱动向上提供杂项设备/dev/binder、/dev/hwbinder、/dev/vndbinder等,用户层通过操作这几个设备实现对binder驱动的访问。
  2. libbinder动态库:所有基于binder开发应用程序使用的动态库,封装了对binder驱动的底层调用,使得用户可以不必关心如何和binder驱动交互。
  3. service_manager可执行程序:在android系统中,所有的binder服务都在一个统一的管理中心注册 并可被系统中的任意进程发现并访问。Service_manager便是这个管理中心。系统启动时,service_manager作为一个守护进程启动,并持续在系统中运行。

service manager介绍

service manager的实现分为两部分:

1. service_manager.c  service manager的主体部分,单独进程运行,处理注册服务/获取服务/检查服务等命令

2. IserviceManager.cpp/IserviceManager.h service manager对外提供的proxy接口,使得其他进程和service manager的交互可以通过几个简单的API来实现。

Service manager创建了一个特殊的binder实体,它的编号是固定值0。其他所有的binder服务需要向0号binder服务去注册。当某个client想要访问某个binder服务时,也是先去0号binder服务去查询该目标服务是否存在。

 

在驱动中,所有binder实体均通过handle来索引。service manager是一个特殊的binder实体,其handle值为固定值0,因而其他任何进程都可以通过0号handle来访问service manager。

每个binder设备(如:/dev/binder)有一个context信息,该context内保存当前binder设备使用的context manager的binder实体。当service manager进程调用ioctl:BINDER_SET_CONTEXT_MGR时,将在该进程内部申请一个binder实体,并将该binder实体用作相应binder设备的context manager。

实现IPC

Binder协议

用户层操作binder驱动基本都是通过ioctl控制/dev/binder设备来实现的,binder协议是指用户层操作binder设备使用的ioctl命令。几个常见的binder协议如下:

 

 

BINDER_WRITE_READ

读写操作,最常用的命令。IPC过程就是通过这个命令进行数据传递

BINDER_SET_MAX_THREADS

设置进程支持的最大线程数量

BINDER_SET_CONTEXT_MGR

设置自身为ServiceManager

BINDER_THREAD_EXIT

通知驱动Binder线程退出

BINDER_VERSION

获取Binder驱动的版本号

 

Binder transact流程

Binder client里调用BpBinder::transact()来向binder服务发送消息。

BpBinder::transact()

  |- IPCThreadState::transact()   -- 此处将目标server改用handle来描述,handle仅在client进程内有意义

       |- IPCThreadState::writeTransactionData(BC_TRANSACTION,...)  -- 此处将目标server的坐标 以及待传数据Parcel的data指针 写入mOut

       |- IPCThreadState::talkWithDriver(doReceive=true)

            |- ioctl(“/dev/binder”, BINDER_WRITE_READ)

 

Driver处理:

binder_ioctl(BINDER_WRITE_READ)

  |- binder_thread_write 解出命令 BC_TRANSACTION, 读出目标server的坐标 以及待传数据Parcel的data指针

      |-- binder_transaction 根据handle找到目标进程的binder实体,执行一次拷贝动作,转换userspace传下来的objects(如 binder/fd等)

            |-- binder_proc_transaction 将binder传输事务 发送给目标进程 并唤醒目标进程(当前线程进入睡眠,直到目标进程处理完成 或出错 返回)

  |-- binder_thread_read 读取返回消息

Binder多线程

用户进程使用binder前会实例化一个ProcessState对象。

ProcessState::self()

  |-- new ProcessState("/dev/binder")

         |-- open_driver

                |-- ioctl(“/dev/binder”, BINDER_SET_MAX_THREADS)

设置默认的最大binder线程个数。

用户也可以自行调用该ioctl来设置最大线程个数。

 

在需要时创建新的binder线程是通过ProcessState完成的。

ProcessState::spawnPooledThread()

|-- ProcessState::makeBinderThreadName()   新线程命名为Binder:%d_%X(%d是进程pid,%X是binder线程序号)

|-- new PoolThread()->run()

      |-- (new thread) IPCThreadState::self()->joinThreadPool(mIsMain)

                                     |-- while busy: getAndExecuteCommand

                                                                          |-- talkWithDriver()

                                                                          |-- executeCommand()     将handle转成BBinder,并调用BBinder->transact()

                                     |-- exit if idle and not main

 

每个binder线程都有一个IPCThreadState对象,用来维护当前线程的一些binder相关的状态信息。如果不是主线程(调用ProcessState::startThreadPool()的线程),那么在处理完所有事务并且读取命令超时后 会自动结束线程。

 

创建线程的触发条件

当启动1个以上binder线程后,binder线程读取并处理binder client发来的消息。

IPCThreadState::getAndExecuteCommand()

     |-- IPCThreadState::talkWithDriver()

            |- ioctl(“/dev/binder”, BINDER_WRITE_READ)

(Driver处理)

binder_ioctl(BINDER_WRITE_READ)

|-- binder_ioctl_write_read

      |-- binder_thread_read  读操作处理完成后,如果发现没有空余线程,并且当前已启动的线程个数少于最大线程数,那么返回给userspace一个 BR_SPAWN_LOOPER命令

(userspace处理)

IPCThreadState::executeCommand()

    |-- BR_SPAWN_LOOPER: ProcessState::spawnPooledThread(false)  创建新的binder线程。

 

实现RPC

在底层binder IPC机制的基础上,binder在用户层整体基于代理模式实现了一套RPC(Remote Procedure Call)框架,来使得用户可以方便的实现RPC操作。

Binder框架

Binder在用户层的框架如上图所示。几个比较关键的类的作用如下:

类名

作用

来源

IBinder

Binder抽象接口

框架原有

BBinder

本地binder对象

框架原有

BpBinder

远端binder代理

框架原有

BnInterface

本地服务的一些公共方法

框架原有

BpInterface

远端服务代理的一些公共方法

框架原有

IINTERFACE

用户自定义的服务接口

自动生成

BnINTERFACE

一个中间层,能够通过类的继承,在调用onTransact()方法中调用用户在IMPLINTERFACE类中实现的服务方法

自动生成

BpINTERFACE

一个代理类,通过binder架构的处理,client端调用此类中的方法,实际上会实现RPC,实际调用到IMPLINTERFACE中的对应方法

自动生成

IMPLINTERFACE

用户实现的服务,即图中sayHello()的具体实现

用户编写

 

AIDL

AIDL全称是Android Interface Definition Language,即Android接口定义语言,是用于定义服务器和客户端通信接口的一种描述语言,可以拿来生成用于RPC的代码。

早期的AIDL主要应用于Java语言,用来协助Android Framework/APP开发者使用binder开发跨进程通信功能。

在Android 9.0版本中,AIDL实现了对C++语言的支持。用户可以编写AIDL文本,在编译期间,由aidl-cpp工具将AIDL源代码翻译成C++代码,自动为用户实现IINTERFACE、BpINTERFACE、BpINTERFACE几个部分的所有源码,大大提高了基于C++语言开发binder应用的效率。

AIDL语法格式与java语言非常相近,更多的信息可以参考AIDL官方介绍文档。

进程间传递binder

按照应用场景,传递binder的操作 可以分为如下几种:

  1. 查找service manager
  2. 向service manager查找service
  3. 传递匿名binder

 

在使用中,根据调用者和binder实体是否在同一进程中,binder在进程中的表示又可分为 本地binder 和 远程(代理)binder两种。

 

下面 以几个常用的场景来分析如何使用binder机制来传递binder。

实例1:普通进程查找service manager

前面有讲到,普通进程查找service manager使用特殊的handle值0来查找。其查找过程中,先是往0号handle的binder发送一条binder命令 PING_TRANSACTION,如果命令被正确执行并返回,那么会在当前进程中根据handle 值0在本地创建一个BpBinder,并保存到当前进程的ProcessState对象中。该被从使用者本地创建出来的BpBinder作为当前进程访问service manager的IBinder指针。

实例二:向service manager查找service

Service manager提供2个相关的API:getService() 和 checkService()。其中 2者的区别在于 在查找不到目标service的情况下,前者会持续查找5s,直到超时 或者 目标service就绪;后者则不会重试,不管目标service是否存在,检查一次立即返回结果。

 

在请求端,进程是通过向service manager发送一条CHECK_SERVICE_TRANSACTION binder消息 并携带目标service的名称实现的。

在service manager中,根据请求端传来的service的名称 查找对应service的handle值,然后将查到的handle值作为一个特殊的binder obj(type: BINDER_TYPE_HANDLE),将目标service的handle返回给请求端(Parcel.cpp:flatten_binder())。

binder驱动收到 BINDER_WRITE_READ 命令来处理binder传送消息,在binder驱动的传输处理结尾,会翻译上层传下来的binder obj,如果传递的binder属于原始请求端(回复消息的接收端)进程,那么把该binder obj的类型修改为BINDER_TYPE_BINDER(binder实体)。

在接收端应用层,Parcel读取该binder obj,并根据binder obj内的handle值本地创建一个BpBinder,用来后续访问该binder(Parcel.cpp:unflatten_binder())。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sanzhong104204

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值