转载

我也来聊聊 Binder

我们知道,同一个程序中的两个函数之间能直接调用的根本原因是处于相同的内存空间中(虚拟地址的映射规则完全一致);反之,两个不同的进程,比如微信App和淘宝App所在的进程,它们是没用办法直接通过内存地址来访问到对方内部的函数或者变量的。 既然无法直接访问到对方进程的内存空间,那有没有间接的方法呢?自然是有的,比如接下来要讲的Binder。

Binder 是 Android 中使用最广泛的 IPC 机制。

整个Binder机制涉及的东西既多又杂,划分下来可以包括

  • Binder 驱动 -> 路由器
  • Service Manager -> DNS
  • Binder Client ->  客户端
  • Binder Server -> 服务端

Binder 的本质目标用一句话来描述,就是进程1(客户端)希望与进程2(服务器)进行互访。但因为它们之间是跨进程(跨网络)的,所以必须借助于 Binder 驱动(路由器)来把请求正确投递到对方所在进程(网络)中。而参与通信的进程们则需持有 Binder 颁发的唯一标志(IP地址)。

数据传递的载体 Parcel

既然 Binder 是用来处理进程间通信的,所谓通信本质是数据的交互。那进程间是如何传递数据的呢?聪明的你肯定猜到了,没错就是 Parcel。我们先用一个例子简单理解一下 Parcel 的概念:

你去逛商场,有件衣服 (商品A) 你特别喜欢,但是由于种种原因没有购买;可是你实在是想要啊,于是你打开了某宝,下单付款一气呵成。几天后,你收到了包裹,终于得到了梦寐以求的衣服,开心的像个孩子。

整个过程中,你拿到了你想要的衣服,但是那个衣服是你当初在商场看到的同一件么? 不见得吧,但是可以肯定的是,你拿到的和商场看到的是一模一样的。整个过程中,你只是手指轻轻点了下下单操作,隐藏在背后的工作人员帮你实现了分检、打包、物流、运输,最后将商品完完整整送到了你的手中。如果把你在商店看到的那件衣服视做 IPC 中的原始数据的话,下单的过程则是你将原始数据的意图发送了出去,接着幕后工作人员们就承当了 Parcel 的角色,根据你的意图,对你发送的数据进行必要的包装,这时候打包进去后的则可能商品 A 的兄弟姐妹们,假设为商品 B,然后经过一层层的运输交到了你的手上。由于商品 A 和商品 B 看上去和用起来都是一模一样的,所以对你来说 商品 A 和 B 没有什么区别的。

回到正题,如果进程间传递的数据只是 int 等基础类型,只需不断复制直到目标进程即可。可是,如果是个对象呢?同一个进程内的对象传递基本都是通过引用来做的,本质上是传递了一个内存地址,由于采用了虚拟的内存机制,每个进程都有自己独立的内存地址空间,跨进程传递的地址值是无效的,那么这种简单的通过复制实现跨进程是行不通的。

既然无法直接复制,那能否通过某种规则,把对象在进程 A 中占据的内存相关数据打包起来,然后寄送到进程 B 中,由 B 在自己的进程空间中复现这个对象,是否可行? Parcel 就具备这种打包和重组的能力 。既然涉及到了打包和重组,那它肯定会依据协议来保证重组后的数据能完整的还原打包前的数据。这有点类似加密与解密的过程,打包与重组使用的数据必须是配套的。

