Android Binder 学习中几个问题

  最近一段时间一直在学习Android 的binder通信,期间看了许多相关的Android书籍和博客,对其Android的跨进程通信原理也有了比较清楚的认识,但是总有些觉得不能把他们串联起来。直到现在把源码对照看了一次后才有点恍然大悟的感觉。

  这里我就不详细的去介绍binder了,只记录一下我遇到的问题,其他的具体内容可参阅参考中的两位大神的文章。

  1、Binder通信中Server和Client是如何获取Service Manager。

  在罗升阳老师的《浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路》中,详细描述了Service Manager的接口获取流程。我这里需要强调一点的是:获取Service Manager接口的client或server只是在自己进程中

gDefaultServiceManager = new BpServiceManager(new BpBinder(0)),  0为binder的编号,这个过程与binder驱动或者service manager一点关系都没有,后续就会用这个编号0与binder驱动通信,binder驱动发现通信的target编号为0就会自动找到之前注册的Service Manager的binder node,然后与它通信。

  2、Binder的编号是怎样产生的,然后怎样根据编号找到对应的进程并与之通信的。

  (1)、client使用编号0与Service Manager通信过程。

    这里我们就来看看client怎样使用编号为0的binder与Service Manager通信,这里以add_service为例。

我们首先到BpBinder::transact函数中去看看:

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;
        return status;
    }

    return DEAD_OBJECT;
}

    注意,这里的mHandle为0,code为ADD_SERVICE_TRANSACTION。ADD_SERVICE_TRANSACTION是上面以参数形式传进来的,那mHandle为什么是0呢?因为这里表示的是Service Manager远程接口,它的句柄值一定是0。这个0是怎么保存并传到binder中去的呢?

    再进入到IPCThreadState::transact函数,看看做了些什么事情:

    

status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    ......
    if (err == NO_ERROR) {
        LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
            (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    }
    ......  

   if (reply) {
     err = waitForResponse(reply);
   } else {
     Parcel fakeReply;
     err = waitForResponse(&fakeReply);
   }

   ......

}

    handle继续被传到writeTranscationData方法中,我们继续往下看:

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
    int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
    binder_transaction_data tr;

    tr.target.handle = handle;
    tr.code = code;
    tr.flags = binderFlags;
    .......
}

    可以看到handle为0并被保存到binder_transaction_data结构中的target.handle中。其中target是一个union,具体可参见binder_transaction_data的定义。

    再回到IPCThreadState::transact中,继续执行waitForResponse,waitForResponse中通过talkWithDriver()与binder驱动交互,具体代码:

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
     ......
#if defined(HAVE_ANDROID_OS)
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
#else
        err = INVALID_OPERATION;
#endif
     ......
}

    ioctl直接操作的binder驱动文件,句柄为mProcess->mDriverFD(在ProcessState中打开,每个进程中只打开一次)。bwr中包含我们传给binder驱动的数据,其中就有handle 0。

    继续到binder驱动中对ioctl的处理,我们只关注cmd为BINDER_WRITE_READ的逻辑:

    

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    ......
    switch (cmd) {
    case BINDER_WRITE_READ: {
        struct binder_write_read bwr;
        if (size != sizeof(struct binder_write_read)) {
            ret = -EINVAL;
            goto err;
        }
        if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
            ret = -EFAULT;
            goto err;
        }
        if (binder_debug_mask & BINDER_DEBUG_READ_WRITE)
            printk(KERN_INFO "binder: %d:%d write %ld at %08lx, read %ld at %08lx\n",
            proc->pid, thread->pid, bwr.write_size, bwr.write_buffer, bwr.read_size, bwr.read_buffer);
        if (bwr.write_size > 0) {
            ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
            if (ret < 0) {
                bwr.read_consumed = 0;
                if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
                    ret = -EFAULT;
                goto err;
            }
        }
        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 (!list_empty(&proc->todo))
                wake_up_interruptible(&proc->wait);
            if (ret < 0) {
                if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
                    ret = -EFAULT;
                goto err;
            }
        }
        if (binder_debug_mask & BINDER_DEBUG_READ_WRITE)
            printk(KERN_INFO "binder: %d:%d wrote %ld of %ld, read return %ld of %ld\n",
            proc->pid, thread->pid, bwr.write_consumed, bwr.write_size, bwr.read_consumed, bwr.read_size);
        if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
            ret = -EFAULT;
            goto err;
        }
        break;
    }
    ......
    }
    ret = 0;
