Binder 机制 1

1,236 阅读51分钟

Android Binder系统概述: Binder是Android系统中大量使用的IPC(Inter-process communication,进程间通讯)机制。无论是应用程序对系统服务的请求,还是应用程序自身提供对外服务,都需要使用到Binder。

Android Binder系统C程序示例

(1)、简述Binder跨进程机制

Android系统中,每个应用程序是由Android的Activity,Service,Broadcast,ContentProvider这四组件的中一个或多个组合而成,这四组件所涉及的多进程间的通信底层都是依赖于Binder IPC机制。

从进程角度来看IPC机制

什么是ioctl ?

ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。

它的调用个数如下:int ioctl(int fd, ind cmd, …); 其中fd是用户程序打开设备时使用open函数返回的文件标示符,cmd是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,这个参数的有无和cmd的意义相关。

ioctl函数是文件结构中的一个属性分量,就是说如果你的驱动程序提供了对ioctl的支持,用户就可以在用户程序中使用ioctl函数来控制设备的I/O通道。

虽然在文件操作结构体"structfile_operations"中有很多对应的设备操作函数,但是有些命令是实在找不到对应的操作函数。如CD-ROM的驱动,想要一个弹出光驱的操作,这种操作并不是所有的字符设备都需要的,所以文件操作结构体也不会有对应的函数操作。

出于这样的原因,ioctl就有它的用处了————一些没办法归类的函数就统一放在ioctl这个函数操作中,通过指定的命令来实现对应的操作。所以,ioctl函数里面都实现了多个的对硬件的操作,通过应用层传入的命令来调用相应的操作。

来个图来说一下应用层与驱动函数的ioctl之间的联系:

上面的图可以看出,fd通过内核后找到对应的inode和file结构体指针并传给驱动函数,而另外两个参数却没有修改(类型改了没什么关系)。

int (*ioctl) (struct inode * node,struct file *filp, unsigned int cmd, unsigned long arg);

参数:

1)inode和file:ioctl的操作有可能是要修改文件的属性,或者访问硬件。要修改

文件属性的话,就要用到这两个结构体了,所以这里传来了它们的指针。

2)cmd:命令,接下来要长篇大论地说。

3)arg:参数,接下来也要长篇大论。

返回值:

1)如果传入的非法命令,ioctl返回错误号-EINVAL。

2)内核中的驱动函数返回值都有一个默认的方法,只要是正数,内核就会傻乎乎的认为这是正确的返回,并把它传给应用层,如果是负值,内核就会认为它是错误号了。

Ioctl里面多个不同的命令,那就要看它函数的实现来决定返回值了。打个比方,如果ioctl里面有一个类似read的函数,那返回值也就可以像read一样返回。

当然,不返回也是可以的。

现在Client进程需要访问Server进程中的服务,会经过以下步骤:

1、Server进程首先向ServiceManager注册服务(ServiceManager先于Server启动)

2、Client进程向ServiceManager查询服务得到一个句柄Handle(Server进程可能不止一个服务,用Handle区分是哪一个服务)

3、Client进程 封装数据Buffer通过Binder驱动发送给Server进程,Server进程取得数据后解析数据,使用Server进程的Handle服务对应的函数处理数据,处理完成后通过Binder驱动传输给Client进程

1.1、Server进程向ServiceManager注册服务

ServiceManager是一个守护进程。它的main()函数源码如下:

ServiceManager是如何启动的? 这里简要介绍一下ServiceManager的启动方式。当Kernel启动加载完驱动之后,会启动Android的init进程,init进程会解析servicemanager.rc,进而启动servicemanager.rc中定义的守护进程。

[->ServiceManager.c]

int main(int argc, char **argv)
{
    struct binder_state *bs;
    void *svcmgr = BINDER_SERVICE_MANAGER;
    // 打开驱动设备,过程需要持有binder_main_lock同步锁;
    bs = binder_open(128*1024);
    // servicemanager进程成为上下文管理者
    if (binder_become_context_manager(bs)) {
        ...
    }   

    svcmgr_handle = svcmgr;
    binder_loop(bs, svcmgr_handler);
    return 0;
}
void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;
    unsigned readbuf[32];

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

    // 告诉Kernel,ServiceManager进程进入了消息循环状态。
    readbuf[0] = BC_ENTER_LOOPER;
    binder_write(bs, readbuf, sizeof(unsigned));

    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (unsigned) readbuf;

        // 向Kernel中发送消息(先写后读)。
        // 先将消息传递给Kernel,然后再从Kernel读取消息反馈
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

        ...

        // 解析读取的消息反馈
        res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);
        ...
    }
}

binder_loop()主要工作: (1)、通过ioctl(,BINDER_WRITE_READ,)进入消息循环,休眠等待Client请求 (2)、当Client通过驱动请求服务时,binder驱动会唤醒ServiceManager,通过binder_parse()解析处理数据,回复信息

代码调用关系图:

时序流程图:

main()主要进行了三项工作: (1) 、通过binder_open()打开”/dev/binder”文件,即打开Binder设备文件。 (2) 、调用binder_become_context_manager(),通过ioctl()告诉Binder驱动程序自己是Binder上下文管理者。 (3) 、调用binder_loop()进入消息循环,等待Client的请求。如果没有Client请求,则进入睡眠等待状态;当有Client请求时,就被唤醒,然后读取并处理Client请求。

1.2、分析Android binder原生示例程序bctest.c:

int main(int argc, char **argv)
{
    struct binder_state *bs;
    uint32_t svcmgr = BINDER_SERVICE_MANAGER;
    uint32_t handle;

    bs = binder_open(128*1024);
    ...
    while (argc > 0) {
            //svcmgr_lookup方法调用binder_call(bs, &msg, &reply, 0, SVC_MGR_ADD_SERVICE)
            handle = svcmgr_lookup(bs, svcmgr, "alt_svc_mgr");
            //svcmgr_publish方法调用binder_call(bs, &msg, &reply, 0, SVC_MGR_CHECK_SERVICE)
            svcmgr_publish(bs, svcmgr, argv[1], &token);
    }
    return 0;
}

1.3、示例程序(bctest.c)注册服务、获取服务过程

注册服务的过程(bctest.c):

(1) 、bs = binder_open(128*1024) (2) 、binder_call(bs, &msg, &reply, 0, SVC_MGR_ADD_SERVICE) 参数说明: // msg含有服务的名字 // reply它会含有servicemanager回复的数据 // target为0表示servicemanager // code: 表示要调用servicemanager中的”addservice函数”

获取服务的过程(bctest.c):

(1) 、bs = binder_open(128*1024) (2) 、binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE) 参数说明: // msg含有服务的名字 // reply它会含有servicemanager回复的数据, 表示提供服务的进程 // target为0表示servicemanager // code: 表示要调用servicemanager中的”getservice函数”

binder_call远程实现: 根据msg、target、code就知道需要调用哪个服务的哪一个函数。

int binder_call(struct binder_state *bs,
                struct binder_io *msg, struct binder_io *reply,
                uint32_t target, uint32_t code)
{
    int res;
    struct binder_write_read bwr;
    struct {
        uint32_t cmd;
        struct binder_transaction_data txn;
    } __attribute__((packed)) writebuf;
    unsigned readbuf[32];

    writebuf.cmd = BC_TRANSACTION;
    writebuf.txn.target.handle = target;
    writebuf.txn.code = code;
    writebuf.txn.flags = 0;
    writebuf.txn.data_size = msg->data - msg->data0;
    writebuf.txn.offsets_size = ((char*) msg->offs) - ((char*) msg->offs0);
    writebuf.txn.data.ptr.buffer = (uintptr_t)msg->data0;
    writebuf.txn.data.ptr.offsets = (uintptr_t)msg->offs0;

    bwr.write_size = sizeof(writebuf);
    bwr.write_consumed = 0;
    bwr.write_buffer = (uintptr_t) &writebuf;

    hexdump(msg->data0, msg->data - msg->data0);
    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;

        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

        res = binder_parse(bs, reply, (uintptr_t) readbuf, bwr.read_consumed, 0);

    }
}

注: 结构体简介 binder_io 封装一次发送的数据 binder_write_read 存储一次读写操作的数据 binder_transaction_data 存储一次事务的数据

(1)构造参数,使用binder_io 描述 (2)数据转换binder_io -> binder_write_read;首先根据binder_io 、target、code三者构造binder_transaction_data,然后将binder_write_read.write_buffer指向binder_transaction_data (3)调用ioctl(bs->fd, BINDER_WRITE_READ, &bwr);发送数据

(2)、Android Binder系统_ServiceManager

2.1、ServiceManager中service句柄如何管理

前面分析过,ServiceManager开机初始会启动成为一个守护进程, ServiceManager是如何管理service句柄的? 进程里有一个全局性的svclist变量:

struct svcinfo *svclist = 0;

它记录着所有添加进系统的”Service”信息,这些信息被组织成一条单向链表,我们不妨称这条链表为”Service向量表”。示意图如下:

链表节点类型为svcinfo

添加服务简单理解就是 新建svcinfo节点插入到单链表中,查询服务就是看单链表是否有此服务。

2.2、解析Binder上传数据-(binder_parse函数)

回到ServiceManager的main()函数。binder_loop()会先向binder驱动发出了BC_ENTER_LOOPER命令,接着进入一个for循环不断调用ioctl()读取发来的数据,接着解析这些数据。假设现在Client有请求,Binder驱动就通过会上传数据。读取数据后会交由binder_parse()解析。

binder_loop(bs, svcmgr_handler);

注意binder_loop()的参数svcmgr_handler()函数指针。而且这个参数会进一步传递给binder_parse()。binder_parse()负责解析从binder驱动读来的数据,其代码截选如下:

int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uintptr_t ptr, size_t size, binder_handler func)
{
    int r = 1;
    uintptr_t end = ptr + (uintptr_t) size;

    while (ptr < end) {
        uint32_t cmd = *(uint32_t *) ptr;
        ptr += sizeof(uint32_t);
        switch(cmd) {
        ...
        //驱动有数据后会返回次cmd
        case BR_TRANSACTION: {
            struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
            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) {
                    binder_free_buffer(bs, txn->data.ptr.buffer);
                } else {
                    binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
                }              
                ...
            }
            ptr += sizeof(*txn);
            break;
        }
        ...
    }
    return r;
}

binder_loop()声明了一个128节的buffer(即uint32_t readbuf[32]),每次用BINDER_WRITE_READ命令从驱动读取一些内容,并传入binder_parse()。 binder_parse()在合适的时机,会回调其func参数(binder_handler func)指代的回调函数,即前文说到的svcmgr_handler()函数。

binder_loop()就这样一直循环下去,完成了整个ServiceManager的工作。

2.3、数据转换binder_transaction_data->binder_io

初始化reply;根据txt(Binder驱动反馈的信息)初始化msg

bio_init(&reply, rdata, sizeof(rdata), 4);
bio_init_from_txn(&msg, txn);

2.4、如何添加服务SVC_MGR_ADD_SERVICE