Parcel 操作对象与内容包括

  1. 原始数据的读写操作

  2. 原始数据类型数组的读写操作

  3. Parcelables 遵循 Parcelable 协议的对象可以通过 Parcel 来存储 。与这类对象相关的操作包括

    method 描述
    writeParcelable(Parcelable,int) 将 Parcelable 类的名字和内容写入 Parcel 中
    readParcelable(ClassLoader) 读取并且返回一个新的 Parcelable 对象
    writeParcelableArray(T[],int) 写入 Parcelable 对象数组
    readParcelableArray(ClassLoader) 读取并返回一个 Parcelable 对象数组
  4. Bundle Bundle 继承自 Parcelable,是一种特殊的 type-safe 的容器

  5. Active Object 通常我们存入 Parcel 的是对象的内容,而 Active Object 可就厉害了,写入的是它们的特殊标志引用,所以从 Parcel 中读取这些对象时,大家看到的并不是重新创建的对象,而是原来那个被写入的实例。 Android中采用这种方式传递的对象主要有2种:

    1. Binder。Binder 一方面是 Android IPC 通信的核心机制之一,另一方面也是一个对象。利用 Binder 将 Binder 对象写入,读取的时候就能得到原始的 Binder 对象,或者是它的特殊代理对象(最终操作的还是原始 Binder 对象)
    2. FileDescriptor。Linux 的文件描述符,通过 Parcel 传递后的对象仍然会基于和原始对象相同的文件流进行操作,因而可以认为是 Active Object 的一种。
  6. Untyped Containers

    用于读写标准的任意类型的 java 容器

Binder 驱动与协议

我们知道,Android 系统是基于 linux 内核的,因而它所依赖的 Binder 驱动也必须是一个标准的 Linux 驱动。具体而言,Binder Driver 会将自己注册成一个虚拟的 misc device,并向上层提供一个/dev/binder 节点。 Binder Driver 位于内核态,可以提供 open(), ioctl() , mmap() 等常用的文件操作。

binder_open

用于打开 binder 驱动(打开 dev/binder 节点),为用户创建一个它自己的 binder_proc 实体,完成对这个新生成的 proc 对象各种初始化并将它加入到 Binder 的全局管理中

/* 在9.0源码中,binder 驱动已经合入 linux 主分支https://github.com/torvalds/linux/blob/master/drivers/android/binder.c  */

static int binder_open(struct inode *nodp, struct file *filp)
{
	struct binder_proc *proc;
	struct binder_device *binder_dev;

  ...
	proc = kzalloc(sizeof(*proc), GFP_KERNEL); /*分配空间*/
	if (proc == NULL)
		return -ENOMEM;
	spin_lock_init(&proc->inner_lock);       /* 宏 spin_lock_init()初始化自旋锁lock */
	spin_lock_init(&proc->outer_lock);
	get_task_struct(current->group_leader);
	proc->tsk = current->group_leader;
	INIT_LIST_HEAD(&proc->todo);             /* todo 链表*/
	proc->default_priority = task_nice(current);
	/* binderfs stashes devices in i_private */
	if (is_binderfs_device(nodp))
		binder_dev = nodp->i_private;
	else
		binder_dev = container_of(filp->private_data,
					  struct binder_device, miscdev);
	proc->context = &binder_dev->context;
	binder_alloc_init(&proc->alloc);

	binder_stats_created(BINDER_STAT_PROC);
	proc->pid = current->group_leader->pid;
	INIT_LIST_HEAD(&proc->delivered_death);
	INIT_LIST_HEAD(&proc->waiting_threads);  /* wait 链表*/
	filp->private_data = proc;               /*  proc 与 flip 关联 */

	mutex_lock(&binder_procs_lock);          /*  mutex 互斥锁 */
	hlist_add_head(&proc->proc_node, &binder_procs);  /* 加入到 Binder 的全局管理  */
	mutex_unlock(&binder_procs_lock);

  ...

	return 0;
}

复制代码

binder_mmap

mmap() 是操作系统中一种内存映射的方法。内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间。

mmap() 通常是用在有物理介质的文件系统上的。但 Binder 本身并不是一个硬件设备,而只是基于内存的“伪硬件”, 因此 Binder 驱动使用 mmap() 并不是为了在物理介质和用户空间之间建立映射,而是用来在内核空间创建数据接收的缓存空间。

一次完整的 Binder IPC 通信过程通常是这样:

  1. 首先 Binder 驱动在内核空间创建一个数据接收缓存区;
  2. 接着在内核空间开辟一块内核缓存区,建立 内核缓存区内核中数据接收缓存区 之间的映射关系,以及 内核中数据接收缓存区接收进程用户空间地址 的映射关系;
  3. 发送方进程通过系统调用 copy from user() 将数据 copy 到内核中的 内核缓存区 ,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。