err:
    ......
    return ret;
}

    这里我们的bwr.write_size > 0,继续执行到binder_thread_write中,我们只关注BC_TRANSACTION部分的逻辑:

    

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;

    while (ptr < end && thread->return_error == BR_OK) {
        if (get_user(cmd, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);
        if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
            binder_stats.bc[_IOC_NR(cmd)]++;
            proc->stats.bc[_IOC_NR(cmd)]++;
            thread->stats.bc[_IOC_NR(cmd)]++;
        }
        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;
        }
        ......
        }
        *consumed = ptr - buffer;
    }
    return 0;
}

    首先将用户传进来的transact参数拷贝在本地变量struct binder_transaction_data tr中去,接着调用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 {
            target_node = binder_context_mgr_node;
            if (target_node == NULL) {
                return_error = BR_DEAD_REPLY;
                goto err_no_context_mgr_node;
            }
        }
        ......
        target_proc = target_node->proc;
        if (target_proc == NULL) {
            return_error = BR_DEAD_REPLY;
            goto err_dead_binder;
        }
        ......
    }
    if (target_thread) {
        ......
    } else {
        target_list = &target_proc->todo;
        target_wait = &target_proc->wait;
    }

    ......

    /* TODO: reuse incoming transaction for reply */
    t = kzalloc(sizeof(*t), GFP_KERNEL);
    if (t == NULL) {
        return_error = BR_FAILED_REPLY;
        goto err_alloc_t_failed;
    }
    ......

    tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
    if (tcomplete == NULL) {
        return_error = BR_FAILED_REPLY;
        goto err_alloc_tcomplete_failed;
    }

    ......

    if (!reply && !(tr->flags & TF_ONE_WAY))
        t->from = thread;
    else
        t->from = NULL;
    t->sender_euid = proc->tsk->cred->euid;
    t->to_proc = target_proc;
    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;
    t->buffer->target_node = target_node;
    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)) {
        ......
        return_error = BR_FAILED_REPLY;
        goto err_copy_data_failed;
    }
    if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) {
        ......
        return_error = BR_FAILED_REPLY;
        goto err_copy_data_failed;
    }
    ......

    off_end = (void *)offp + tr->offsets_size;
    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;
            struct binder_node *node = binder_get_node(proc, fp->binder);
            if (node == NULL) {
                node = binder_new_node(proc, fp->binder, fp->cookie);
                if (node == NULL) {
                    return_error = BR_FAILED_REPLY;
                    goto err_binder_new_node_failed;
                }
                node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
                node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
            }
            if (fp->cookie != node->cookie) {
                ......
                goto err_binder_get_ref_for_node_failed;
            }
            ref = binder_get_ref_for_node(target_proc, node);
            if (ref == NULL) {
                return_error = BR_FAILED_REPLY;
                goto err_binder_get_ref_for_node_failed;
            }
            if (fp->type == BINDER_TYPE_BINDER)
                fp->type = BINDER_TYPE_HANDLE;
            else
                fp->type = BINDER_TYPE_WEAK_HANDLE;
            fp->handle = ref->desc;
            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 {
        ......
    }
    t->work.type = BINDER_WORK_TRANSACTION;
    list_add_tail(&t->work.entry, target_list);
    tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
    list_add_tail(&tcomplete->entry, &thread->todo);
    if (target_wait)
        wake_up_interruptible(target_wait);
    return;
    ......
}

    注意到其中的if (tr->target.handle) 这个判断,还记得这里保存的是什么吗?不错,这里保存的就是Service Manager 的BpBinder中的handle,为0!然后就执行else中代码:target_node = binder_context_mgr_node; binder_context_mgr_node是Sevice Manager申明自己为服务管理者时创建的。并且本方法后续分别会找到target_proc、target_thread、target_node、target_list和target_wait, 他们的值分别为:

target_node = binder_context_mgr_node;
target_proc = target_node->proc;
target_list = &target_proc->todo;
target_wait = &target_proc->wait; 

    这样就找到了为编号为 0 的 binder对应的进程,然后把它唤醒就可以与之通信了,具体细节就不再说了。

  (2)、其他binder编号的产生和查询对应的进程。

    待续

参考:

http://blog.csdn.net/Luoshengyang/article/list/3

http://www.cnblogs.com/innost/archive/2011/01/09/1931456.html

时间: 2024-10-25 04:43:54

Android Binder 学习中几个问题的相关文章

Android binder学习一:主要概念

要看得懂android代码,首先要了解binder机制.binder机制也是android里面比較难以理解的一块,这里记录一下binder的重要概念以及实现.作为备忘. 部分内容来源于网上,如有侵权.请及时告知. 1.binder通信机制概述 binder通信是一种client-server的通信结构, 1.从表面上来看.是client通过获得一个server的代理接口,对server进行直接调用: 2.实际上,代理接口中定义的方法与server中定义的方法是一一相应的. 3.client调用某