前面讲过 binder_parse()在合适的时机,会回调其func参数(binder_handler func)指代的回调函数,即前文说到的svcmgr_handler()函数。并且会根据binder_transaction_data的code判断具体调用哪一个函数。

int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    struct svcinfo *si;
    uint16_t *s;
    size_t len;
    uint32_t handle;
    uint32_t strict_policy;
    int allow_isolated;

    ......
    switch(txn->code) {
    case SVC_MGR_GET_SERVICE:
    case SVC_MGR_CHECK_SERVICE:
        s = bio_get_string16(msg, &len);
        handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
        bio_put_ref(reply, handle);
        return 0;

    case SVC_MGR_ADD_SERVICE:
        s = bio_get_string16(msg, &len);
        handle = bio_get_ref(msg);
        allow_isolated = bio_get_uint32(msg) ? 1 : 0;
        if (do_add_service(bs, s, len, handle, txn->sender_euid,
            allow_isolated, txn->sender_pid))
            return -1;
        break;
...
    }
    bio_put_uint32(reply, 0);
    return 0;
}

由代码可知code = SVC_MGR_ADD_SERVICE 会调用do_add_service()函数

int do_add_service(struct binder_state *bs,
                   const uint16_t *s, size_t len,
                   uint32_t handle, uid_t uid, int allow_isolated,
                   pid_t spid)
{
    struct svcinfo *si;
    ...
    si = find_svc(s, len);
    if (si) {
        ...
    } else {
        si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
        ...
        si->handle = handle;
        si->len = len;
        memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
        si->name[len] = '\0';
        si->death.func = (void*) svcinfo_death;
        si->death.ptr = si;
        si->allow_isolated = allow_isolated;
        si->next = svclist;
        svclist = si;
    }
    binder_acquire(bs, handle);
    binder_link_to_death(bs, handle, &si->death);
    return 0;
}

可见添加Service只是新建了一个svcinfo然后插入到前面所说的”Service向量表”中。

2.5、如何获取服务SVC_MGR_CHECK_SERVICE

uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid)
{
    struct svcinfo *si = find_svc(s, len);
    ...
    return si->handle;
}

获取服务会查询”Service向量表”是否有此服务,然后返回Service的句柄handle。

2.6、ServiceManager回复数据

前面分析回调svcmgr_handler()函数处理数据后,会调用binder_send_reply()函数 回复消息给驱动。

binder_send_reply(bs, &reply, txn->data.ptr.buffer, res)

2.7、总结:

示例程序(bctest.c)注册、获取服务一般分以下步骤: (1)源进程通过binder_open()打开”/dev/binder”文件,即打开Binder设备文件。 (2)源进程构造数据:[a].构造binder_io [b].转为binder_transaction_data [c].放入binder_write_read (3)源进程调用ioctl(bs->fd, BINDER_WRITE_READ, &bwr);发送数据给驱动 (4)驱动上报数据到目的进程ServiceManager (5)目的进程ServiceManager处理完数据,重新构造数据,通过调用ioctl(bs->fd, BINDER_WRITE_READ, &bwr);发送数据给驱动 (6)驱动然后将数据反馈到源进程

(3)、Android Binder系统C程序

3.1、Android Binder系统C程序_框架

总结bctest.c注册服务获取服务的一般流程框架:

二、Android Binder系统-Driver层

(1)、Binder驱动概述

1.1 概述

Binder驱动是Android专用的,但底层的驱动架构与Linux驱动一样。binder驱动在以misc设备进行注册,作为虚拟字符设备,没有直接操作硬件,只是对设备内存的处理。主要是驱动设备的初始化(binder_init),打开 (binder_open),映射(binder_mmap),数据操作(binder_ioctl)。如启动ServiceManager调用:

1.2 系统调用

用户态的程序调用Kernel层驱动是需要陷入内核态,进行系统调用(syscall),比如打开Binder驱动方法的调用链为: open-> open() -> binder_open()。 open()为用户空间的方法,open()便是系统调用中相应的处理方法,通过查找,对应调用到内核binder驱动的binder_open()方法,至于其他的从用户态陷入内核态的流程也基本一致。

(2)、Binder核心方法

2.1、binder_init()

主要工作是为了注册misc设备 binder_init函数中最主要的工作其实下面这行:

ret = misc_register(&binder_miscdev);

该行代码真正向内核中注册了Binder设备。binder_miscdev的定义如下:

static struct miscdevice binder_miscdev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "binder",
    .fops = &binder_fops
};

这里指定了Binder设备的名称是”binder”。这样,在用户空间便可以通过对/dev/binder文件进行操作来使用Binder。 binder_miscdev同时也指定了该设备的fops。fops是另外一个结构体,这个结构中包含了一系列的函数指针,其定义如下:

static const struct file_operations binder_fops = {
    .owner = THIS_MODULE,
    .poll = binder_poll,
    .unlocked_ioctl = binder_ioctl,
    .compat_ioctl = binder_ioctl,
    .mmap = binder_mmap,
    .open = binder_open,
    .flush = binder_flush,
    .release = binder_release,
};

2.2、主要结构

驱动中的结构体可以分为两类:

一类是与用户空间共用的,这些结构体在Binder通信协议过程中会用到。因此,这些结构体定义在binder.h中,包括:

从前面Binder系统C程序框架分析,这其中,binder_write_read和binder_transaction_data这两个结构体最为重要,它们存储了IPC调用过程中的数据。

Binder驱动中,还有一类结构体是仅仅Binder驱动内部实现过程中需要的,它们定义在binder.c中,包括:

这里需要读者关注的结构体已经用加粗做了标注。

2.3、Binder协议 Binder协议可以分为控制协议和驱动协议两类。

控制协议是进程通过ioctl(“/dev/binder”) 与Binder设备进行通讯的协议,该协议包含以下几种命令:

Binder的驱动协议描述了对于Binder驱动的具体使用过程。驱动协议又可以分为两类:

一类是binder_driver_command_protocol,描述了进程发送给Binder驱动的命令 一类是binder_driver_return_protocol,描述了Binder驱动发送给进程的命令 binder_driver_command_protocol共包含17个命令,分别是:

binder_driver_return_protocol共包含18个命令,分别是:

单独看上面的协议可能很难理解,这里我们以一次Binder请求过程来详细看一下Binder协议是如何通信的,就比较好理解了。

这幅图的说明如下:

Binder是C/S架构的,通信过程牵涉到:Client,Server以及Binder驱动三个角色 Client对于Server的请求以及Server对于Client回复都需要通过Binder驱动来中转数据 BC_XXX命令是进程发送给驱动的命令 BR_XXX命令是驱动发送给进程的命令 整个通信过程由Binder驱动控制

2.4、binder_open()

任何进程在使用Binder之前,都需要先通过open(“/dev/binder”)打开Binder设备。上文已经提到,用户空间的open系统调用对应了驱动中的binder_open函数。在这个函数,Binder驱动会为调用的进程做一些初始化工作。binder_open函数代码如下所示:

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

   // 创建进程对应的binder_proc对象
    proc = kzalloc(sizeof(*proc), GFP_KERNEL);
    if (proc == NULL)
        return -ENOMEM;
    get_task_struct(current);
    proc->tsk = current;
    // 初始化binder_proc
    INIT_LIST_HEAD(&proc->todo);
    init_waitqueue_head(&proc->wait);
    proc->default_priority = task_nice(current);

    // 锁保护
    binder_lock(__func__);

    binder_stats_created(BINDER_STAT_PROC);
    // 添加到全局列表binder_procs中
    hlist_add_head(&proc->proc_node, &binder_procs);
    proc->pid = current->group_leader->pid;
    INIT_LIST_HEAD(&proc->delivered_death);
    filp->private_data = proc;

    binder_unlock(__func__);

    return 0;
}

在Binder驱动中,通过binder_procs记录了所有使用Binder的进程。每个初次打开Binder设备的进程都会被添加到这个列表中的。

binder_proc binder_node binder_thread binder_ref binder_buffer 这些结构体互相之间都留有字段存储关联的结构。

2.5、binder_mmap()

在打开Binder设备之后,进程还会通过mmap进行内存映射。mmap的作用有如下两个:

申请一块内存空间,用来接收Binder通信过程中的数据 对这块内存进行地址映射,以便将来访问 binder_mmap函数对应了mmap系统调用的处理,这个函数也是Binder驱动的精华所在(这里说的binder_mmap函数也包括其内部调用的binder_update_page_range函数,见下文)。

前文我们说到,使用Binder机制,数据只需要经历一次拷贝就可以了,其原理就在这个函数中。

binder_mmap这个函数中,会申请一块物理内存,然后在用户空间和内核空间同时对应到这块内存上。在这之后,当有Client要发送数据给Server的时候,只需一次,将Client发送过来的数据拷贝到Server端的内核空间指定的内存地址即可,由于这个内存地址在服务端已经同时映射到用户空间,因此无需再做一次复制,Server即可直接访问,整个过程如下图所示:

这幅图的说明如下:

Server在启动之后,调用对/dev/binder设备调用mmap 内核中的binder_mmap函数进行对应的处理:申请一块物理内存,然后在用户空间和内核空间同时进行映射 Client通过BINDER_WRITE_READ命令发送请求,这个请求将先到驱动中,同时需要将数据从Client进程的用户空间拷贝到内核空间 驱动通过BR_TRANSACTION通知Server有人发出请求,Server进行处理。由于这块内存也在用户空间进行了映射,因此Server进程的代码可以直接访问 了解原理之后,我们再来看一下Binder驱动的相关源码。这段代码有两个函数:

binder_mmap函数对应了mmap的系统调用的处理 binder_update_page_range函数真正实现了内存分配和地址映射

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;

    ...
   // 在内核空间获取一块地址范围
    area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
    if (area == NULL) {
        ret = -ENOMEM;
        failure_string = "get_vm_area";
        goto err_get_vm_area_failed;
    }
    proc->buffer = area->addr;
    // 记录内核空间与用户空间的地址偏移
    proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
    mutex_unlock(&binder_mmap_lock);

  ...
    proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
    if (proc->pages == NULL) {
        ret = -ENOMEM;
        failure_string = "alloc page array";
        goto err_alloc_pages_failed;
    }
    proc->buffer_size = vma->vm_end - vma->vm_start;

    vma->vm_ops = &binder_vm_ops;
    vma->vm_private_data = proc;

    /* binder_update_page_range assumes preemption is disabled */
    preempt_disable();
    // 通过下面这个函数真正完成内存的申请和地址的映射
    // 初次使用,先申请一个PAGE_SIZE大小的内存
    ret = binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma);
    ...
}

static int binder_update_page_range(struct binder_proc *proc, int allocate,
                    void *start, void *end,
                    struct vm_area_struct *vma)
{
    void *page_addr;
    unsigned long user_page_addr;
    struct vm_struct tmp_area;
    struct page **page;
    struct mm_struct *mm;

    ...