我也来聊聊 Binder

假如有两个进程 A 和 B,其中 B 通过 open()和 mmap()后与 Binder 驱动建立了联系

  • 对于应用程序而已,它通过 mmap()返回值得到一个内存地址(当然这是虚拟地址),这个地址通过虚拟内存转换后最终指向物理内存的某个位置
  • 对于 Binder 驱动而言,它也有一个指针(binder_proc->buffer) 指向某个虚拟内存地址。而经过虚拟内存转换后,它和应用程序中指向的物理内存处于同一个位置

这时 Binder 和应用程序就相当于拥有了若干物理内存块。它们对各自内存地址的操作,实际上就是在同一块内存中执行的。当进程 A 希望与进程 B 通信时,进程 A 先通过 Binder 驱动调用 copy_from_user() 将用户空间数据复制到 binder_proc->buffer 所指向的内存空间,因为 binder_proc->buffer 在物理内存中的位置进程 B 是共享的,因为进程 B 可以直接访问这段数据。

Binder 驱动通过 copy_from_user(), 把进程 A 中的某段数据复制到 binder_proc->buffer 所指向的内存空间中。因为 binder_proc->buffer 在物理内存中的位置和进程 B 是共享的,因而进程 B 能够直接访问到这段数据。于是,Binder 驱动只用了一次复制,就实现了进程 A 和进程 B 间的数据共享。

在实际操作之前,Binder 驱动会判定应用程序申请的内存大小是否合理--它最多只支持 4M 的空间的 mmap 操作。当应用程序申请内存大小超过 4M 时,并没有直接退出,而是只满足用户刚好 4M 的请求:

static int binder_mmap(struct file *filp, struct vm_area_struct *vma) { int ret; struct vm_struct *area; struct binder_proc *proc = filp->private_data; const char *failure_string; struct binder_buffer *buffer; //限制不能超过4M if ((vma->vm_end - vma->vm_start) > SZ_4M) vma->vm_end = vma->vm_start + SZ_4M; ... }

那理论上通过 Intent 传递数据大小的限制是否也是 4M 呢?内核上 binder 限定确实是 4M ,但是普通的由 Zygote 孵化而来的用户进程所映射的 Binder 内存大小已经被限定在了1M左右,这个限制定义 processState 这个类中

/* frameworks/native/libs/binder/ProcessState.cpp */

define BINDER_VM_SIZE ((1 1024 1024) - (4096 *2))

故而,对于超过 BINDER_VM_SIZE 的内存申请请求,还没到 binder drive ,系统就已经抛出异常了。

DNS 服务器--ServiceManager [Binder 服务端]

ServiceManager 的启动

ServiceManager (后面统一称 SM )的启动是在 init 程序解析 init.rc 时候启动的。init 进程是 Android 中第一个被启动的进程,init 的 PID 是0。注意,这里的 SM 是指的 native 层的 ServiceManager,源码路径在 /frameworks/native/cmds/servicemanager 。

ServiceManager 的构建

/* /frameworks/native/cmds/servicemanager/servive_manager.c */
int main(int argc, char** argv)
{
    struct binder_state *bs;
    union selinux_callback cb;
    char *driver;

    if (argc > 1) {
        driver = argv[1];
    } else {
        driver = "/dev/binder";
    }
    // 打开 Binder 设备,内部执行 open 、mmap
    bs = binder_open(driver, 128*1024);
    
    ...
    // 将 servicesmanager 注册成 Binder 机制大管家
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)/n", strerror(errno));
        return -1;
    }
    ...
    // 进入循环,等待客户请求
    binder_loop(bs, svcmgr_handler);

    return 0;
}

  
复制代码
/* framework/native/cmds/servicemanager/binder.c */
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
    struct binder_state *bs;   // binder_state 结构体记录了 SM 中有关 Binder 的所有信息
    struct binder_version vers;

    bs = malloc(sizeof(*bs));
    if (!bs) {
        errno = ENOMEM;
        return NULL;
    }

    bs->fd = open(driver, O_RDWR | O_CLOEXEC);   //调用驱动 open 函数,打开 Binder 驱动节点
    if (bs->fd < 0) {
        fprintf(stderr,"binder: cannot open %s (%s)/n",
                driver, strerror(errno));
        goto fail_open;
    }

    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
        (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
        fprintf(stderr,
                "binder: kernel driver version (%d) differs from user space version (%d)/n",
                vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);
        goto fail_open;
    }

    bs->mapsize = mapsize;    // 映射大小 是由 SM 设置的 ,大小为 128k
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    if (bs->mapped == MAP_FAILED) {
        fprintf(stderr,"binder: cannot map device (%s)/n",
                strerror(errno));
        goto fail_map;
    }

    return bs;