Android Binder机制中的异步回调

“Binder通信是同步而不是异步的”,但是在实际使用时,是设计成客户端同步而服务端异步. 看看Framwork层的各service类java源码便会知道,在客户端调用服务端的各种方法时,通常会传递一个Binder过来,该Binder对象用于服务端做异步回调,而服务端本身会使用handler或队列的方式做成异步处理.在Android中,系统service是作为"管理者"的身份存在的,像Ams(ActivityManagerService),它并不创建Activity,创建Activit

Android Binder驱动中的基础数据结构整理

最近突然看到一个博客,是讲Binder原理的,以前看深入理解Android I的时候看过,那时候就看不懂.感觉这个还有点意思,就看了好几天,发现越看越不懂,然后看老罗的博客,发现也不是太懂,现在想根据书上的东西好好梳理下Binder. 感觉里面应该重点掌握的应是bind_node是注册的服务在内核中的代表,bind_ref是客户端连接在驱动的代表.bind_buffer内核缓冲区,通过mmap之后可以在用户空间操作,然后在内核也可以访问,bind_proc是进程的代表,客户端和服务端都有,对上面

Android FM学习中的模块 FM启动过程

最近的研究FM模,FM是一家值我正在学习模块.什么可以从上层中可以看出. 上层是FM按钮的操作和界面显示,因此调用到FM来实现广播收听的功能. 看看Fm启动流程:例如以下图: 先进入FMRadio.java类,onCreate初始化一些数据.画出FM界面.启动fm在onStart()方法里启动FMRadioService.java (调用bindToService(this, osc)方法). watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdGZzbG

(原创)Android Binder设计与实现 - 实现篇(1)

本文属于原创作品,转载请注明出处并放于明显位置,原文地址:http://www.cnblogs.com/albert1017/p/3849585.html 前言 在学习Android的Binder机制时,看了http://blog.csdn.net/universus/article/details/6211589这篇文章(读本文前最好读一下),觉得写得非常棒,可惜只有设计篇,都几年了还没有实现篇,就想尝试完成这个工作,虽然可能没有universus写得那么好,但也希望能对同在学习Android

Android开发学习一些建议【I】

[笔者自我介绍:03年开始进入IT开发领域,05年涉足IT职业培训,08年-12年先后在中软国际及诺亚舟教育两家上市公司任架构及PM,12年继续从事职业教育,目前选择了中国最大的在线教育潭州教育进行共同创业]越来越多的朋友选择了移动互联网应用开发这条道路,既可以选择去谋得一份待遇不错的职业,有可以选择成为一名独立应用开发者,但是很多的朋友们,是从0开始了,往往就会出现一种一筹莫展的感觉,今天就和大家来分享下,想学android,准确来说学好android的一些建议:一.我们都知道android一

Android Binder设计与实现篇

摘要 Binder是Android系统进程间通信(IPC)方式之一.Linux已经拥有管道,system V IPC,socket等IPC手段,却还要倚赖Binder来实现进程间通信,说明Binder具有无可比拟的优势.深入了解Binder并将之与传统IPC做对比有助于我们深入领会进程间通信的实现和性能优化.本文将对Binder的设计细节做一个全面的阐述,首先通过介绍Binder通信模型和Binder通信协议了解Binder的设计需求:然后分别阐述Binder在系统不同部分的表述方式和起的作用:

【转】Android开发学习笔记(一)——初识Android

对于一名程序员来说,“自顶向下”虽然是一种最普通不过的分析问题和解决问题的方式,但其却是简单且较为有效的一种.所以,将其应用到Android的学习中来,不至于将自己的冲动演变为一种盲目和不知所措. 根据“自顶向下”.“从整体到局部”的思想,我的基本学习思路是: 从全局入手,宏观的了解Android平台的架构 了解基于Android平台的应用程序的运行原理 了解环境的搭建,为后面结合实践来学习细节做准备 根据平台架构,从上层到下层,一层一层的了解其相关的具体内容,并通过实践来强化学习 市面上关于A

Android Binder机制(2) ContextManager注册过程分析

引言 Context Manager对应的进程为servicemanager,它先于Service Server与服务客户端运行,首先进入接收IPC数据的状态,处理来自Service Server或服务客户端的请求.在init.rc脚本文件中也可以看到Context Manager在mediaserver与system_server之前运行了. 每当Service Server注册服务时,Context Manager都会把服务的名称与Binder节点编号注册到自身的服务目录中,该服务目录通过根