    for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
        int ret;
        struct page **page_array_ptr;
        page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];

        BUG_ON(*page);
        // 真正进行内存的分配
        *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
        if (*page == NULL) {
            pr_err("%d: binder_alloc_buf failed for page at %p\n",
                proc->pid, page_addr);
            goto err_alloc_page_failed;
        }
        tmp_area.addr = page_addr;
        tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */;
        page_array_ptr = page;
        // 在内核空间进行内存映射
        ret = map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr);
        if (ret) {
            pr_err("%d: binder_alloc_buf failed to map page at %p in kernel\n",
                   proc->pid, page_addr);
            goto err_map_kernel_failed;
        }
        user_page_addr =
            (uintptr_t)page_addr + proc->user_buffer_offset;
        // 在用户空间进行内存映射
        ret = vm_insert_page(vma, user_page_addr, page[0]);
        if (ret) {
            pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n",
                   proc->pid, user_page_addr);
            goto err_vm_insert_page_failed;
        }
        /* vm_insert_page does not seem to increment the refcount */
    }
    if (mm) {
        up_write(&mm->mmap_sem);
        mmput(mm);
    }

    preempt_disable();

    return 0;
...

binder_update_page_range主要完成工作:分配物理空间,将物理空间映射到内核空间,将物理空间映射到进程空间. 另外,不同参数下该方法也可以释放物理页面。

2.6、binder_ioctl()内存管理

上文中,我们看到binder_mmap的时候,会申请一个PAGE_SIZE(通常是4K)的内存。而实际使用过程中,一个PAGE_SIZE的大小通常是不够的。

在驱动中,会根据实际的使用情况进行内存的分配。有内存的分配,当然也需要内存的释放。这里我们就来看看Binder驱动中是如何进行内存的管理的。

首先,我们还是从一次IPC请求说起。

当一个Client想要对Server发出请求时,它首先将请求发送到Binder设备上,由Binder驱动根据请求的信息找到对应的目标节点,然后将请求数据传递过去。

进程通过ioctl系统调用来发出请求:ioctl(bs->fd, BINDER_WRITE_READ, &bwr)

这里的bs->fd对应了打开Binder设备时的fd。BINDER_WRITE_READ对应了具体要做的操作码,这个操作码将由Binder驱动解析。bwr存储了请求数据,其类型是binder_write_read。

binder_write_read其实是一个相对外层的数据结构,其内部会包含一个binder_transaction_data结构的数据。binder_transaction_data包含了发出请求者的标识,请求的目标对象以及请求所需要的参数。它们的关系如下图所示:

binder_ioctl函数对应了ioctl系统调用的处理。这个函数的逻辑比较简单,就是根据ioctl的命令来确定进一步处理的逻辑,具体如下:

● 如果命令是BINDER_WRITE_READ,并且

● 如果 bwr.write_size > 0,则调用binder_thread_write

● 如果 bwr.read_size > 0,则调用binder_thread_read

● 如果命令是BINDER_SET_MAX_THREADS,则设置进程的max_threads,即进程支 持的最大线程数

● 如果命令是BINDER_SET_CONTEXT_MGR,则设置当前进程为ServiceManager,见下文

● 如果命令是BINDER_THREAD_EXIT,则调用binder_free_thread,释放binder_thread

● 如果命令是BINDER_VERSION,则返回当前的Binder版本号

最关键的就是binder_thread_write方法。当Client请求Server的时候,便会发送一个BINDER_WRITE_READ命令,同时框架会将将实际的数据包装好。此时,binder_transaction_data中的code将是BC_TRANSACTION,由此便会调用到binder_transaction方法,这个方法是对一次Binder事务的处理,这其中会调用binder_alloc_buf函数为此次事务申请一个缓存。

struct binder_buffer {
    struct list_head entry;
    struct rb_node rb_node;

    unsigned free:1;
    unsigned allow_user_free:1;
    unsigned async_transaction:1;
    unsigned debug_id:29;

    struct binder_transaction *transaction;

    struct binder_node *target_node;
    size_t data_size;
    size_t offsets_size;
    uint8_t data[0];
};

而在binder_proc(描述了使用Binder的进程)中,包含了几个字段用来管理进程在Binder IPC过程中缓存,如下:

struct binder_proc {
    ...
    struct list_head buffers; // 进程拥有的buffer列表
    struct rb_root free_buffers; // 空闲buffer列表
    struct rb_root allocated_buffers; // 已使用的buffer列表
    size_t free_async_space; // 剩余的异步调用的空间

    size_t buffer_size; // 缓存的上限
  ...
};

进程在mmap时,会设定支持的总缓存大小的上限(下文会讲到)。而进程每当收到BC_TRANSACTION,就会判断已使用缓存加本次申请的和有没有超过上限。如果没有,就考虑进行内存的分配。

进程的空闲缓存记录在binder_proc的free_buffers中,这是一个以红黑树形式存储的结构。每次尝试分配缓存的时候,会从这里面按大小顺序进行查找,找到最接近需要的一块缓存。查找的逻辑如下:

while (n) {
    buffer = rb_entry(n, struct binder_buffer, rb_node);
    BUG_ON(!buffer->free);
    buffer_size = binder_buffer_size(proc, buffer);

    if (size < buffer_size) {
        best_fit = n;
        n = n->rb_left;
    } else if (size > buffer_size)
        n = n->rb_right;
    else {
        best_fit = n;
        break;
    }
}

找到之后,还需要对binder_proc中的字段进行相应的更新:

rb_erase(best_fit, &proc->free_buffers);
buffer->free = 0;
binder_insert_allocated_buffer(proc, buffer);
if (buffer_size != size) {
    struct binder_buffer *new_buffer = (void *)buffer->data + size;
    list_add(&new_buffer->entry, &buffer->entry);
    new_buffer->free = 1;
    binder_insert_free_buffer(proc, new_buffer);
}
binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
         "%d: binder_alloc_buf size %zd got %p\n",
          proc->pid, size, buffer);
buffer->data_size = data_size;
buffer->offsets_size = offsets_size;
buffer->async_transaction = is_async;
if (is_async) {
    proc->free_async_space -= size + sizeof(struct binder_buffer);
    binder_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC,
             "%d: binder_alloc_buf size %zd async free %zd\n",
              proc->pid, size, proc->free_async_space);
}

BC_FREE_BUFFER命令是通知驱动进行内存的释放,binder_free_buf函数是真正实现的逻辑,这个函数与binder_alloc_buf是刚好对应的。在这个函数中,所做的事情包括:

重新计算进程的空闲缓存大小 通过binder_update_page_range释放内存 更新binder_proc的buffers,free_buffers,allocated_buffers字段

2.7、Binder中的”面向对象”

Binder机制淡化了进程的边界,使得跨越进程也能够调用到指定服务的方法,其原因是因为Binder机制在底层处理了在进程间的”对象”传递。

在Binder驱动中,并不是真的将对象在进程间来回序列化,而是通过特定的标识来进行对象的传递。Binder驱动中,通过flat_binder_object来描述需要跨越进程传递的对象。其定义如下:

struct flat_binder_object {
    __u32        type;
    __u32        flags;

    union {
        binder_uintptr_t    binder; /* local object */
        __u32            handle;    /* remote object */
    };
    binder_uintptr_t    cookie;
};

这其中,type有如下5种类型。

enum {
    BINDER_TYPE_BINDER    = B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE),
    BINDER_TYPE_WEAK_BINDER    = B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE),
    BINDER_TYPE_HANDLE    = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),
    BINDER_TYPE_WEAK_HANDLE    = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),
    BINDER_TYPE_FD        = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),
};

当对象传递到Binder驱动中的时候,由驱动来进行翻译和解释,然后传递到接收的进程。

例如当Server把Binder实体传递给Client时,在发送数据流中,flat_binder_object中的type是BINDER_TYPE_BINDER,同时binder字段指向Server进程用户空间地址。但这个地址对于Client进程是没有意义的(Linux中,每个进程的地址空间是互相隔离的),驱动必须对数据流中的flat_binder_object做相应的翻译:将type该成BINDER_TYPE_HANDLE;为这个Binder在接收进程中创建位于内核中的引用并将引用号填入handle中。对于发生数据流中引用类型的Binder也要做同样转换。经过处理后接收进程从数据流中取得的Binder引用才是有效的,才可以将其填入数据包binder_transaction_data的target.handle域,向Binder实体发送请求。

由于每个请求和请求的返回都会经历内核的翻译,因此这个过程从进程的角度来看是完全透明的。进程完全不用感知这个过程,就好像对象真的在进程间来回传递一样。

2.8、驱动层的线程管理

上文多次提到,Binder本身是C/S架构。由Server提供服务,被Client使用。既然是C/S架构,就可能存在多个Client会同时访问Server的情况。 在这种情况下,如果Server只有一个线程处理响应,就会导致客户端的请求可能需要排队而导致响应过慢的现象发生。解决这个问题的方法就是引入多线程。

Binder机制的设计从最底层–驱动层,就考虑到了对于多线程的支持。具体内容如下:

使用Binder的进程在启动之后,通过BINDER_SET_MAX_THREADS告知驱动其支持的最大线程数量 驱动会对线程进行管理。在binder_proc结构中,这些字段记录了进程中线程的信息:max_threads,requested_threads,requested_threads_started,ready_threads binder_thread结构对应了Binder进程中的线程 驱动通过BR_SPAWN_LOOPER命令告知进程需要创建一个新的线程 进程通过BC_ENTER_LOOPER命令告知驱动其主线程已经ready 进程通过BC_REGISTER_LOOPER命令告知驱动其子线程(非主线程)已经ready 进程通过BC_EXIT_LOOPER命令告知驱动其线程将要退出 在线程退出之后,通过BINDER_THREAD_EXIT告知Binder驱动。驱动将对应的binder_thread对象销毁

2.9、再聊ServiceManager

上文已经说过,每一个Binder Server在驱动中会有一个binder_node进行对应。同时,Binder驱动会负责在进程间传递服务对象,并负责底层的转换。另外,我们也提到,每一个Binder服务都需要有一个唯一的名称。由ServiceManager来管理这些服务的注册和查找。

而实际上,为了便于使用,ServiceManager本身也实现为一个Server对象。任何进程在使用ServiceManager的时候,都需要先拿到指向它的标识。然后通过这个标识来使用ServiceManager。

这似乎形成了一个互相矛盾的现象:

通过ServiceManager我们才能拿到Server的标识 ServiceManager本身也是一个Server 解决这个矛盾的办法其实也很简单:Binder机制为ServiceManager预留了一个特殊的位置。这个位置是预先定好的,任何想要使用ServiceManager的进程只要通过这个特定的位置就可以访问到ServiceManager了(而不用再通过ServiceManager的接口)。

在Binder驱动中,有一个全局的binder_node 变量:

一般情况下,对于每一个Server驱动层会对应一个binder_node节点,然而binder_context_mgr_node比较特殊,它没有对应的应用层binder实体。在整个系统里,它是如此特殊,以至于系统规定,任何应用都必须使用句柄0来跨进程地访问它。
static struct binder_node *binder_context_mgr_node;

这个变量指向的就是ServiceManager。

当有进程通过ioctl并指定命令为BINDER_SET_CONTEXT_MGR的时候,驱动被认定这个进程是ServiceManager,binder_ioctl()函数中对应的处理如下:

case BINDER_SET_CONTEXT_MGR:
    if (binder_context_mgr_node != NULL) {
    }
    ret = security_binder_set_context_mgr(proc->tsk);
    else {
    binder_context_mgr_uid = current->cred->euid;
    binder_context_mgr_node = binder_new_node(proc, 0, 0);//在Binder驱动层创建binder_node结构体对象
    binder_context_mgr_node->local_weak_refs++;
    binder_context_mgr_node->local_strong_refs++;
    binder_context_mgr_node->has_strong_ref = 1;
    binder_context_mgr_node->has_weak_ref = 1;
    }
    break;

ServiceManager应当要先于所有Binder Server之前启动。在它启动完成并告知Binder驱动之后,驱动便设定好了这个特定的节点。

在这之后,当有其他模块想要使用ServerManager的时候,只要将请求指向ServiceManager所在的位置即可。

在Binder驱动中,通过handle = 0这个位置来访问ServiceManager。例如,binder_transaction中,判断如果target.handler为0,则认为这个请求是发送给ServiceManager的,相关代码如下:

if (tr->target.handle) {
    struct binder_ref *ref;
    ref = binder_get_ref(proc, tr->target.handle, true);
    if (ref == NULL) {
        binder_user_error("%d:%d got transaction to invalid handle\n",
            proc->pid, thread->pid);
        return_error = BR_FAILED_REPLY;
        goto err_invalid_target_handle;
    }
    target_node = ref->node;
} else {
    target_node = binder_context_mgr_node;
    if (target_node == NULL) {
        return_error = BR_DEAD_REPLY;
        goto err_no_context_mgr_node;
    }
}

2.10、binder_node等重要结构体

  1. Binder实体binder_node

Binder实体,是各个Server以及ServiceManager在内核中的存在形式。 Binder实体实际上是内核中binder_node结构体的对象,它的作用是在内核中保存Server和ServiceManager的信息(例如,Binder实体中保存了Server对象在用户空间的地址)。简言之,Binder实体是Server在Binder驱动中的存在形式,内核通过Binder实体可以找到用户空间的Server对象。 在上图中,Server和ServiceManager在Binder驱动中都对应的存在一个Binder实体。

  1. Binder引用binder_ref

说到Binder实体,就不得不说”Binder引用”。所谓Binder引用,实际上是内核中binder_ref结构体的对象,它的作用是在表示”Binder实体”的引用。换句话说,每一个Binder引用都是某一个Binder实体的引用,通过Binder引用可以在内核中找到它对应的Binder实体。 如果将Server看作是Binder实体的话,那么Client就好比Binder引用。Client要和Server通信,它就是通过保存一个Server对象的Binder引用,再通过该Binder引用在内核中找到对应的Binder实体,进而找到Server对象,然后将通信内容发送给Server对象。

Binder实体和Binder引用都是内核(即,Binder驱动)中的数据结构。每一个Server在内核中就表现为一个Binder实体,而每一个Client则表现为一个Binder引用。这样,每个Binder引用都对应一个Binder实体,而每个Binder实体则可以多个Binder引用。

3、Binder buffer:binder_buffer

4、Binder进程binder_proc

5、Binder线程binder_thread

binder机制到底是如何从Binder对象找到其对应的Binder实体呢?

注意其中的那4个rb_root域,”rb”的意思是”red black”,可见binder_proc里搞出了4个红黑树。

其中,nodes树用于记录binder实体,refs_by_desc树和refs_by_node树则用于记录binder代理。之所以会有两个代理树,是为了便于快速查找,我们暂时只关心其中之一就可以了。threads树用于记录执行传输动作的线程信息。

在一个进程中,有多少”被其他进程进行跨进程调用的”binder实体,就会在该进程对应的nodes树中生成多少个红黑树节点。另一方面,一个进程要访问多少其他进程的binder实体,则必须在其refs_by_desc树中拥有对应的引用节点。

这4棵树的节点类型是不同的,threads树的节点类型为binder_thread,nodes树的节点类型为binder_node,refs_by_desc树和refs_by_node树的节点类型相同,为binder_ref。这些节点内部都会包含rb_node子结构,该结构专门负责连接节点的工作,和前文的hlist_node有点儿异曲同工,这也是linux上一个常用的小技巧。我们以nodes树为例

nodes树是用于记录binder实体的,所以nodes树中的每个binder_node节点,必须能够记录下相应binder实体的信息。因此请大家注意binder_node的ptr域和cookie域。

另一方面,refs_by_desc树和refs_by_node树的每个binder_ref节点则和上层的一个BpBinder对应,而且更重要的是,它必须具有和”目标binder实体的binder_node”进行关联的信息。

请注意binder_ref的那个node域,它负责和binder_node关联。另外,binder_ref中有两个类型为rb_node的域:rb_node_desc域和rb_node_node域,它们分别用于连接refs_by_desc树和refs_by_node。也就是说虽然binder_proc中有两棵引用树,但这两棵树用到的具体binder_ref节点其实是复用的。

binder_node.ptr对应于flat_binder_object.binder; binder_node.cookie对应于flat_binder_object.cookie。

OK,现在我们可以更深入地说明binder句柄的作用了,比如进程1的BpBinder在发起跨进程调用时,向binder驱动传入了自己记录的句柄值,binder驱动就会在”进程1对应的binder_proc结构”的引用树中查找和句柄值相符的binder_ref节点,一旦找到binder_ref节点,就可以通过该节点的node域找到对应的binder_node节点,这个目标binder_node当然是从属于进程2的binder_proc啦,不过不要紧,因为binder_ref和binder_node都处于binder驱动的地址空间中,所以是可以用指针直接指向的。目标binder_node节点的cookie域,记录的其实是进程2中BBinder的地址,binder驱动只需把这个值反映给应用层,应用层就可以直接拿到BBinder了。这就是Binder完成精确打击的大体过程。

三、Android Binder系统驱动情景分析

为了更深刻的了解Binder系统 注册服务、获取服务、使用服务的过程,在Driver层(kernel/drivers/staging/android/binder.c)的binder_thread_read()函数、binder_transaction()函数入打印log,让前面编写的C程序示例与binder驱动交互打印更详细的过程。

static void binder_transaction(struct binder_proc *proc,
                   struct binder_thread *thread,
                   struct binder_transaction_data *tr, int reply)
int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
            void __user *buffer, int size, signed long *consumed)

(1)、Binder系统驱动情景分析–服务”Hello”注册过程

1.1、ServiceManager休眠等待

回顾一下ServiceManager启动流程,ServiceManager进入binder_loop()后 会休眠等待响应client请求。

binder_loop(){
    // 告诉Kernel,ServiceManager进程进入了消息循环状态。
    readbuf[0] = BC_ENTER_LOOPER;
    binder_write(bs, readbuf, sizeof(unsigned));

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

    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (unsigned) readbuf;

        // 向Kernel中发送消息(先写后读)。
        // 先将消息传递给Kernel,然后再从Kernel读取消息反馈
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
        // 解析读取的消息反馈
        res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);
        ...
    }
}

binder_write(bs, readbuf, sizeof(unsigned));会调用ioctl向内核发送数据。

 int binder_write(struct binder_state *bs, void *data, size_t len)
{
    struct binder_write_read bwr;
    int res;

    bwr.write_size = len;
    bwr.write_consumed = 0;
    bwr.write_buffer = (uintptr_t) data;
    bwr.read_size = 0;
    bwr.read_consumed = 0;
    bwr.read_buffer = 0;
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
    return res;
}

如果 bwr.write_size > 0,则调用binder_thread_write 如果 bwr.read_size >0,则调用binder_thread_read

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
  int ret;
  struct binder_proc *proc = filp->private_data;
  struct binder_thread *thread;
  unsigned int size = _IOC_SIZE(cmd);
  void __user *ubuf = (void __user *)arg;

  // 中断等待函数。
  ret = wait_event_interruptible(...);
  // 在proc进程中查找该线程对应的binder_thread;若查找失败,则新建一个binder_thread,并添加到proc->threads中。
  thread = binder_get_thread(proc);
  ...
  switch (cmd) {
  case BINDER_WRITE_READ: {
      struct binder_write_read bwr;
      ...
      // 将binder_write_read从"用户空间" 拷贝到 "内核空间"
      if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
          ...
      }
      // 如果write_size>0,则进行写操作
      if (bwr.write_size > 0) {
          ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
          ...
      }
      // 如果read_size>0,则进行读操作
      if (bwr.read_size > 0) {
          ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags   & O_NONBLOCK);
          ...
      }
      ...
      if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
      }
      break;
  }
  }
  return ret;
}

bwr.write_size > 0; 继续查看binder_thread_write()

注:只有BR_TRANSACTION、BR_REPLY、BC_TRANSACTION、BC_REPLY涉及两进程 其他所有BC_XXX、BR_XXX都只是App和驱动交互用于改变报告状态。

int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
          void __user *buffer, int size, signed long *consumed)
{
  uint32_t cmd;
  void __user *ptr = buffer + *consumed;
  void __user *end = buffer + size;

  // 读取binder_write_read.write_buffer中的内容。
  // 每次读取32bit(即4个字节)
  while (ptr < end && thread->return_error == BR_OK) {
      // 从用户空间读取32bit到内核中,并赋值给cmd。
      if (get_user(cmd, (uint32_t __user *)ptr))
          return -EFAULT;

      ptr += sizeof(uint32_t);
      switch (cmd) {
        case BC_ENTER_LOOPER:
            thread->looper |= BINDER_LOOPER_STATE_ENTERED;
            break;
      ...
      }
      // 更新bwr.write_consumed的值
      *consumed = ptr - buffer;
  }
  return 0;
}

当前线程进入BC_ENTER_LOOPER状态,等待请求。 继续binder_loop()中的for(;;;)循环,bwr.read_size >0;会通过binder_thread_read()读操作。

static int binder_thread_read(struct binder_proc *proc,
                struct binder_thread *thread,
                void  __user *buffer, int size,
                signed long *consumed, int non_block)
{
  void __user *ptr = buffer + *consumed;
  void __user *end = buffer + size;
  int ret = 0;
  int wait_for_proc_work;
  // 如果*consumed=0,则写入BR_NOOP到用户传进来的bwr.read_buffer缓存区
  if (*consumed == 0) {
      if (put_user(BR_NOOP, (uint32_t __user *)ptr))
          return -EFAULT;
      // 修改指针位置
      ptr += sizeof(uint32_t);
  }
  ...
}

可以看到驱动put_user(BR_NOOP, (uint32_t __user *)ptr)发送BR_NOOP到ServiceManager

对于所有的读操作,数据头都是BR_NOOP,如BR_REPLY

>  ./service_manager &
> [   32.566620] service_manager (1362, 1362), binder_thread_write : BC_ENTER_LOOPER
> [   32.566712] service_manager (1362, 1362), binder_thread_read : BR_NOOP
>

1.2、Clent(此处为Test_server)请求SM添加服务

构造数据发送给驱动 我们执行Test_server时,打印了很多数据,我们首先看一下数据的构造过程 和 组织格式,这有助于加深我们对binder系统的理解。