fail_map:
    close(bs->fd);
fail_open:
    free(bs);
    return NULL;
}
复制代码

binder_become_context_manager会向 Binder 驱动发送 BINDER_SET_CONTEXT_MGR 命令

/* /frameworks/native/cmds/servicemanager/binder.c */
int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
复制代码

binder_loop不停循环,从 Binder 驱动获取数据,正常情况下就像是一个永不退出的消息队列

void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;  //  执行 BINDER_WRITE_READ 命令所需的数据结构
    uint32_t readbuf[32];

    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;

    readbuf[0] = BC_ENTER_LOOPER;        // BC_ENTER_LOOPER 命令
    binder_write(bs, readbuf, sizeof(uint32_t));  

    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;
        
        /*  BINDER_WRITE_READ 既可读也可写,根据 bwr.write_size 和 bwr.read_size 来确定具体的执行逻辑  */
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);   // 读取消息
        ...
        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);  //解析消息
        ...
    }
}
复制代码

binder_parse

int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uintptr_t ptr, size_t size, binder_handler func)
{
    ...
    while (ptr < end) {
        uint32_t cmd = *(uint32_t *) ptr;
        ptr += sizeof(uint32_t);
#if TRACE
        fprintf(stderr,"%s:/n", cmd_name(cmd));
#endif
        switch(cmd) {   // 这里 cmd 对应 svcmgr_handler
         ...
       // BR_TRANSACTION 这个命令在 整个 Binder 中会频繁用到 
       case BR_TRANSACTION: {
            struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
            if ((end - ptr) < sizeof(*txn)) {
                ALOGE("parse: txn too small!/n");
                return -1;
            }
            binder_dump_txn(txn);
            if (func) {
                unsigned rdata[256/4];
                struct binder_io msg;
                struct binder_io reply;
                int res;

                bio_init(&reply, rdata, sizeof(rdata), 4);
                bio_init_from_txn(&msg, txn);
                res = func(bs, txn, &msg, &reply);   //主要干活的函数 具体处理消息
                if (txn->flags & TF_ONE_WAY) {  /* TF_ONE_WAY代表是单向通信,不需要回复 */
                    binder_free_buffer(bs, txn->data.p tr.buffer);
                } else {
                    binder_send_reply(bs, &reply, txn->data.ptr.buffer, res); /* 回应处理结果*/
                }
            }
            ptr += sizeof(*txn);
            break;
        }
        ...
     }

    return r;
}       
复制代码

BR_TRANSACTION 命令主要由 func 来完成,然后将结果返回给 Binder 驱动。而 ServiceManager 是为了完成 *“域名” ”IP地址“*间的对应关系的,所以我们可以推测它提供的服务至少包括 注册查询 等功能。

对于注册、查询等具体实现这里就不分析了。

这里涉及到 binder_io 类型的 msg 和 reply 对象,想象一下 Parcel 中的 mIn、 mOut, 你能联想到什么?

获取 ServiceManager 服务

如果要获取 SM 服务,流程应该是怎么样的?

  • 打开 Binder 设备
  • 执行 mmap
  • 通过 Binder 驱动向 SM 发送请求
  • 获得结果

核心工作就只有这些。不过有些具体细节需要再商榷

  • 向 SM 发送请求的 Binder Client 可能是 Android 的 APK 应用程序,所以 SM 必须提供 Java 层接口
  • 如果每个 Binder Client 都要亲力亲为的执行上诉4个步骤来获取 SM 服务,可想而知,必然会浪费不少时间
  • 如果应用程序每次使用 SM 都要打开一次 Binder 驱动、执行 mmap,必然会消耗越来越多的系统资源、直到崩溃。有个解决办法是每个设备只允许打开一次 binder 驱动,只做一次内存映射,让所有需要使用 Binder 驱动的线程共享这一资源

问题转换为 如何设计出一个符合上述要求的 Binder Client?

  • ProcessState

ProcessState 顾名思义,肯定与进程状态相关。实际上也是如此,它是用来管理每个应用进程Binder操作的

  • IPCThreadState

ProcessState 用于管理进程 Binder ,进程中还可能会有很多线程,进程中的每一个线程都应该有与 Binder 驱动沟通的权利,那么仅仅只有 ProcessState 就有点力不从心了。

与 Binder 驱动进行实际命令通信的就是 IPCThreadStat

  • Proxy

    有了上面两个类,应用程序就可以与 Binder 驱动通信类,应用程序可以发送 BINDER_WRITE_READ 等 Binder 支持的指令来与其交互。那为啥还要代理呢?

    代理简单来说是为了让调用方使用起来方便而进行的一层层封装,在 binder 中真正干活的是在驱动层,而使用方则可能是 app 层、java framework层、c++ framework层、native 层、HAL 层中的任意一个程序;为了让客户端使用起来尽可能简洁,binder 经历了层层代理;以至于最终我们通过 startActivity()、bindService()等方式调用到 binder 服务时候根本无感知了。

    整个代理过程涉及到类较多,也有很多拗口的新名词出现,但是不管怎么绕,它们最终的目的只有一个,那就是将要请求传递到 IPCThreadState 让其与 Binder 驱动交换并且拿到请求结果。

ServiceManagerProxy

首先,我们需要有 native 层的 ServiceManager 的代理,通过这个代理我们可以轻松获取 SM 服务

ServiceManagerProxy sm=new ServiceManagerProxy(new BpBinder(HANDLE));
IBinder wms_binder= sm.getService("window");

这样 应用程序仅需两步就能获取到 ServiceManager 提供的服务。当然,上面并不是源码真正实现方式,但是并不影响我们理解它;在 android 中对上面的代码做了进一步的封装。这里我们先从设计思路来一步步拆解binder设计思想,以免一下子陌生名称太多导致大脑“类爆炸”,cpu运转不过来

ServiceManagerProxy 能提供的服务和服务端的 SM 必须是要一致的,我们可以把这些方法抽成接口的形式;

/* framework/base/core/java/android/os/ServiceManagerNative */
class ServiceManagerProxy implements IServiceManager {
复制代码

这个接口就是 IServiceManager ,IServiceManager 则继承自 IInterface 接口

IInterface 这个接口只定义了一个函数声明 asBinder(),它会返回一个 IBinder 对象

public interface IInterface
{
    /**
     * Retrieve the Binder object associated with this interface.
     * You must use this instead of a plain cast, so that proxy objects
     * can return the correct result.
     */
    public IBinder asBinder();
}
复制代码

既然 SM 是 Binder 的大管家,那它自然会提高获取和添加 binder 服务的功能,作为 SM 的经理人,ServiceManagerProxy 必然也需要提供这些功能。前面也说过,这些功能是以 IServiceManager 接口的形式定义的。我们来看看具体实现

/* framework/base/core/java/android/os/ServiceManagerNative */
public final class ServiceManagerNative {
    private ServiceManagerNative() {}

    /**
     * Cast a Binder object into a service manager interface, generating
     * a proxy if needed.
     */
    @UnsupportedAppUsage
    static public IServiceManager asInterface(IBinder obj)
    {
        if (obj == null) {
            return null;
        }
        IServiceManager in =
                (IServiceManager) obj.queryLocalInterface(IServiceManager.descriptor);
        if (in != null) {
            return in;
        }

        return new ServiceManagerProxy(obj);
    }
}

class ServiceManagerProxy implements IServiceManager {
    public ServiceManagerProxy(IBinder remote) {
        mRemote = remote;
    }

    public IBinder asBinder() {
        return mRemote;
    }

    @UnsupportedAppUsage
    public IBinder getService(String name) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IServiceManager.descriptor);
        data.writeString(name);
        mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
        IBinder binder = reply.readStrongBinder();
        reply.recycle();
        data.recycle();
        return binder;
    }

    ...
  
    @UnsupportedAppUsage
    private IBinder mRemote;
}
复制代码

