注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

东东的博客

江南烟雨,同大家一起分享

 
 
 

日志

 
 

Android之binder驱动分析  

2011-04-28 11:01:19|  分类: android相关 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

理解binder驱动最好有一个上层的概念,也就是对binder怎么用的有所了解。binder解决的是进程间通信,也能共享对象。IPC的过程是通过内核一个模块(或用户空间的守护进程),中转和翻译不同进程需要共享的对象,以取得所有进程似乎处于同一个名字空间的效果。比如A进程如果要使用B进程的服务,B进程首先注册此服务,A进程通过binder获取该服务的hanlde,用这个handle,A进程就可以使用该服务了。你可以把handle理解成地址。A进程使用B进程的服务还意味着二者遵循相同的协议,这个协议反映在代码上,就是二者要实现IBinder接口。
关于binder的IPC机制,这个链接必读,读此文档后,你能建立理解binder驱动所需要的抽象。http://www.angryredplanet.com/~hackbod/openbinder/docs/html/BinderIPCMechanism.html
以下分析Binder驱动中主要的几个操作。
    * Open操作
记录进程的信息,并在/proc目录下生成一个直读文件项。注意,进程信息记录在file结构的private字段,意味着每个进程的这个值是独立的。
把进程信息proc加入全局的proc列表里,这样,binder就可以访问所有参与的进程了。
    * Mmap操作
确定大小,最大4MB。然后获得地址空间,并把此空间的地址记录在进程信息里(buffer)。
分配物理页面,记录下来(pages)。
把此buffer插入到进程信息的buffer列表中。
调用binder_update_page_range,把分配的物理页面和vm空间对应起来。
把此进程的buffer插入到进程信息中(binder_insert_free_buffer(proc, buffer))。
    * Ioctl操作
先调用binder_get_thread()找到这个线程的控制数据结构,找不到就创建一个新的。注意在Linux里,线程和进程是一回事,线程有自己的pid。
BINDER_WRITE_READ:
这个是最核心的操作,是所有IPC的基础。实现是binder_thread_write()和binder_thread_read(),先写后读,就是发命令,然后读结果。
看binder_thread_write()函数,这个函数接收的一个参数是用户发来的数据。数据包括写缓冲和读缓冲两部分。写缓冲包括一些book-keeping命令(增加/减小引用计数),最后是一个需要响应的命令(IPC!),写缓冲的命令请看BinderDriverCommandProtocol枚举类型。读缓冲也是先包含一些book-keeping命令,最后要么是写缓冲中最后命令的结果,要么是另个嵌套命令,读缓冲的命令请看BinderDriverReturnProtocol枚举类型。
写方向的命令最重要的是BC_TRANSACTION和BC_REPLY,意思就是启动一个事务,然后获得结果。这两个命令后面跟的数据的数据结构由transaction_flags和binder_transaction_data定义。看一下数据结构的定义:
struct binder_transaction_data {
    union {
        size_t handle;
        void *ptr;
    } target;
    void *cookie;
    unsigned int code;
    unsigned int flags;
    pid_t sender_pid;
    uid_t sender_euid;
    size_t data_size;
    size_t offsets_size;
    union {
        struct {
        const void *buffer;
        const void *offsets;
        } ptr;
        uint8_t buf[8];
    } data;
};
其中的target的handle是要处理此事务的目标对象的句柄,根据此handle驱动可以找到应该由哪个进程处理此事务,并把此任务分发给一个线程,而那个线程也正在执行ioctl的BINDER_WRITE_READ操作,即正在等待一个请求。binder驱动把要做的事放在读缓冲里,返回给这个服务线程,后者执行的操作,如上述,遵循BinderDriverReturnProtocol。命令后面仍然是binder_transaction_data数据。这时的binder_transaction_data和发送方写缓冲的是相对应的。
处理请求的线程把数据交给合适的对象,该对象执行预定操作,然后把返回结果用binder_transaction_data结构封装,以写命令的方式传回给binder驱动,binder驱动把此数据放在一个读缓冲里,返回给正在等待结果的原进程(线程)。这样就完成了一次交易。
target的ptr字段是什么意思?为什么跟handle合用一个空间?ptr正是handle的对应物。对于请求方,使用handle来指出远程对象,对于响应方,使用ptr来寻址要执行需要动作的对象。所以handle和ptr是一个东西的两种表达。Handle和ptr之间的翻译关系正是binder驱动需要维护的。
这个映射关系也是进程间引用对象的基础,对一个对象的引用,在远程,是handle,在本地,是地址。
transaction的处理函数是binder_transaction(),这是个大函数。
这个函数分成两个部分:第一部分解析目标进程和目标线程;第二部分完成事务语义。第一部分又分两个子部分:A)此事务是reply;B)不是。A)情况要从事务栈取得目标线程;B)情况则只取得目标进程,因为此时不知道具体哪一个线程会处理此请求。
第二部分首先分配相应的事务数据结构,基本上是输入事务数据结构的克隆。填入一些已知或现成的数据,例如相关进程pid之类。接着开始处理数据部分的内容。此部分区分要处理的是binder对象传递(BINDER_TYPE_BINDER/BINDER_TYPE_WEAK_BINDER),引用(BINDER_TYPE_HANDLE/BINDER_TYPE_WEAK_HANDLE),还是文件(BINDER_TYPE_FD)。
如果是binder对象,那么相当于是在注册服务,此时将分配节点,生成handle,以备引用;如果是引用,则应该是客户在调用该引用所指向的服务,则找到代表此服务的节点,如果该节点的主人确实是目标进程,则增加该节点的计数;反之,尝试在目标进程上下文中找这个节点。如果是文件,则原handle是个文件描述符,根据此fd找到对应的文件(fget),再在目标进程中分配一fd(target_fd=task_get_unused_fd_flags();task_fd_install()),然后把这个fd赋值给返回的handle。
注意此处BINDER_TYPE_HANDLE与BINDER_TYPE_BINDER和handle与ptr的对应关系。
其它几个ioctl操作都简单了:
BINDER_SET_MAX_THREADS:设置进程最大线程数目,进程据此决定线程池的容量。
BINDER_SET_CONTEXT_MGR:ServiceManager设置自己作为context manager节点,凡为0的handle都是只这个master节点。
BINDER_THREAD_EXIT:删除一个线程的数据结构。
BINDER_VERSION:返回版本信息。

 

转自:http://blog.sina.com.cn/s/blog_606334a20100gq3h.html

  评论这张
 
阅读(2682)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017