int svcmgr_publish(struct binder_state *bs, uint32_t target, const char *name, void *ptr)
{
    int status;
    unsigned iodata[512/4];
    struct binder_io msg, reply;

    bio_init(&msg, iodata, sizeof(iodata), 4);
    bio_put_uint32(&msg, 0);  // strict mode header
    bio_put_string16_x(&msg, SVC_MGR_NAME);
    bio_put_string16_x(&msg, name);
    bio_put_obj(&msg, ptr);

    if (binder_call(bs, &msg, &reply, target, SVC_MGR_ADD_SERVICE))
        return -1;
    status = bio_get_uint32(&reply);
    binder_done(bs, &msg, &reply);
    return status;
}

bio_init()、bio_put_uint32()、bio_put_string16_x()函数比较简洁。我们看下bio_put_obj()函数。 构建初始化flat_binder_object结构体:

void bio_put_obj(struct binder_io *bio, void *ptr)
{
    struct flat_binder_object *obj;

    obj = bio_alloc_obj(bio);
    if (!obj)
        return;

    obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;//
    obj->type = BINDER_TYPE_BINDER;//
    obj->binder = (uintptr_t)ptr;//
    obj->cookie = 0;//0
}

数据结构示意图:

Clent(此处为Test_server),test_server.c调用流程: ->svcmgr_publish() ->binder_call() ->ioctl(bs->fd, BINDER_WRITE_READ, &bwr) ->binder_thread_write() ->binder_transaction()

现在数据构造好了,binder_call()会调用ioctl(bs->fd, BINDER_WRITE_READ, &bwr)

int binder_call(struct binder_state *bs,
                struct binder_io *msg, struct binder_io *reply,
                uint32_t target, uint32_t code)
{
    int res;
    struct binder_write_read bwr;
    struct {
        uint32_t cmd;
        struct binder_transaction_data txn;
    } __attribute__((packed)) writebuf;
    unsigned readbuf[32];

    writebuf.cmd = BC_TRANSACTION;
    writebuf.txn.target.handle = target;
    writebuf.txn.code = code;
    writebuf.txn.flags = 0;
    writebuf.txn.data_size = msg->data - msg->data0;
    writebuf.txn.offsets_size = ((char*) msg->offs) - ((char*) msg->offs0);
    writebuf.txn.data.ptr.buffer = (uintptr_t)msg->data0;
    writebuf.txn.data.ptr.offsets = (uintptr_t)msg->offs0;

    bwr.write_size = sizeof(writebuf);
    bwr.write_consumed = 0;
    bwr.write_buffer = (uintptr_t) &writebuf;

    hexdump(msg->data0, msg->data - msg->data0);
    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;

        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
        ...
        res = binder_parse(bs, reply, (uintptr_t) readbuf, bwr.read_consumed, 0);
    }
}

[ 38.320197] test_server (1363, 1363), binder_thread_write : BC_TRANSACTION 发送数据,进而会调用binder_thread_write()处理数据。

binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
  int ret;
  struct binder_proc *proc = filp->private_data;
  struct binder_thread *thread;
  unsigned int size = _IOC_SIZE(cmd);
  void __user *ubuf = (void __user *)arg;

  // 中断等待函数。
  ret = wait_event_interruptible(...);
  // 在proc进程中查找该线程对应的binder_thread;若查找失败,则新建一个binder_thread,并添加到proc->threads中。
  thread = binder_get_thread(proc);
  ...
  switch (cmd) {
  case BINDER_WRITE_READ: {
      struct binder_write_read bwr;
      ...
      // 将binder_write_read从"用户空间" 拷贝到 "内核空间"
      if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
          ...
      }
      // 如果write_size>0,则进行写操作
      if (bwr.write_size > 0) {
          ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
          ...
      }
      // 如果read_size>0,则进行读操作
      if (bwr.read_size > 0) {
          ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags   & O_NONBLOCK);
          ...
      }
      ...
      if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
      }
      break;
  }
  }
  return ret;
}

由于write_size>0,调用binder_thread_write()处理数据:

int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
          void __user *buffer, int size, signed long *consumed)
{
  uint32_t cmd;
  void __user *ptr = buffer + *consumed;
  void __user *end = buffer + size;

  // 读取binder_write_read.write_buffer中的内容。
  // 每次读取32bit(即4个字节)
  while (ptr < end && thread->return_error == BR_OK) {
      // 从用户空间读取32bit到内核中,并赋值给cmd。
      if (get_user(cmd, (uint32_t __user *)ptr))
          return -EFAULT;

      ptr += sizeof(uint32_t);
      ...
      switch (cmd) {
      ...
        case BC_TRANSACTION:
        case BC_REPLY: {
            struct binder_transaction_data tr;

            if (copy_from_user(&tr, ptr, sizeof(tr)))
                return -EFAULT;
            ptr += sizeof(tr);
            binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
            break;
        }
      ...
      }
      // 更新bwr.write_consumed的值
      *consumed = ptr - buffer;
  }
  return 0;
}

由之前binder_call()分析,writebuf.cmd = BC_TRANSACTION;会执行binder_transaction()函数

static void binder_transaction(struct binder_proc *proc,
                   struct binder_thread *thread,
                   struct binder_transaction_data *tr, int reply)
{
    struct binder_transaction *t;
    struct binder_work *tcomplete;
    size_t *offp, *off_end;
    struct binder_proc *target_proc;
    struct binder_thread *target_thread = NULL;
    struct binder_node *target_node = NULL;
    struct list_head *target_list;
    wait_queue_head_t *target_wait;
    struct binder_transaction *in_reply_to = NULL;
    struct binder_transaction_log_entry *e;
    uint32_t return_error;

    ...
    if (reply) {
    } else {
        if (tr->target.handle) {
            target_node = ref->node;
        } else {
            // 事务目标对象是ServiceManager的binder实体
            // 即,该事务是交给Service Manager来处理的。
            target_node = binder_context_mgr_node;
        }
        // 设置处理事务的目标进程
        target_proc = target_node->proc;
        ...
    }

    if (target_thread) {
        ...
    } else {
        target_list = &target_proc->todo;
        target_wait = &target_proc->wait;
    }
    ...

    // 分配一个待处理的事务t,t是binder事务(binder_transaction对象)
    t = kzalloc(sizeof(*t), GFP_KERNEL);
    // 分配一个待完成的工作tcomplete,tcomplete是binder_work对象。
    tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
    ...

    // 事务将交给target_proc进程进行处理
    t->to_proc = target_proc;
    // 事务将交给target_thread线程进行处理
    t->to_thread = target_thread;
    ...

    // 分配空间,从目的进程映射的空间分配buf
    t->buffer = binder_alloc_buf(target_proc, tr->data_size,
        tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
    ...
    // 保存事务
    t->buffer->transaction = t;
    // 保存事务的目标对象(即处理该事务的binder对象)
    t->buffer->target_node = target_node;

    offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));


    // 将"用户空间的数据"拷贝到内核中
    // tr->data.ptr.buffer就是用户空间数据的起始地址,tr->data_size就是数据大小
    if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) {
        ...
    }
    // 将"用户空间的数据中所含对象的偏移地址"拷贝到内核中
    // tr->data.ptr.offsets就是数据中的对象偏移地址数组,tr->offsets_size就数据中的对象个数
    // 拷贝之后,offp就是flat_binder_object对象数组在内核空间的偏移数组的起始地址
    if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) {
        ...
    }
    ...
    // off_end就是flat_binder_object对象数组在内核空间的偏移地址的结束地址
    off_end = (void *)offp + tr->offsets_size;
    // 将所有的flat_binder_object对象读取出来
    // 对TestServer而言,只有一个flat_binder_object对象。
    for (; offp < off_end; offp++) {
        struct flat_binder_object *fp;
        ...
        fp = (struct flat_binder_object *)(t->buffer->data + *offp);

        switch (fp->type) {
        case BINDER_TYPE_BINDER:
        case BINDER_TYPE_WEAK_BINDER: {
            struct binder_ref *ref;
            // 在proc中查找binder实体对应的binder_node
            struct binder_node *node = binder_get_node(proc, fp->binder);
            // 若找不到,则新建一个binder_node;下次就可以直接使用了。
            if (node == NULL) {
                node = binder_new_node(proc, fp->binder, fp->cookie);
            }
            ...
            // 在target_proc(即,ServiceManager的进程上下文)中查找是否包行"该Binder实体的引用",
            // 如果没有找到的话,则将"该binder实体的引用"添加到target_proc->refs_by_node红黑树中。这样,就可以通过Service Manager对该
Binder实体进行管理了。
            ref = binder_get_ref_for_node(target_proc, node);

            // 现在修改目的进程type,表示ServiceManager持有TestServer引用,TestServer进程才能拥有实体。
            if (fp->type == BINDER_TYPE_BINDER)
                fp->type = BINDER_TYPE_HANDLE;
            else
                fp->type = BINDER_TYPE_WEAK_HANDLE;
            // 修改handle。handle和binder是联合体,这里将handle设为引用的描述。
            // 根据该handle可以找到"该binder实体在target_proc中的binder引用";
            // 即,可以根据该handle,可以从Service Manager找到对应的Binder实体的引用,从而获取Binder实体。
            fp->handle = ref->desc;
            // 增加引用计数,防止"该binder实体"在使用过程中被销毁。
            binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,
                       &thread->todo);
            ...
        } break;
        ...
        }
    }
    if (reply) {
        ..
    } else if (!(t->flags & TF_ONE_WAY)) {
        BUG_ON(t->buffer->async_transaction != 0);
        t->need_reply = 1;
        t->from_parent = thread->transaction_stack;
        // 将当前事务添加到当前线程的事务栈中
        thread->transaction_stack = t;
    } else {
        ...
    }
    // 设置事务的类型为BINDER_WORK_TRANSACTION
    t->work.type = BINDER_WORK_TRANSACTION;
    // 将事务添加到target_list队列中,即target_list的待处理事务中
    list_add_tail(&t->work.entry, target_list);
    // 设置待完成工作的类型为BINDER_WORK_TRANSACTION_COMPLETE
    tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
    // 将待完成工作添加到thread->todo队列中,即当前线程的待完成工作中。
    list_add_tail(&tcomplete->entry, &thread->todo);
    // 唤醒目标进程
    if (target_wait)
        wake_up_interruptible(target_wait);
    return;

    ...
}

说明:这里的tr->target.handle=0,因此,会设置target_node为ServiceManager对应的Binder实体。下面是target_node,target_proc等值初始化之后的值。

target_node = binder_context_mgr_node; // 目标节点为Service Manager对应的Binder实体
target_proc = target_node->proc;       // 目标进程为Service Manager对应的binder_proc进程上下文信息
target_list = &target_thread->todo;    // 待处理事务队列
target_wait = &target_thread->wait;    // 等待队列

