binder学习笔记

  • 学习资料
    1.Android Bander设计与实现 - 设计篇 :强烈推荐此文,对binder整体设计讲得比较清楚,十分容易读懂。
    2.红茶一杯话Binder:强烈推荐此文,这是一个系列,对binder驱动的具体实现做了详细分析。全文读下来基本对binder就了解得差不多了。
    3.《深入理解Android:卷1》中的binder章节:以MediaPlayerService为例分析利用binder进行ipc的流程。
    4.Android源码:看源码什么的最开心了。

  • 自问自答
    本着带着问题去学习的精神,下文会记录在学习过程中遇到的问题,并尝试用自己的理解作出解答。

    1.server或者client是使用何种方式与binder驱动跨进程通信的?
    答:通过ioctl函数向binder驱动发送指令和数据,ioctl是阻塞式的,当binder驱动处理完后,结果也将由ioctl中的参数返回。

    2.在binder.c的源码中(如binder_ioctl_write_read),copy_from_user和copy_to_user经常是成对出现的,binder的一次拷贝优势体现在哪?
    答:以BINDER_WRITE_READ指令为例,在用户空间和内核空间之间传输的数据结构是:

struct binder_write_read {
  signed long write_size;
  signed long write_consumed;
  unsigned long write_buffer;
  signed long read_size;
  signed long read_consumed;
  unsigned long read_buffer;
};

binder_ioctl_write_read中的copy_from_user和copy_to_user都仅仅拷贝了这个结构体,里面真正保存了数据缓存的write_buffer会在binder_thread_write方法中才做了一次从用户空间到内核空间的拷贝,以及read_buffer只会简单的将已经保存在内核空间的数据指针稍作变换(加上用户空间内存映射地址和内核空间内存映射地址的偏移量)即可得到,没有拷贝其中的数据。简单来说,数据从来源用户空间到内核空间做了一次全拷贝,数据从内核空间到目标用户空间仅仅拷贝了一个外壳。

3.binder驱动中定义的链表节点只有prev和next指针,真正的节点数据保存在哪里?
以插入到todo列表里的binder_work为例:

struct binder_work {
    struct list_head entry;
    enum {
        BINDER_WORK_TRANSACTION = 1,
        BINDER_WORK_TRANSACTION_COMPLETE,
        BINDER_WORK_NODE,
        BINDER_WORK_DEAD_BINDER,
        BINDER_WORK_DEAD_BINDER_AND_CLEAR,
        BINDER_WORK_CLEAR_DEATH_NOTIFICATION,
    } type;
};

只定义了list_head和type,看起来并没有什么用,实际上,binder_work外面还可以有一层binder_node

struct binder_node {
    int debug_id;
    struct binder_work work;
    union {
        struct rb_node rb_node;
        struct hlist_node dead_node;
    };
    struct binder_proc *proc;
    struct hlist_head refs;
    int internal_strong_refs;
    int local_weak_refs;
    int local_strong_refs;
    binder_uintptr_t ptr;
    binder_uintptr_t cookie;
    unsigned has_strong_ref:1;
    unsigned pending_strong_ref:1;
    unsigned has_weak_ref:1;
    unsigned pending_weak_ref:1;
    unsigned has_async_transaction:1;
    unsigned accept_fds:1;
    unsigned min_priority:8;
    struct list_head async_todo;
};

插入到列表里的是binder_work里的entry,取出来的时候是binder_work,最后通过container_of来获得binder_node。这个对java程序猿来说由于不了解c的list,所以看代码的时候可能会比较困惑。

4.server和client是怎么处理binder驱动一次返回的多条指令的?
其实ServiceManager就比较好理解,从binder_loop通过ioctl取得binder驱动返回的数据后,调用binder_parse进行处理,binder_paser会在一个while循环中逐次解析每个命令。但是如果只看别人的对其他server(如MediaPlayerService)的分析,而不自己看一遍源码,可能会弄不明白。这里也以MediaPlayerService为例,简要回答一下。
MediaPlayerService通过IPCThreadState来跟binder驱动打交道,其中关键的方法是talkWithDriver。网上大部分分析都会省略该方法前面一部分而直接从ioctl开始分析。其实此方法前面部分先判断是否要从binder驱动中读数据。如果之前binder返回的指令还未执行完,则无需再读取,继续使用缓存中现有的数据。不管是否需要写数据,此方法执行完后,下一条还在缓存中的指令都能保证被执行到。

    binder_write_read bwr;

    // Is the read buffer empty?
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();

    // We don't want to write anything if we are still reading
    // from data left in the input buffer and the caller
    // has requested to read the next data.
    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;

    bwr.write_size = outAvail;
    bwr.write_buffer = (long unsigned int)mOut.data();

    // This is what we'll read.
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (long unsigned int)mIn.data();
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }
    ...
    // Return immediately if there is nothing to do.
    if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

整个read流程走下来大致是这样的:
binder read流程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值