getService 要取得 ServiceMananger 这个服务,想必得与 Binder 建立关系, 通过mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0); 利用 IBinder 的 transact 将 Parcel 包装后数据发送出去,而不用去理会 Binder 驱动的 open 、 mmap 、ioctl 等一大堆具体的协议命令,就可以获得想要的结果。所以我们可以大胆猜测,这个 IBinder 一定会在内部调用 ProcessState 和 IPCThreadState 来与 Binder 驱动通信。

transact 是耗时操作,但是上面再执行完 transact 后就立马返回了目标 IBinder 对象,所以 Binder 驱动一定要先将调用者线程挂起,直到有了结果再将它唤醒。

GET_SERVICE_TRANSACTION 这个业务码在 IBinder 中定义的值是 1

int GET_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
int FIRST_CALL_TRANSACTION  = 0x00000001;
复制代码

它与 SM 中关联的业务是 SVC_MGR_GET_SERVICE

/* framework/native/cmds/servicemanager/binder.h */
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,
};
复制代码

mRemote.transact 的流程稍后分析,这里又出现了 IBinder 这个新对象,有必要先对它说明一下

IBinder

为了让应用程序简单便利的使用 SM 服务,我们见识到了 ServiceManagerProxy ,ServiceManagerProxy 又是通过 ServiceManagerNative.asInterface(IBinder obj) 方法来创建的,创建的时候保证了 ServiceManagerProxy 的唯一性。

