Android Binder-框架简析

Android这个庞大的系统中会涉及非常多的进程间通信,是什么让各个进程间通信起来毫无障碍且有条不紊的呢?传统的IPC(即”进程间的通信”缩写),例如Pipe和Socket,执行一次通信需要两次数据的拷贝,举个例子如,Client要将一块内存数据传递给Server,一般的做法是,Client将这块数据从它的进程空间拷贝到内核空间中,然后内核再将这个数据从内核空间拷贝到Server的进程空间,这样,Server就可以访问这个数据了,但是在这种方法中,执行了两次内存拷贝操作。而采用Binder机制,只需要把Client进程空间的数据拷贝一次到内核空间,然后Server与内核共享这个数据就可以了,整个过程只需要执行一次内存拷贝,提高了效率。Binder的主要核心有两部分,分别是IPC(进程间通信)和RPC(远程过程调用)。

  1. IPC
    进程间的通信,以AB两个进程间通信作为例子,数据传输有三要素:

    1. 源:A进程(要去访问B进程提供的LED驱动接口)
    2. 目的: B进程(进程A是如何得知是进程B提供LED访问呢?):
      B进程向ServiceManager注册led服务
      A进程向ServiceManager查询led服务,得到一个handle(句柄)
    3. 数据传输:使用buffer
  2. RPC
    远程调用(在IPC的基础上做了层封装),例如调用led_open / led_ctl,但它并没有权限,所以需要做如下处理:

    1. 封装(构造)数据
    2. A进程发送数据(通过IPC通道发送给B进程),B进程会取出数据然后调用led_open / led_ctl,就好像A直接操作LED一样

RPC:远程调用涉及的过程:

  1. 调用哪一个函数:根据Server的函数编号(led_ctl、led_open)
  2. 传给给它什么参数:通过IPC的buffer传输(哪个灯,亮还是灭)
  3. 返回值:通过IPC的buffer传输(返回成功与否)

Binder系统涉及4个部分:

1. Client(如上文提及的A进程),程序操作如下:

  1. Open binder驱动(涉及进程间的通信都需要open)
  2. 获取服务
    a) 向servicemanager查询服务
    b) 获得一个handle(句柄)
  3. 向handle发数据

2. ServiceManager(一个特殊的service,告诉A如何找到B),程序操作如下:

  1. Open binder驱动
  2. 告诉binder驱动程序,它是ServiceManager
  3. While循环,读驱动获取数据,解析数据,调用下面两个函数
    a) Server注册服务(在链表中记录服务名)
    b) Client获取服务
    i. 在链表中查询有无服务
    ii. 返回server进程的handle

3. Server(上文提及的B进程),程序操作如下:

  1. Open binder 驱动
  2. 注册服务
    a) 向servicemanager发送服务名
  3. While循环,读驱动,解析数据,调用对应的函数

4. Binder(上面三部分的通信通过binder驱动实现)


下面用系统自带的binder程序来理解整个调用过程,代码是使用C语言来实现的,它在系统文件中的目录如下:

android\frameworks\native\cmds\servicemanager\

调用过程分析只涉及下面三个文件:

service_manager.c
bctest.c(半成品,可以根据它写出我们的Client和Server程序)
binder.c(封装好的C库)

上面三个文件整个调用过程对应的源码简析如下:

**************************************  service_manager.c  **************************************
a. binder_open
b. binder_become_context_manager
c. binder_loop(bs, svcmgr_handler);
   c.1 res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
   c.2 binder_parse
          // 解析
          // 处理  : svcmgr_handler
                       SVC_MGR_GET_SERVICE/SVC_MGR_CHECK_SERVICE : 获取服务
                       SVC_MGR_ADD_SERVICE : 注册服务          
          // 回复
*******************************************  bctest.c  *******************************************
注册服务的过程:

a. binder_open
b. binder_call (bs, &msg, &reply, 0, SVC_MGR_ADD_SERVICE)
// bs: 打开的device的binder fd                   
// msg: 含有服务的名字
// reply: 它会含有servicemanager回复的数据 
// 0: 表示servicemanager
// SVC_MGR_ADD_SERVICE(code): 表示要调用servicemanager中的"addservice函数"



获取服务的过程:

a. binder_open
b. binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE)
// bs: 打开的device的binder fd                   
// msg: 含有服务的名字,表示要获得哪一个名字
// reply: 它会含有servicemanager回复的数据, 表示提供服务的进程
// target :里面是0,表示servicemanager
// SVC_MGR_CHECK_SERVICE(code): 表示要调用servicemanager中的"checkservice函数"
*******************************************  binder.c  *******************************************
binder_call:远程调用
int binder_call(struct binder_state *bs,
                struct binder_io *msg, struct binder_io *reply,
                uint32_t target, uint32_t code)

//target :向谁发数据
//code:调用哪个函数
//msg:提供什么参数
//reply:返回值

binder_call最终会构造一个binder_write_read结构体,然后调用ioctl发送出去

struct binder_write_read {
    binder_size_t       write_size; /* bytes to write */
    binder_size_t       write_consumed; /* bytes consumed by driver */
    binder_uintptr_t    write_buffer;
    binder_size_t       read_size;  /* bytes to read */
    binder_size_t       read_consumed;  /* bytes consumed by driver */
    binder_uintptr_t    read_buffer;
};

Read_buffer里面调用binder_transaction_data(形参里面有code和用户构造的参数),根据code可以决定调用什么函数

如何使用binder进行数据传输:

1.  构造参数:一般放在buf里面,有个结构体叫binder_io来描述

2.  调用ioctl来发数据
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

3.  Ioctl不仅能发数据,也可以收数据,收到一个binder_write_read结构体数据,需要转换为binder_io(即将收到的数据构造成一个binder_io)

怎么写APP:

  1. client
    a) binder_open
    b) 获得服务:handle
    c) 构造参数:binder_io
    d) 调用binder_call函数,形参中包含:handle(发给哪个进程),code(想调用进程的哪个函数),binder_io(发送的参数)
    e) binder_call会返回binder_io,取出返回值
  2. server
    a) binder_open
    b) 注册服务
    c) ioctl(这里会读到client发来的数据,会读到handle,code, binder_io这些参数)
    d) 解析数据得出code和参数
    binder_write_read. read_buffer->binder_transaction_data->code 和 参数,这个参数会转换为binder_io
    e) 根据code,决定调用哪个函数,从binder_io取出参数,这个参数是传给调用的那个函数的
    f) 把返回值再次转换为binder_io发给client,client就可以从binder_call得到这个binder_io了

下一章会具体实现编写代码的过程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值