小结: 驱动接收到TestServer发送的数据后,驱动主要工作: (1)根据Handle = 0 找到目的进程ServiceManager (2)把数据通过copy_from_user()放到目的进程ServiceManager的空间(mmap) (3)处理offs数据,即解析flat_binder_object结构体 a. 为TestServer构造binder_node node = binder_new_node(proc, fp->binder, fp->cookie); b.构造binder_ref给目的进程ServiceManager ref = binder_get_ref_for_node(target_proc, node); c.增加引用计数TestServer binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE, &thread->todo); 增加引用计数会添加work.entry(BR_INCREFS、BR_ACQUIR)到TestServer todod队列 list_add_tail(&node->work.entry, target_list)

说明:就新建Binder实体的引用,并将其添加到target_proc->refs_by_node红黑树 和 target_proc->refs_by_desc红黑树中。 这样,ServiceManager的进程上下文中就存在Hello Service的Binder引用,ServiceManager也就可以对Hello Service进行管理了!然后,修改fp->type=BINDER_TYPE_HANDLE,并使fp->handle = ref->desc。

(4)新建一个待处理事务t和待完成的工作tcomplete,并对它们进行初始化。待处理事务t会被提交给目标(即ServiceManager对应的Binder实体)进行处理;而待完成的工作tcomplete则是为了反馈给TestServer服务,告诉TestServer它的请求Binder驱动已经收到了。注意,这里仅仅是告诉TestServer该请求已经被收到,而不是处理完毕!待ServiceManager处理完毕该请求之后,Binder驱动会再次反馈相应的消息给TestServer。 (5)binder_thread_write()中执行binder_transaction()后,会更新*consumed的值,即bwr.write_consumed的值 (6)此时,TestServer进程还会继续运行,而且它也通过wake_up_interruptible()唤醒了ServiceManager进程。

// 更新bwr.write_consumed的值
*consumed = ptr - buffer;

接下来,ioctl()会执行binder_thread_read()来设置反馈数据给TestServer进程

static int binder_thread_read(struct binder_proc *proc,
                  struct binder_thread *thread,
                  void  __user *buffer, int size,
                  signed long *consumed, int non_block)
{
    // 如果*consumed=0,则写入BR_NOOP到用户传进来的bwr.read_buffer缓存区
    if (*consumed == 0) {
        if (put_user(BR_NOOP, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);
    }
    ...
    while (1) {
        uint32_t cmd;
        struct binder_transaction_data tr;
        struct binder_work *w;
        struct binder_transaction *t = NULL;

        // 如果当前线程的"待完成工作"不为空,则取出待完成工作。
        if (!list_empty(&thread->todo))
            w = list_first_entry(&thread->todo, struct binder_work, entry);
        }
        ...
        switch (w->type) {
        ...
        case BINDER_WORK_TRANSACTION_COMPLETE: {
            cmd = BR_TRANSACTION_COMPLETE;
            // 将BR_TRANSACTION_COMPLETE写入到用户缓冲空间中
            if (put_user(cmd, (uint32_t __user *)ptr))
                return -EFAULT;
        } break;

    ...
    // 更新bwr.read_consumed的值
    *consumed = ptr - buffer;
}

首先发送BR_NOOP给TestServer,然后处理todo队列,处理完成后会发送BR_TRANSACTION_COMPLETE。

现在内核已经处理完数据,我们从log看看数据发生了哪些变化: 我们发现flat_binder_object结构体的type值发生了变化,binder变成了Handle,看一下结构体,handler 和 binder是一个union,占用同一个位置;Handle为1代表第一个引用,意思是在ServiceManager进程里面根据1能找到第一个binder_ref,根据binder_ref能找到服务hello的binder_node实体。

接下来就等待ServiceManager处理完成后,回复消息。

1.3、唤醒ServiceManager执行添加”hello”服务

前面驱动已经创建好TestServer的binder_node,现在唤醒ServiceManager添加svcinfo 看看ServiceManager被唤醒后,会干些什么。

static int binder_thread_read(struct binder_proc *proc,
                struct binder_thread *thread,
                void  __user *buffer, int size,
                signed long *consumed, int non_block)
{
    ...
    wait_for_proc_work = thread->transaction_stack == NULL &&
                list_empty(&thread->todo);


    while (1) {
        struct binder_transaction_data tr;
        struct binder_work *w;
        struct binder_transaction *t = NULL;

        // 如果当前线程的"待完成工作"不为空,则取出待完成工作。
        if (!list_empty(&thread->todo))
            w = list_first_entry(&thread->todo, struct binder_work, entry);
        else if (!list_empty(&proc->todo) && wait_for_proc_work)
        ...
        switch (w->type) {
            case BINDER_WORK_TRANSACTION: {
                t = container_of(w, struct binder_transaction, work);
            } break;
            ...
        }

        // t->buffer->target_node是目标节点。
        // 这里,addService请求的目标是ServiceManager,因此target_node是ServiceManager对应的节点;
        // 它的值在事务交互时(binder_transaction中),被赋值为ServiceManager对应的Binder实体。  
        if (t->buffer->target_node) {
            // 事务目标对应的Binder实体(即,ServiceManager对应的Binder实体)
            struct binder_node *target_node = t->buffer->target_node;
            // Binder实体在用户空间的地址(ServiceManager的ptr为NULL)
            tr.target.ptr = target_node->ptr;
            // Binder实体在用户空间的其它数据(ServiceManager的cookie为NULL)
            tr.cookie =  target_node->cookie;
            t->saved_priority = task_nice(current);
            if (t->priority < target_node->min_priority &&
                !(t->flags & TF_ONE_WAY))
                binder_set_nice(t->priority);
            else if (!(t->flags & TF_ONE_WAY) ||
                 t->saved_priority > target_node->min_priority)
                binder_set_nice(target_node->min_priority);
            **cmd = BR_TRANSACTION;//将命令改为BR_TRANSACTION
        } else {
            tr.target.ptr = NULL;
            tr.cookie = NULL;
            cmd = BR_REPLY;
        }
        // 交易码
        tr.code = t->code;
        tr.flags = t->flags;
        tr.sender_euid = t->sender_euid;

        if (t->from) {
            struct task_struct *sender = t->from->proc->tsk;
            tr.sender_pid = task_tgid_nr_ns(sender,
                            current->nsproxy->pid_ns);
        } else {
            tr.sender_pid = 0;
        }

        // 数据大小
        tr.data_size = t->buffer->data_size;
        // 数据中对象的偏移数组的大小(即对象的个数)
        tr.offsets_size = t->buffer->offsets_size;
        // 数据
        tr.data.ptr.buffer = (void *)t->buffer->data +
                    proc->user_buffer_offset;
        // 数据中对象的偏移数组
        tr.data.ptr.offsets = tr.data.ptr.buffer +
                    ALIGN(t->buffer->data_size,
                        sizeof(void *));

        // 将cmd指令写入到ptr,即传递到用户空间
        if (put_user(cmd, (uint32_t __user *)ptr))
            return -EFAULT;
        // 将tr数据拷贝到用户空间
        ptr += sizeof(uint32_t);
        if (copy_to_user(ptr, &tr, sizeof(tr)))
            return -EFAULT;
        ptr += sizeof(tr);

        ...
        // 删除已处理的事务
        list_del(&t->work.entry);
        t->buffer->allow_user_free = 1;
        // 设置回复信息
        if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
            // 该事务会发送给Service Manager守护进程进行处理。
            // Service Manager处理之后,还需要给Binder驱动回复处理结果。
            // 这里设置Binder驱动回复信息。
            t->to_parent = thread->transaction_stack;
            // to_thread表示Service Manager反馈后,将反馈结果交给当前thread进行处理
            t->to_thread = thread;
            // transaction_stack交易栈保存当前事务。用于之处反馈是针对哪个事务的。
            thread->transaction_stack = t;
        } else {
            ...
        }
        break;
    }

done:

    // 更新bwr.read_consumed的值
    *consumed = ptr - buffer;

    ...
    return 0;
}

说明:ServiceManager进程在调用wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread))进入等待之后,被TestServer进程唤醒。唤醒之后,binder_has_thread_work()为true,因为ServiceManager的待处理事务队列中有个待处理事务(即,TestServer添加服务的请求)。 (01) 进入while循环后,首先取出待处理事务。 (02) 事务的类型是BINDER_WORK_TRANSACTION,得到对应的binder_transaction*类型指针t之后,跳出switch语句。很显然,此时t不为NULL,因此继续往下执行。下面的工作的目的,是将t中的数据转移到tr中(tr是事务交互数据包结构体binder_transaction_data对应的指针),然后将指令和tr数据都拷贝到用户空间,让ServiceManager读取后进行处理。

Service Manager守护进程在处理完事务之后,需要反馈结果给Binder驱动。因此,接下来会设置t->to_thread和t->transaction_stack等成员。最后,修改*consumed的值,即bwr.read_consumed的值,表示待读取内容的大小。 执行完binder_thread_read()之后,回到binder_ioctl()中,执行copy_to_user()将数据拷贝到用户空间。接下来,就回到了Service Manager的守护进程当中,即回到binder_loop()中。 binder_loop()会将ioctl()反馈的数据发送给binder_parse()进行解析。

int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uintptr_t ptr, size_t size, binder_handler func)
{
        switch(cmd) {
        case BR_NOOP:
        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);
                binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
            }
            ptr += sizeof(*txn);
            break;
        }
}

首先会调用svcmgr_handler()->do_add_service()

int do_add_service(struct binder_state *bs,
                   const uint16_t *s, size_t len,
                   uint32_t handle, uid_t uid, int allow_isolated,
                   pid_t spid)
{
    struct svcinfo *si;
    si = find_svc(s, len);
    if (si) {
        if (si->handle) {
                    svcinfo_death(bs, si);
        }
        si->handle = handle;
    } else {
        si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
        si->handle = handle;
        si->len = len;
        memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
        si->name[len] = '\0';
        si->death.func = (void*) svcinfo_death;
        si->death.ptr = si;
        si->allow_isolated = allow_isolated;
        si->next = svclist;
        svclist = si;
    }

    binder_acquire(bs, handle);
    binder_link_to_death(bs, handle, &si->death);
    return 0;
}

可以看到首先为hello服务新分配一个结构体svcinfo,然后将handle赋值给svcinfo,这也是以后我们查找服务所得到的handle。 然后调动了binder_acquire、binder_link_to_death发送信息给驱动。 [ 38.467270] service_manager (1362, 1362), binder_thread_write : BC_ACQUIRE [ 38.480122] service_manager (1362, 1362), binder_thread_write : BC_REQUEST_DEATH_NOTIFICATION 接着看binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);

void binder_send_reply(struct binder_state *bs,
                       struct binder_io *reply,
                       binder_uintptr_t buffer_to_free,
                       int status)
{
    data.cmd_free = BC_FREE_BUFFER;
    data.buffer = buffer_to_free;
    data.cmd_reply = BC_REPLY;
    data.txn.target.ptr = 0;
    data.txn.cookie = 0;
    data.txn.code = 0;
    ...
    binder_write(bs, &data, sizeof(data));
}

可以看到有BC_FREE_BUFFER、BC_REPLY,通过binder_write(bs, &data, sizeof(data))回复BC_REPLY到驱动。