ServiceManagerNative.asInterface 则是被 ServiceManager的 getIServiceManager() 调用唤起的。而ServiceManager.java 这个类提供的全是 static 的方法,所以 ServiceManager.java 这个类则是对ServiceManagerProxy 使用进一步封装。这样应用程序想要使用 SM 提供的服务,只需要通过调用 ServiceManager.getService(HANDLE) 即可。在这样层层封装之下,ServiceManagerNative、ServiceManagerProxy、 ProcessState 、IPCThreadState 、IServiceManager 、BpBinder 、BBinder 等许多让人头大的对象都对应用程序隐藏了。

/* framework/base/core/java/android/os/ServiceManager.java  */
/** @hide */
public final class ServiceManager {


    /**
     * Place a new @a service called @a name into the service
     * manager.
     *
     * @param name the name of the new service
     * @param service the service object
     * @param allowIsolated set to true to allow isolated sandboxed processes
     * @param dumpPriority supported dump priority levels as a bitmask
     * to access this service
     */
    @UnsupportedAppUsage
    public static void addService(String name, IBinder service, boolean allowIsolated,
            int dumpPriority) {
        try {
            getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
        } catch (RemoteException e) {
            Log.e(TAG, "error in addService", e);
        }
    }
    
    
    public static IBinder getService(String name) {
        try {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return Binder.allowBlocking(rawGetService(name));
            }
        } catch (RemoteException e) {
            Log.e(TAG, "error in getService", e);
        }
        return null;
    }

    @UnsupportedAppUsage
    private static IServiceManager getIServiceManager() {
        if (sServiceManager != null) {
            return sServiceManager;
        }

        // Find the service manager
        sServiceManager = ServiceManagerNative
                .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
        return sServiceManager;
    }
    
 }    
复制代码

