浅谈Android之Binder原理介绍(二)

2.2 ServiceManager

Binder Kernel提供命令BINDER_SET_CONTEXT_MGR来设置bindercontext manager:

1)  BINDER_SET_CONTEXT_MGR在binder kernel对应一个特殊的binder node,说其特殊,是因为这个bindernode没有binder对象相关数据(它本来就没有),只有设置进程(service manager)的进程相关信息,并且只允许一个进程成为binder context manager

2)  Binder kernel为binder contextmanager对应binder node分配固定的handle为0

 

这样app就可以通过handle:0与service manager建立连接。

Service manager对应的代码目录为:

/system/frameworks/native/cmds/servicemanager

 

Servicemanager直接使用c编写,直接通过使用ioctl和binder kernel通信,代码这里就不贴了,逻辑很简单,这里做下简单介绍:

1)  程序启动后,先会调用binder_open,这个函数主要是打开/dev/binder设备,然后检查binder version,接着mmap映射共享内存

2)  接着调用binder_become_context_manager->ioctl(bs->fd,BINDER_SET_CONTEXT_MGR, 0)设置为bindercontext manager

3)  最后调用binder_loop(bs, svcmgr_handler)-> ioctl(bs->fd,BINDER_WRITE_READ, &bwr)等待数据进来;svcmgr_handler是一个回调函数地址,它负责transcation data数据的解析。

 

Service manager定义了四个transaction code:

enum {

   /* Must match definitions in IBinder.h and IServiceManager.h */

   PING_TRANSACTION  =B_PACK_CHARS('_','P','N','G'),

   SVC_MGR_GET_SERVICE = 1,

   SVC_MGR_CHECK_SERVICE,

   SVC_MGR_ADD_SERVICE,

   SVC_MGR_LIST_SERVICES,

};

 

每一个被添加到servicemanager的service(native binder)都要有对应名字,这样,后续就可以通过这个名字来找到这个service对应的handle了。

 

2.3 基础命令封装

如果要开发一个binder进程间通信程序,我们肯定希望越简单越好,这样我们就可以把更多精力放在业务层面,所以,我们必须要将上头很多ioctl命令进行封装,提供更加简洁易懂的接口给应用开发人员使用。

 

Android提供ProcessState和IPCThreadState两个类,ProcessState封装一些作用域在整个进程的命令,而IPCThreadState则封装的命令都是作用在单个线程的。

 

下面看下它们都封装了什么:

类名

函数名

介绍

ProcessState

单例,进程只存在唯一的一个实例,通过静态函数self完成初始化

ProcessState()

构造函数,先调用openDrive打开/dev/binder设备,接着调用mmap

setThreadPoolMaxThreadCount

设置binder线程池最大线程数,对应封装

BINDER_SET_MAX_THREADS

命令

getStrongProxyForHandle(int32_t handle)

传入binder handle,返回基于handle创建的BpBinder对象,同时将已经创建的BpBinder添加到缓存

getContextObject(const sp<IBinder> &)

通过调用

getStrongProxyForHandle(0);

获取ServiceManager对应的BpBinder

startThreadPool

如果未设置binder main looper thread,则创建一条线程并设置为main looper thread

IPCThreadState

同样的,也是通过静态self创建的实例,不同的是,创建的实例都是保存到TLS中的,这就保证了,每个线程只会有唯一的IPCThreadState对象实例

IPCThreadState()

构造函数,主要是将对象自身存入到TLS中,同时初始化Parcel mIn和Parcel mOut用于作为接收和发送数据缓存

joinThreadPool

将当前调用线程注册到binder线程池,并阻塞当前线程

incStrongHandle(int32_t handle)

mOut.writeInt32(BC_ACQUIRE);

talkWithDriver(Boolean receive)

封装BINDER_WRITE_READ命令,如果receive为true,说明有返回数据,mOut和mIn两个缓存数据都要读取,否则,只需要读取mOut缓存数据即可。接着发送到binder kernel。

flushCommands

调用talkWithDriver(false)立即发送mOut缓存中的命令数据

waitForResponse

调用talkwithdriver(true),发送命令数据,并等待返回结果并依次处理

transact(int32_t handle, uint32_t                                 code, const Parcel& data, Parcel*                               reply, uint32_t flags)

发送BC_TRANSACTION命令,如果flags未指定TF_ONE_WAY,则调用waitForResponse等待数据返回

executeCommand

处理返回数据

包括BC_TRANSACTION

 

ProcessState封装的都是作用在整个进程的一些功能,比如打开并保存设备,mmap,缓存已经创建的handle对应的BpBinder,获取servicemanager,设置binder线程池大小等等

 