驱动处理消息跟之前流程类似,这里不再分析。简单总结: 1、驱动接收到BC_REPLY请求,会新建一个待处理事务t(TestServer处理)和待完成的工作tcomplete(service_manager处理) 2、然后唤醒TestServer处理BC_REPLY请求 至此,已经成功添加Hello Service svcmgr: add_service(‘hello’), handle = 1

(2)、Binder系统驱动情景分析–TestClent获取”Hello”服务过程

2.0、构造数据

2.1、发送数据给ServiceManager bwr初始化完成之后,调用ioctl(,BINDER_WRITE_READ,)和Binder驱动进行交互。

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
  int ret;
  struct binder_proc *proc = filp->private_data;
  struct binder_thread *thread;
  unsigned int size = _IOC_SIZE(cmd);
  void __user *ubuf = (void __user *)arg;
  switch (cmd) {
  case BINDER_WRITE_READ: {
      struct binder_write_read bwr;
      // 将binder_write_read从"用户空间" 拷贝到 "内核空间"
      if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
      }

      // 如果write_size>0,则进行写操作
      if (bwr.write_size > 0) {
          ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
      }

      // 如果read_size>0,则进行读操作
      if (bwr.read_size > 0) {
          ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags   & O_NONBLOCK);

      }
      if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
          ret = -EFAULT;
          goto err;
      }
      break;
  }
}

首先,会将binder_write_read从用户空间拷贝到内核空间之后。拷贝之后,读取出来的bwr.write_size和bwr.read_size都>0,因此先写后读。即,先执行binder_thread_write(),然后执行binder_thread_read()。

2.2、binder_thread_write()处理数据

int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
          void __user *buffer, int size, signed long *consumed)
{
  uint32_t cmd;
  void __user *ptr = buffer + *consumed;
  void __user *end = buffer + size;

  // 读取binder_write_read.write_buffer中的内容。
  // 每次读取32bit(即4个字节)
  while (ptr < end && thread->return_error == BR_OK) {
      // 从用户空间读取32bit到内核中,并赋值给cmd。
      if (get_user(cmd, (uint32_t __user *)ptr))
          return -EFAULT;

      ptr += sizeof(uint32_t);
      ...
      switch (cmd) {
      ...
        case BC_TRANSACTION:
        case BC_REPLY: {
            struct binder_transaction_data tr;

            if (copy_from_user(&tr, ptr, sizeof(tr)))
                return -EFAULT;
            ptr += sizeof(tr);
            binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
            break;
        }
      ...
      }
      // 更新bwr.write_consumed的值
      *consumed = ptr - buffer;
  }
  return 0;
}

说明:MediaPlayer发送的指令是BC_TRANSACTION,这里只关心与BC_TRANSACTION相关的部分。在通过copy_from_user()将数据拷贝从用户空间拷贝到内核空间之后,就调用binder_transaction()进行处理。

2.3、Binder驱动中binder_transaction()的源码

static void binder_transaction(struct binder_proc *proc,
                   struct binder_thread *thread,
                   struct binder_transaction_data *tr, int reply)
{
    struct binder_transaction *t;
    struct binder_work *tcomplete;
    size_t *offp, *off_end;
    struct binder_proc *target_proc;
    struct binder_thread *target_thread = NULL;
    struct binder_node *target_node = NULL;
    struct list_head *target_list;
    wait_queue_head_t *target_wait;
    struct binder_transaction *in_reply_to = NULL;
    struct binder_transaction_log_entry *e;
    uint32_t return_error;

    ...

    if (reply) {
        ...
    } else {
        if (tr->target.handle) {
            ...
        } else {
            // 该getService是从ServiceManager中获取MediaPlayer;
            // 因此事务目标对象是ServiceManager的binder实体。
            target_node = binder_context_mgr_node;
            ...
        }
        ...
        // 设置处理事务的目标进程
        target_proc = target_node->proc;
        ...
    }

    if (target_thread) {
        ...
    } else {
        target_list = &target_proc->todo;
        target_wait = &target_proc->wait;
    }
    ...

    // 分配一个待处理的事务t,t是binder事务(binder_transaction对象)
    t = kzalloc(sizeof(*t), GFP_KERNEL);
    ...

    // 分配一个待完成的工作tcomplete,tcomplete是binder_work对象。
    tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
    ...

    t->debug_id = ++binder_last_id;
    ...

    // 设置from,表示该事务是MediaPlayer线程发起的
    if (!reply && !(tr->flags & TF_ONE_WAY))
        t->from = thread;
    else
        t->from = NULL;
    // 下面的一些赋值是初始化事务t
    t->sender_euid = proc->tsk->cred->euid;
    // 事务将交给target_proc进程进行处理
    t->to_proc = target_proc;
    // 事务将交给target_thread线程进行处理
    t->to_thread = target_thread;
    // 事务编码
    t->code = tr->code;
    // 事务标志
    t->flags = tr->flags;
    // 事务优先级
    t->priority = task_nice(current);

    ...

    // 分配空间
    t->buffer = binder_alloc_buf(target_proc, tr->data_size,
        tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
    ...
    t->buffer->allow_user_free = 0;
    t->buffer->debug_id = t->debug_id;
    // 保存事务
    t->buffer->transaction = t;
    // 保存事务的目标对象(即处理该事务的binder对象)
    t->buffer->target_node = target_node;
    trace_binder_transaction_alloc_buf(t->buffer);
    if (target_node)
        binder_inc_node(target_node, 1, 0, NULL);

    offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));

    // 将"用户空间的数据"拷贝到内核中
    // tr->data.ptr.buffer就是用户空间数据的起始地址,tr->data_size就是数据大小
    if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) {
        ...
    }
    // 将"用户空间的数据中所含对象的偏移地址"拷贝到内核中
    // MediaPlayer中不包含对象, offp=null
    if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) {
        ...
    }
    ...
    // MediaPlayer中不包含对象, off_end为null
    off_end = (void *)offp + tr->offsets_size;
    // MediaPlayer中不包含对象, offp=off_end
    for (; offp < off_end; offp++) {
        ...
    }
    if (reply) {
        ..
    } else if (!(t->flags & TF_ONE_WAY)) {
        BUG_ON(t->buffer->async_transaction != 0);
        t->need_reply = 1;
        t->from_parent = thread->transaction_stack;
        // 将当前事务添加到当前线程的事务栈中
        thread->transaction_stack = t;
    } else {
        ...
    }
    // 设置事务的类型为BINDER_WORK_TRANSACTION
    t->work.type = BINDER_WORK_TRANSACTION;
    // 将事务添加到target_list队列中,即target_list的待处理事务中
    list_add_tail(&t->work.entry, target_list);
    // 设置待完成工作的类型为BINDER_WORK_TRANSACTION_COMPLETE
    tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
    // 将待完成工作添加到thread->todo队列中,即当前线程的待完成工作中。
    list_add_tail(&tcomplete->entry, &thread->todo);
    // 唤醒目标进程
    if (target_wait)
        wake_up_interruptible(target_wait);
    return;

    ...
}

说明:参数reply=0,表明这是个请求事务,而不是反馈。binder_transaction新建会新建”一个待处理事务t”和”待完成的工作tcomplete”,并根据请求的数据对它们进行初始化。 (01) TestClent的getService请求是提交给ServiceManager进行处理的,因此,”待处理事务t”会被添加到ServiceManager的待处理事务队列中。此时的target_thread是ServiceManager对应的线程,而target_proc则是ServiceManager对应的进程上下文环境。 (02) 此时,Binder驱动已经收到了TestClent的getService请求;于是,将一个BINDER_WORK_TRANSACTION_COMPLETE类型的”待完成工作tcomplete”添加到当前线程(即,TestClent线程)的待处理事务队列中。目的是告诉TestClent,Binder驱动已经收到它的getService请求了。 (03) 最后,调用wake_up_interruptible(target_wait)将ServiceManager唤醒。

接下来,还是先分析完TestClent线程,再看ServiceManager被唤醒后做了些什么。

binder_transaction()执行完毕之后,就会返回到binder_thread_write()中。binder_thread_write()更新bwr.write_consumed的值后,就返回到binder_ioctl()继续执行”读”动作。即执行binder_thread_read()。

2.4、Binder驱动中binder_thread_read()的源码