通过 getService() 会得到 IBinder 对象。Binder 提供的功能可以统一在 IBinder 接口中表示。此外,通过getIServiceManager() 获取 ServiceManagerProxy 时候提供了一个 BinderInternal.getContextObject()。

/**
 * Return the global "context object" of the system.  This is usually
 * an implementation of IServiceManager, which you can use to find
 * other services.
 */
@UnsupportedAppUsage
public static final native IBinder getContextObject();
复制代码

这是个 native 函数,返回的也是一个 IBinder 对象,它的最终实现是通过 jni 调用本地代码实现的

/* framework/base/core/jni/android_util_Binder.cpp */
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
    sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
    return javaObjectForIBinder(env, b);
}
复制代码

前面引出的 ProcessState 在这里终于出现了。这里也有个 IBinder,对应native 层的 Binder 接口。它肯定与java 层的 IBinder 功能是对应的。native 层 IBinder 对应的实现是 BpBinder,java 层 IBinder 对应的实现是 BinderProxy。其中 BinderProxy 是由 ProcessState 创建的,BpBinder 是由 javaObjectForIBinder() 函数创建的。

那么 mRemote.transact 的调用流程 就清晰了

mRemote.transact -> IBinder.transact -> BinderProxy.transact ->BpBinder.transact

/* framework/native/libs/binder/Bpbinder.cpp */
status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;

        if (reply != nullptr) {
            reply->setTransactingBinder(this);
        }

        return status;
    }

    return DEAD_OBJECT;
}
复制代码

可见 确实是通过 ProcessState 和 IPCThreadState 来真正和 Binder 深入交流的

ProcessState

ProcessState 要实现的功能包括

  • 保证同一个进程只有一个 ProcessState 实例

  • 只在 ProcessState 对象创建的时候才打开 Binder 设备以及内存映射

  • 提供 IPC 服务给上层

    /* framework/native/libs/binder/ProcessState.cpp */
    sp<ProcessState> ProcessState::self()
    {
        Mutex::Autolock _l(gProcessMutex);
        if (gProcess != nullptr) {
            return gProcess;
        }
        gProcess = new ProcessState(kDefaultDriver);
        return gProcess;
    }
    复制代码

    ProcessState::self() 保证了 ProcessState 实例的唯一性,并且仅仅在 ProcessState 实例生成时才会打开 Binder 设备以及内存映射,与 binder 交互的处理是在 ProcessState 构造函数中

    /* framework/native/libs/binder/ProcessState.cpp */
    ProcessState::ProcessState(const char *driver)
        : mDriverName(String8(driver))
        , mDriverFD(open_driver(driver))
        , mVMStart(MAP_FAILED)
        , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
        , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
        , mExecutingThreadsCount(0)
        , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
        , mStarvationStartTimeMs(0)
        , mBinderContextCheckFunc(nullptr)
        , mBinderContextUserData(nullptr)
        , mThreadPoolStarted(false)
        , mThreadPoolSeq(1)
        , mCallRestriction(CallRestriction::NONE)
    {
        if (mDriverFD >= 0) {
            // mmap the binder, providing a chunk of virtual address space to receive transactions.
            mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
            ...
        }
    }
    复制代码

​ mDriverFD(open_driver(driver)) 会打开 binder 节点,拿到 binder 驱动对应的文件描述符 ,之后开始执行内存映射,映射的内存块大小为 BINDER_VM_SIZE ,即之前在 binder_mmap 中提到的约等于1M的空间 。

BpBinder

ProcessState 有了,接着便会调用它的 getContextObject() 方法返回一个 BpBinder 对象

/* framework/native/libs/binder/ProcessState.cpp */
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
    return getStrongProxyForHandle(0);
}

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;
    ...
    handle_entry* e = lookupHandleLocked(handle);

    if (e != nullptr) {
        IBinder* b = e->binder;
        if (b == nullptr || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, nullptr, 0);
                if (status == DEAD_OBJECT)
                   return nullptr;
            }

            b = BpBinder::create(handle);
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } 
        ...
    }

    return result;
}

复制代码

