Android Binder-框架简析

来源:互联网 发布:ubuntu如何设置ip地址 编辑:程序博客网 时间:2024/06/08 13:16

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_openb. binder_become_context_managerc. 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_openb. 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_openb. 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了

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

原创粉丝点击