IPCThreadState则封装了binder命令数据发送和接收,由于命令以及关联数据在用户空间和内核空间都会被访问,这就需要在两个空间保持数据序列化规则统一以及做好数据同步,具体的实现是这样的:

1:用户空间使用Parcel序列化数据,内核空间同样使用Parcel规则操作数据

2:由于ioctl是阻塞操作,所以,如果要保持数据同步,最简单的方法,就是开辟两份内存用于保存发送和接收命令数据并保存到TLS(线程本地存储)中即可

 

这就是IPCThreadState被创建后保存到TLS的原因,同时,它内部保存有两个Parcel类型的

私有变量mOut和mIn,分别用于缓存发送和接收命令以及关联数据。

 

ProcessState和IPCThreadState怎么用?看代码:

int main(int argc, char** argv)

{

#if defined(HAVE_PTHREADS)

    setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);

#endif

 

    char value[PROPERTY_VALUE_MAX];

    property_get("debug.sf.nobootanimation", value, "0");

    int noBootAnimation = atoi(value);

    ALOGI_IF(noBootAnimation,  "boot animation disabled");

    if (!noBootAnimation) {

 

        sp<ProcessState> proc(ProcessState::self());

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

 

        // create the boot animation object

        sp<BootAnimation> boot = new BootAnimation();

 

        IPCThreadState::self()->joinThreadPool();

 

    }

    return 0;

}

 

其实核心的就三行代码,ProcessState::self()完成后,进程binder环境就已经初始化完成,接下去要做的就是创建binder主线程并注册到binder kernel:

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

 

其实从binder命令的接收角度,到这里就够了,因为后续线程不够用时,binder kernel会

动发命令创建新线程添加到binder 线程池

 

后续继续调用IPCThreadState::self()->joinThreadPool();将主线程加入binder线程池,主要是为

了阻塞主线程,防止main函数执行结束,进程退出。

 

Binder环境已经创建ok,接下去就可以创建自己的nativebinder添加到servicemanager中或者从servicemanager拿到目标nativebinder的handle,就可以建立数据通信了。

 

Binder数据传输是通过BC_TRANSACTION和BR_TRANSACTION两个命令来发送和接收,不同的transaction通过code来区分,这样在实际开发中,我们可能就会定义很多的code来区分各种各样的transaction。

 

这样明显不方便,需要做进一步的封装。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android Binder是在Android系统中用于进程间通信(IPC)的一种机制。Binder提供了一种高效的、低延迟的IPC方式,使得不同进程之间可以安全、可靠地进行通信。 下面是Android Binder的详细原理: 1. Binder驱动 Android Binder的底层是由Binder驱动实现的,它是一个在内核空间运行的模块,用于处理进程间通信。Binder驱动提供了一些API给用户空间的Binder库使用,这些API包括创建Binder对象、发送消息、接收消息等。 2. Binder对象 在Android系统中,每个进程都有一个唯一的Binder对象。Binder对象是由Binder驱动动态创建的,它包含了一个通信线程(Binder线程),用于处理当前进程与其他进程之间的通信。当进程需要与另一个进程通信时,它会向Binder驱动发送请求,请求创建一个新的Binder对象。 3. Binder代理和存根 当进程A需要与进程B通信时,它会向Binder驱动发送请求,请求创建一个新的Binder对象。Binder驱动会在进程B中创建一个Binder代理对象和一个Binder存根对象。 Binder代理对象是在进程A中创建的,它是一个轻量级的对象,用于向进程B发送请求。当进程A需要向进程B发送请求时,它会通过Binder代理对象向Binder驱动发送请求,请求将消息发送给进程B。 Binder存根对象是在进程B中创建的,它是一个重量级的对象,用于接收进程A发送的请求,并将请求转发给进程B中的相应组件。当进程B接收到来自进程A的请求时,它会通过Binder存根对象处理请求,并将结果返回给Binder驱动,最终返回给进程A。 4. 远程代理 Android Binder还提供了一种远程代理机制,它可以将一个对象在不同的进程之间传递。当进程A需要访问进程B中的某个对象时,它会先向Binder驱动发送请求,请求获取该对象的远程代理。Binder驱动会在进程A中创建一个轻量级的远程代理对象,用于向进程B发送请求,并将请求转发给进程B中的相应组件。当进程B接收到来自进程A的请求时,它会通过Binder存根对象处理请求,并将结果返回给Binder驱动,最终返回给进程A中的远程代理对象。 以上就是Android Binder的详细原理Android Binder是一个非常重要的组件,它为Android系统中的进程间通信提供了高效、可靠的解决方案。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值