ProcessState 中有一个 handle_entry 类型的全局列表来记录所有与 Binder 对象相关的信息, 这里的 e->binder 指向的则是 BpBinder。与此同时,IPCThreadState 终于出现在了我们视野中,而且它还是与transact 这个我们重点关注的方法有关。

总结来说就是 ProcessState 只是负责打开了 Binder 节点并做 mmap,IPCThreadState 负责与 Binder 驱动进行具体的交互命令

IPCThreadState

/* framework/native/libs/binder/IPCThreadState.cpp */
status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    status_t err;

    flags |= TF_ACCEPT_FDS;
    ...
    err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr);

    if (err != NO_ERROR) {
        if (reply) reply->setError(err);
        return (mLastError = err);
    }

    if ((flags & TF_ONE_WAY) == 0) {
        ...
        #endif
        if (reply) {
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
    } else {
        err = waitForResponse(nullptr, nullptr);
    }

    return err;
}
复制代码

writeTransactionData() 会将数据整理,打包成 Binder 驱动协议规定的格式(具体来讲对应于 binder_transaction_data 这个结构体),并且将结果存入 mOut 中,再由 waitForResponse() 将数据发送出去并处理回馈信息。

/* framework/native/libs/binder/IPCThreadState.cpp */
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    uint32_t cmd;
    int32_t err;

    while (1) {
        /* 通过talkWithDriver处理与 Binder之间的交互命令 */
        if ((err=talkWithDriver()) < NO_ERROR) break; 
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;
        ...
    }
    ...
    return err;
}
复制代码

talkWithDriver() 这个函数负责真正与 Binder 驱动打交道,它将数据进行必要的包装后,发送给 Binder 驱动(以 ioctl 的形式执行),当程序执行到 mIn.errorCheck() 时,说明已经收到了 Binder 驱动到回复,接下来则是对回复的数据进行处理操作。在目标进程未给予回复时,在 Binder 内核调度上会先将发起请求的进程挂起,直到目标进程返回结果后,Binder 驱动再唤醒等待的线程。这一块的逻辑都是在 binder 驱动中定义的,由于高版本 binder 内核代码已经合并至 linux 内核,感兴趣的可以自行去阅读,这里就不再细聊了。

最后我们再简单回顾下 java 层的 ServiceManager 执行 getService() 对大概流程,简单的梳理下整个 binder 机制中从 java 到驱动的调用关系吧

我也来聊聊 Binder

ServiceManager(java层) 执行 getService() 首先要拿到 native 层 ServiceManager 的代理 BpBinder,BpBinder 是一个 IBinder 类型对象。为了拿到这个 IBinder ,Android 进行了一系列的封装,首先它会通过 ServiceManagerNative 的 asInterface 方法拿到一个 IServiceManager 对象,这个 IServiceManager 就是 ServiceManagerProxy, 为了拿到这个 ServiceManagerProxy,又需要 BinderInternal 的 getContextObject 取得目标 Service 客户端代理 BpBinder。由于当前进程和目标 Service 很可能不是在同一个进程,所以要通过 ServiceManager 来统一调度找到目标 Service,拿到 BpBinder 后通过 transact 函数最终调用调用IPCThreadState 的 transact 函数进行数据传输。

小结

文章简单描述了 Binder 客户端获取服务的简单流程,主要围绕 ServiceManager 来展开的,涉及 java 层和 native 层的调用逻辑, 对于大部分细节都没有深入去探讨。

对于 Binder 服务端如何提供服务未涉及、对于 binder 线程池管理或者其他内部一些数据结构没有细究、对于 binder 驱动内存管理和数据与驱动交互逻辑未深入探讨。

参考文献

  • 深入理解 Android 内核设计思想 (林学森著 )
  • 深入理解 LINUX 内核 (中国电力出版社)
  • gityuan 博客关于 binder 的系列文章(gityuan.com/)
  • 知乎等平台关于 binder 相关话题的文章
原文  https://juejin.im/post/5d6d283ee51d4561e0516b52
正文到此结束
Loading...