static int binder_thread_read(struct binder_proc *proc,
                  struct binder_thread *thread,
                  void  __user *buffer, int size,
                  signed long *consumed, int non_block)
{
    void __user *ptr = buffer + *consumed;
    void __user *end = buffer + size;

    int ret = 0;
    int wait_for_proc_work;

    // 如果*consumed=0,则写入BR_NOOP到用户传进来的bwr.read_buffer缓存区
    if (*consumed == 0) {
        if (put_user(BR_NOOP, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);
    }

retry:
    // 等待proc进程的事务标记。
    // 当线程的事务栈为空 并且 待处理事务队列为空时,该标记位true。
    wait_for_proc_work = thread->transaction_stack == NULL &&
                list_empty(&thread->todo);

    ...

    if (wait_for_proc_work) {
        ...
    } else {
        if (non_block) {
            ...
        } else
            ret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread));
    }

    ...

    while (1) {
        uint32_t cmd;
        struct binder_transaction_data tr;
        struct binder_work *w;
        struct binder_transaction *t = NULL;

        // 如果当前线程的"待完成工作"不为空,则取出待完成工作。
        if (!list_empty(&thread->todo))
            w = list_first_entry(&thread->todo, struct binder_work, entry);
        else if (!list_empty(&proc->todo) && wait_for_proc_work)
            ...
        else {
            if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) /* no data added */
                goto retry;
            break;
        }

        ...

        switch (w->type) {
        ...
        case BINDER_WORK_TRANSACTION_COMPLETE: {
            cmd = BR_TRANSACTION_COMPLETE;
            // 将BR_TRANSACTION_COMPLETE写入到用户缓冲空间中
            if (put_user(cmd, (uint32_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(uint32_t);

            ...
            // 待完成事务已经处理完毕,将其从待完成事务队列中删除。
            list_del(&w->entry);
            kfree(w);
            binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
        } break;
        ...
        }

        if (!t)
            continue;

        ...
    }

    ...
    // 更新bwr.read_consumed的值
    *consumed = ptr - buffer;

    ...
    return 0;
}

说明: (01) bwr.read_consumed=0,即if (*consumed == 0)为true。因此,会将BR_NOOP写入到bwr.read_buffer中。 (02) thread->transaction_stack不为空,thread->todo也不为空。因为,前面在binder_transaction()中有将一个BINDER_WORK_TRANSACTION_COMPLETE类型的待完成工作添加到thread的待完成工作队列中。因此,wait_for_proc_work为false。 (03) binder_has_thread_work(thread)为true。因此,在调用wait_event_interruptible()时,不会进入等待状态,而是继续运行。 (04) 进入while循环后,通过list_first_entry()取出待完成工作w。w的类型w->type=BINDER_WORK_TRANSACTION_COMPLETE,进入到对应的switch分支。随后,将BR_TRANSACTION_COMPLETE写入到bwr.read_buffer中。此时,待处理工作已经完成,将其从当前线程的待处理工作队列中删除。 (05) 最后,更新bwr.read_consumed的值。

经过binder_thread_read()处理之后,bwr.read_buffer中包含了两个指令:BR_NOOP和BR_TRANSACTION_COMPLETE。

2.5、ServiceManager处理getService请求

下面看看ServiceManager被唤醒之后,是如何处理getService请求的

int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uint32_t *ptr, uint32_t size, binder_handler func)
{
    while (ptr < end) {
        case BR_TRANSACTION: {
            struct binder_txn *txn = (void *) ptr;
            ...
            if (func) {
                unsigned rdata[256/4];
                struct binder_io msg;   // 用于保存"Binder驱动反馈的信息"
                struct binder_io reply; // 用来保存"回复给Binder驱动的信息"
                int res;

                // 初始化reply
                bio_init(&reply, rdata, sizeof(rdata), 4);
                // 根据txt(Binder驱动反馈的信息)初始化msg
                bio_init_from_txn(&msg, txn);
                // 消息处理
                res = func(bs, txn, &msg, &reply);
                // 反馈消息给Binder驱动。
                binder_send_reply(bs, &reply, txn->data, res);
}

binder_send_reply(bs, &reply, txn->data, res);->binder_write()

int binder_write(struct binder_state *bs, void *data, unsigned len)
{
    struct binder_write_read bwr;
    int res;
    bwr.write_size = len;
    bwr.write_consumed = 0;
    bwr.write_buffer = (unsigned) data;
    bwr.read_size = 0;
    bwr.read_consumed = 0;
    bwr.read_buffer = 0;
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
    if (res < 0) {
        fprintf(stderr,"binder_write: ioctl failed (%s)\n",
                strerror(errno));
    }
    return res;
}

说明:binder_write()会将数据封装到binder_write_read的变量bwr中;其中,bwr.read_size=0,而bwr.write_size>0。接着,便通过ioctl(,BINDER_WRITE_READ,)和Binder驱动交互,将数据反馈给Binder驱动。

再次回到Binder驱动的binder_ioctl()对应的BINDER_WRITE_READ分支中。此时,由于bwr.read_size=0,而bwr.write_size>0;因此,Binder驱动只调用binder_thread_write进行写操作,而不会进行读。

返回数据:

handle = 1 代表第一个

2.6、Binder驱动中处理ServiceManager返回数据

int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
          void __user *buffer, int size, signed long *consumed)
{
  uint32_t cmd;
  void __user *ptr = buffer + *consumed;
  void __user *end = buffer + size;

  // 读取binder_write_read.write_buffer中的内容。
  // 每次读取32bit(即4个字节)
  while (ptr < end && thread->return_error == BR_OK) {
      // 从用户空间读取32bit到内核中,并赋值给cmd。
      if (get_user(cmd, (uint32_t __user *)ptr))
          return -EFAULT;

      ptr += sizeof(uint32_t);
      ...
      switch (cmd) {
        case BC_FREE_BUFFER:
        ...
        case BC_TRANSACTION:
        case BC_REPLY: {
            struct binder_transaction_data tr;

            if (copy_from_user(&tr, ptr, sizeof(tr)))
                return -EFAULT;
            ptr += sizeof(tr);
            binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
            break;
        }
      ...
      }
      // 更新bwr.write_consumed的值
      *consumed = ptr - buffer;
  }
  return 0;
}

binder_thread_write()进入BC_REPLY之后,会将数据拷贝到内核空间,然后调用binder_transaction()进行处理

static void binder_transaction(struct binder_proc *proc,
                   struct binder_thread *thread,
                   struct binder_transaction_data *tr, int reply)
{
    struct binder_transaction *t;
    struct binder_work *tcomplete;
    size_t *offp, *off_end;
    struct binder_proc *target_proc;
    struct binder_thread *target_thread = NULL;
    struct binder_node *target_node = NULL;
    struct list_head *target_list;
    wait_queue_head_t *target_wait;
    struct binder_transaction *in_reply_to = NULL;
    struct binder_transaction_log_entry *e;
    uint32_t return_error;

    ...

    if (reply) {
        // 事务栈
        in_reply_to = thread->transaction_stack;
        ...
        // 设置优先级
        binder_set_nice(in_reply_to->saved_priority);
        ...
        thread->transaction_stack = in_reply_to->to_parent;
        // 发起请求的线程,即MediaPlayer所在线程。
        // from的值,是MediaPlayer发起请求时在binder_transaction()中赋值的。
        target_thread = in_reply_to->from;
        ...
        // MediaPlayer对应的进程
        target_proc = target_thread->proc;
    } else {
        ...
    }
    if (target_thread) {
        e->to_thread = target_thread->pid;
        target_list = &target_thread->todo;
        target_wait = &target_thread->wait;
    } else {
        ...
    }
    e->to_proc = target_proc->pid;

    // 分配一个待处理的事务t,t是binder事务(binder_transaction对象)
    t = kzalloc(sizeof(*t), GFP_KERNEL);
    if (t == NULL) {
        return_error = BR_FAILED_REPLY;
        goto err_alloc_t_failed;
    }

    // 分配一个待完成的工作tcomplete,tcomplete是binder_work对象。
    tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
    if (tcomplete == NULL) {
        return_error = BR_FAILED_REPLY;
        goto err_alloc_tcomplete_failed;
    }
    binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE);

    t->debug_id = ++binder_last_id;
    e->debug_id = t->debug_id;

    if (!reply && !(tr->flags & TF_ONE_WAY))
        t->from = thread;
    else
        t->from = NULL;
    // 下面的一些赋值是初始化事务t
    t->sender_euid = proc->tsk->cred->euid;
    // 事务将交给target_proc进程进行处理
    t->to_proc = target_proc;
    // 事务将交给target_thread线程进行处理
    t->to_thread = target_thread;
    // 事务编码
    t->code = tr->code;
    // 事务标志
    t->flags = tr->flags;
    // 事务优先级
    t->priority = task_nice(current);

    // 分配空间
    t->buffer = binder_alloc_buf(target_proc, tr->data_size,
        tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
    if (t->buffer == NULL) {
        return_error = BR_FAILED_REPLY;
        goto err_binder_alloc_buf_failed;
    }
    t->buffer->allow_user_free = 0;
    t->buffer->debug_id = t->debug_id;
    // 保存事务
    t->buffer->transaction = t;
    // target_node为NULL
    t->buffer->target_node = target_node;
    trace_binder_transaction_alloc_buf(t->buffer);
    if (target_node)
        binder_inc_node(target_node, 1, 0, NULL);

    offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));

    // 将"用户传入的数据"保存到事务中
    if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) {
        ...
    }
    // 将"用户传入的数据偏移地址"保存到事务中
    if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) {
        ...
    }

    ...
    off_end = (void *)offp + tr->offsets_size;
    // 将flat_binder_object对象读取出来,
    // 这里就是Service Manager中反馈的MediaPlayerService对象。
    for (; offp < off_end; offp++) {
        struct flat_binder_object *fp;
        ...
        fp = (struct flat_binder_object *)(t->buffer->data + *offp);

        switch (fp->type) {
            ...
            case BINDER_TYPE_HANDLE:
            case BINDER_TYPE_WEAK_HANDLE: {
                // 根据handle获取对应的Binder引用,即得到MediaPlayerService的Binder引用
                struct binder_ref *ref = binder_get_ref(proc, fp->handle);
                if (ref == NULL) {
                    ...
                }
                // ref->node->proc是MediaPlayerService的进程上下文环境,
                // 而target_proc是MediaPlayer的进程上下文环境
                if (ref->node->proc == target_proc) {
                    ...
                } else {
                    struct binder_ref *new_ref;
                    // 在MediaPlayer进程中引用"MediaPlayerService"。
                    // 表现为,执行binder_get_ref_for_node()会,会先在MediaPlayer进程中查找是否存在MediaPlayerService对应的Binder引用;
                    // 很显然是不存在的。于是,并新建MediaPlayerService对应的Binder引用,并将其添加到MediaPlayer的Binder引用红黑树中。
                    new_ref = binder_get_ref_for_node(target_proc, ref->node);
                    if (new_ref == NULL) {
                        ...
                    }
                    // 将new_ref的引用描述复制给fp->handle。
                    fp->handle = new_ref->desc;
                    binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL);
                    ...
                }
            } break;

        }
    }

    if (reply) {
        binder_pop_transaction(target_thread, in_reply_to);
    } else if (!(t->flags & TF_ONE_WAY)) {
        ...
    } else {
        ...
    }
    // 设置事务的类型为BINDER_WORK_TRANSACTION
    t->work.type = BINDER_WORK_TRANSACTION;
    // 将事务添加到target_list队列中,即target_list的待处理事务中
    list_add_tail(&t->work.entry, target_list);
    // 设置待完成工作的类型为BINDER_WORK_TRANSACTION_COMPLETE
    tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
    // 将待完成工作添加到thread->todo队列中,即当前线程的待完成工作中。
    list_add_tail(&tcomplete->entry, &thread->todo);
    // 唤醒目标进程
    if (target_wait)
        wake_up_interruptible(target_wait);
    return;
    ...
}

说明:reply=1,这里只关注reply部分。 (01) 此反馈最终是要回复给TestClient的。因此,target_thread被赋值为TestServer所在的线程,target_proc则是TestClient对应的进程,target_node为null。 (02) 这里,先看看for循环里面的内容,取出BR_REPLY指令所发送的数据,然后获取数据中的flat_binder_object变量fp。因为fp->type为BINDER_TYPE_HANDLE,因此进入BINDER_TYPE_HANDLE对应的分支。接着,通过binder_get_ref()获取Hello Service对应的Binder引用;很明显,能够正常获取到Hello Service的Binder引用。因为在Hello Service调用addService请求时,已经创建了它的Binder引用。 binder_get_ref_for_node()的作用是在TestClent进程上下文中添加”TestServer对应的Binder引用”。这样,后面就可以根据该Binder引用一步步的获取TestServer对象。 最后,将Binder引用的描述赋值给fp->handle。 (03) 此时,Service Manager已经处理了getService请求。便调用binder_pop_transaction(target_thread, in_reply_to)将事务从”target_thread的事务栈”中删除,即从MediaPlayer线程的事务栈中删除该事务。 (04) 新建的”待处理事务t”的type为设为BINDER_WORK_TRANSACTION后,会被添加到MediaPlayer的待处理事务队列中。 (05) 此时,Service Manager已经处理了getService请求,而Binder驱动在等待它的回复。于是,将一个BINDER_WORK_TRANSACTION_COMPLETE类型的”待完成工作tcomplete”(作为回复)添加到当前线程(即,Service Manager线程)的待处理事务队列中。 (06) 最后,调用wake_up_interruptible()唤醒TestServer。TestServer被唤醒后,会对事务BINDER_WORK_TRANSACTION进行处理。

OK,到现在为止,还有两个待处理事务:(01) ServiceManager待处理事务列表中有个BINDER_WORK_TRANSACTION_COMPLETE类型的事务 (02) TestServer待处理事务列表中有个BINDER_WORK_TRANSACTION事务。

2.7. Testclient获取handle

(3)、Binder系统驱动情景分析–TestClent使用”Hello”服务过程 构造数据发送数据”weidongshan”