版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
【移动应用开发技术】3.Androidbinder设计篇
一.引言--女娲造人的故事在天地混沌之际,上神女娲因为觉得自己太孤单,没人跟它一起嗨,决定按照自己的模样添加一些生物;于是呢,捏泥为人,并赋予了人生育的能力,所以女娲被称为了人类的母亲;神话故事很美好,解释了人类的来源,但是这个逻辑中其实有个缺陷,就是女娲从哪里来的?那有些同学就会问了,这里说Binder设计,为什么要提到女娲造人的故事呢?这是因为二者在源头这个问题的处理上有异曲同工之妙,嗯,后面会再做说明。二.AndroidBinder简介AndoidBinder从OpenBinder发展而来,提供了跨进程通信机制;其实linux已经提供很多的跨进程机制,比如管道,共享内存等等,那为什么google要使用binder呢?其实,大家也可以思考下,针对于移动设备这种低配置的设备(相对于服务器),新的进程间通信机制需要具备什么新的优点呢?1.内存节省,因为移动设备的低配置,所以需要新的通信机制尽可能的节省资源,binder在跨进程数据拷贝时只进行一次;2.高效性,比如对于手机这种东西,大家肯定希望它反应迅速,界面流畅;3.安全性,手机涉及了大量的用户隐私,比如支付宝账号,电话本联系人号码,短信记录,通话记录等等;在linux系统中对于权限安全性管理,UID是一个很重要的东西,所以对于安全性,binder就在binderdriver里面负责填写调用进程的UID和PID;相比一些在应用程序填写UID和PID的进程间通信机制,安全性得到了极大提高。总体来说,Binder肯定是一个不错的进程间通信机制,否则android经过这么多年的发展,如果Binder不是足够优秀的话,以google的技术实力,早就被google替换了吧!三.Binder通信关系总图Binder作为Android里面进程间通信机制,主要有4个模块参与这个过程,分别是clientserviceServiceManagerBinderDriverclient和service作为通信的模块很容易理解,因为client需要和service通信,所以它们两个必然会成为通信的一部分。那servicemanager和Binderdriver起的作用是什么呢?servicemanager提供service的注册,查询等服务,那说的再明白一点,就是serviceBinder本地对象把自己和一个名字添加到servicemanager注册;这样,client就可以向servicemanager通过指定的名字去查询之前注册的service,从而找到对应的Binder实体对象,再继而生成属于自己进程的Binder引用对象。最后把这个属于自己进程的Binder引用对象和自己进程内的一个Binder代理对象对应起来;这样client就间接的拥有了serviceBinder本地对象的引用,进而可以和service通信。那Binderdriver是做什么用的呢?Binderdriver提供的是桥梁的作用,比如从service注册是servicemanager,抑或从client到servicemangager查询service,以及从client到service的通信,都需要通过Binderdriver。所以Binderdriver提供了一个桥梁,同时在通信的过程中记录了一些数据。为了更好的说明,先上一张整体Binder的关系图。这张图已经基本包括了binder通信过程中的所有对象,我们可以用言语来简略的描述下这个过程和各个对象的含义,至于更详细的我们后面再讲。过程简短描述:1.首先这张图有两个空间,用户空间和内核空间;client,service,servicemanager运行在用户空间;而binderdriver运行在内核空间。2.servicemanager首先注册为binderdriver的服务管理者,注册的过程中,servicemanager会调用open和mmap方法来通知binderdriver为它分配一块最大不超过4MB的内存,当然这个大小可以由servicemanager来指定,servicemanager指定的是128KB。这块内存同时被两个虚拟地址应用,一个binderdriver的一个是servicemanager的。换句话就是说binderdriver为servicemanager分配了一块同时被内核空间和用户空间映射的内存。这也是binder驱动的精华,通过这种虚拟内存双重映射,减少了binderdriver和servicemanager之间的数据拷贝。3.这样,binderdriver就记录下了与servicemanager对应的binder实体对象(也就是图中的smbinder实体)。并且强制规定smbinder实体对象对应的是句柄值是0。也就是其他进程来访问service,只要它传到binderdriver的句柄值是0,那就意味着目标service就是servicemanager。然后servicemanager进程进入等待状态,等待别的进程来唤醒。比如别的进程要注册service或者查询service。4.service进程启动后,同样会通过open和mmap方法通知binderdriver为service进程分配一块不超过4MB的内存,一般应用程序的是1MB左右(1mb-8kb)。同第2点一样,这块内存也被binderdriver内核空间和service进程用户空间的虚拟地址同时映射。然后向servicemanager注册service;正如第2点所言,binderdriver强制规定了0号句柄对应的是servicemanager实体对象,所以service进程只要传入0号句柄,然后把想注册的service一起传入过来就可以了。5.binderdriver接到service的ioctl调用后,找到第2点描述的smbinder实体,然后通过这个smbinder实体找到servicemanager对应的进程。此时,servicemanager的进程正在睡眠等待状态,于是binderdriver把要处理的工作封装成一个binder_transaction丢给servicemanager进程,也就是把数据拷贝到binderdriver为servicemanager分配的那块内存(也就是第2步binderdriver为servicemanager分配的那块内存),这也是binder数据传输的唯一一次数据拷贝,然后唤醒它。6.此时,service进程会返回service进程用户空间,然后会再次进入binderdriver,并且进入等待状态;它需要等待servicemanager注册service的返回结果。7.servicemanager进程被唤醒后,回到servicemanager用户空间,同时把binderdriver拷贝过来的数据读取出来,把名称和从binderdriver传入的句柄值保存起来,再次执行结果通过ioctl指令通知binderdriver。8.servicemanager进程回到binderdriver之后,通过之前第5步产生的binder_transaction找到之前调用的binderdriver的service进程和线程,把第7点从servicemanager返回的结果读出来,然后拷贝到binderdriver为service分配的内存,再唤醒service线程。9.servicemanager在binderdriver完成之后,会再次进程等待状态。10.service进程被servicemanager进程唤醒之后,会回到service进程的用户空间,然后把第8步binderdriver拷贝service进程内存的数据读取出来(这块内存在binderdriver中,被service进程和binderdriver同时映射),再完成最后的逻辑。11.client进程启动后,也会像service和servicemanager一样通过open和mmap方法请求binderdriver为client进程分配一块内存用户进程间通信。12.client进程向servicemanager发起查询service请求,同时传入要查询service的名称,传入的binder句柄值是0,也就是对应着servicemanager。13.binderdriver接受到client的查询请求后,根据句柄值0找到servicemanager的binder实体对象(smbinder实体),然后通过smbinder实体对象找到servicemanager进程。此时,servicemanager进程正处于等待状态。14.binderdriver把从client传入的数据拷贝到为servicemanager分配的那块内存,生成一个binder_transaction,然后唤醒servicemanager进程。15.client进程经过一些处理后进入等待状态,等待servicemanager来唤醒。16.servicemanager被唤醒后,把binderdriver拷贝给它的数据读取出来,当然最重要的是从client进程传入的service名称。通过查找,找到名称对应的句柄值。也就是第7点那个名称和句柄值。然后通过ioctl再次回到binderdriver。17.binderdriver通过binder_transaction找到第15点的client进程和线程,通过从servicemanager返回的句柄值找到binderdriver里面对应的service实体对象。然后唤醒client线程。18.在binderdriver里面,client线程根据service实体对象,生成属于client进程的binder引用对象,也就是一个句柄值,再返回client进程用户空间。19.在client进程用户空间,把句柄值拷贝出来,封装成一个BpBinder对象,BpBinder对象里面mHandle值就保存了这个句柄值。20.client和service通信,client使用第19步获取到的mHandle句柄值,发送给binderdriver。21.binderdriver根据mHandle句柄值找到client进程内对应的binder引用对象,再根据binder引用对象找到它对应的binder实体对象,再根据binder实体对象找到其所在的binder进程。然后把数据拷贝到binderdriver为service进程分配的那块内存,再唤醒service进程。22.client进程的调用线程进入等待状态。23.由于binder实体对象有个cookie值指向用户空间service进程的的service地址,所以service在被唤醒后,它可以找到是由哪个service组件来响应此次服务(service进程内可能不止一个service组件)。24.service完成自己的逻辑后,再次通过binder_ioctl通知binderdriver,告诉binderdriver处理结果,然后再唤醒client进程。这个过程就和servicemanager通知service是一样的了。这样,整个通信过程就基本结束了。ps:1.这里说的service和android应用里面说的4大组件service并不是一个概念,这里的service指的是提供Binder服务的Binder本地对象。2.上面说的Binder代理对象,Binder引用对象,Binder实体对象,Binder本地对象是按照从client到service的顺序的,可以按个解释下这几个对象的含义。a.Binder代理对象,运行在client用户空间,对应的类是BpBinder,实现的关键在于它有个句柄mHandle变量,通过这个变量可以在Binderdriver内核空间里面找到对应的Binder引用对象。b.Binder引用对象,运行在Binderdriver内核空间,对应的数据是binder_ref结构体。c.Binder实体对象,运行在Binderdriver内核空间,对应的数据是binder_node结构体。d.Binder本地对象,运行在service用户空间,对应的数据是BBinder,其实就是一个提供服务的Binderservice。3.可能大家看到看到对于servicemanager在binderdriver中,client和并没有binder引用对象,而其他service在client进程中却有binder引用对象,这是为什么呢?这个问题就回到文章开始的第一点,里面在描述女娲造人关于源头这个问题上的时候,我们说神话故事里面假设女娲是事先存在的,从而弥补了这个故事里面的漏洞。那在binder机制里面,也有这个问题:就是client通过servicemanager拿到service的binder句柄值,那么service怎么拿到servicemanager的句柄值呢?就好比说人是女娲造的,那女娲又是哪里来的呢?好吧,跟神话故事一样,我们也是假设servicemanager是事先存在的,并且规定它的句柄值就是0。那么client和service只要传入句柄0就可以找到servicemanager在binderdriver中的binder实体对象,自然也不需要binder引用对象了。而且servicemanager进程也不会像其他service进程一样,可能存在多个service服务。4.servicemanager请求binderdriver为它分配的内存是128kb,一般应用程序(client,service)请求binderdriver为它分配的内存是1MB-8KB。这是我们要求使用binder时不能传输大于1MB数据的原因,比如通过intent传输图片,很有可能会超过binder传输上限。当然这个我们在后面分析代码的时候会看到。详见:android_proj/framework/base/libs/binder/ProcessState.cpp#defineBINDER_VM_SIZE((1*1024*1024)-(4096*2))5.在上图中大家看到有些service进程中可能存在多个service组件,比如service2进程。它们都通过binderdriver注册到了servicemanager,但是它们有不同的名称对应以及不同的service组件地址,这两个区别分别会被servicemanager和binderdriver(通过binder_node结构体的cookie数据)所记住。所以可以区分。6.binder线程池,用户空间的程序有个线程池来响应binderdriver,但是有个上限,一般是15个线程,详见android_proj/framework/base/libs/binder/ProcessState.cppopen_driver(){
...
size_tmaxThreads=15;
result=ioctl(fd,BINDER_SET_MAX_THREADS,&maxThreads);
...}但是需要说明的是这15个线程指的是binderdriver请求用户空间程序创建的最大线程数(当应用程序线程数不足以响应binder请求时,binderdriver会请求应用程序分配线程),并不包括用户空间程序主动注册到binderdriver的。四.binder机制中的数据结构在初步了解binder机制的通信过程后,我们需要更深一步的了解binder通信,所以需要简单讲解下binder通信过程中的数据结构。binderservice对象描述,每个添加到binderdriver的service都会对应一个binder_node,包括servicemanager也是。flat_binder_object顾名思义就是压扁了的binder_object,为什么要压扁呢?是为了传输的需求,所以把binder对象从用户空间传输到binderdriver内核空间的时候,就需要用到这个数据结构,比如addService的时候。对应一个进程,当进程调用open方法打开binderdriver的时候,binderdriver会保存这个进程的相关信息,就是用binder_proc这个数据结构。在上面我们说起过,一个进程中可能有多个service组件,比如service2进程,那在binderdriver里面也是这种关系。binder_proc和binder_node是一对多的关系。binder_proc使用了红黑树的数据结构来描述了它里面binder_node。红黑树的数据结构大家有不清楚可以百度下。binder_transaction用来描述binderdriver里面的一个事务。事务在数据库里面用的比较多,binderdriver也借用了这个概念。那binder_transaction_data就刚好是用来描述从用户空间到内核空间传输binder_transaction数据的。在前面描述过,binderdriver会为每个进程分配一个不超过4MB的内存,用来传输数据。那怎么来管理这块内存呢?binderdriver把这块内存划成一页一页来管理,那一个binder_buffer就表示一页。binderdriver里面的binder引用对象。binder_ref主要的作用就是拥有一个句柄值,同时指向一个binder_nodebinder实体对象。句柄值对应client里面的binder代理对象BpBinder里面的mHandle,所以client可以根据mHandle找到内核对应的binder_ref数据。binder_ref关联的binder_node对象,这样,找到binder_ref后,就可以进一步找到binder_node。binder_node上面讲过,关联了binder_proc,以及service进程里面的service组件,也就是binder本地对象。所以,通过这一层层的关联,都连接起来了。跟binder本地对象死亡通知有关。我们知道binder机制是这样的引用的。BpBinder(mHandle)->binder_ref->binder_node->BBinderbinder本地对象。那如果binder本地对象意外的挂了呢?比如service所在进程由于程序逻辑错误异常退出了,调用它的client怎么办呢?所以为了部分解决这个问题,binder因为死亡通知这个功能,当binder本地对象消亡之后,需要通知正在监听它的binder_refclient。ioctl指令BINDER_WRITE_READ的数据结构。五.实名binder对象和匿名binder对象所谓的实名和匿名是针对servicemanager而言的,就是servicemanager知不知道这个binder对象的存在,并且给它一个名字相对应。正如第一步里面所描述的女娲造人的故事,女娲可以造人,但是人也可以造人。如果把女娲比喻成servicemanager,那么第一代人就是实名binder对象,第一代人以及后面的人造的人就是匿名binder对象;因为它对于女娲这个servicemanager而言,女娲并不知道它们的存在和名字。这个比喻如果难以理解的话,那再举个更加现实的栗子:如果一个人想进入火车候车室,那么他需要火车票或者站台票,那我们现在来做如下比喻:1.假如把进入候车室这个功能比喻成实现binder通信这个功能,那么:2.火车票和站台票都可以进入候车室,也就是说实名binder和匿名binder都可以完成进程间通信功能,这点并没有什么区别。3.由于火车票是实名制的,所以铁道部能知道卖出的票和购买人的***一一对应;但是对于站台票,铁道部并不能知道这种对应关系;那对于servicemanager和实名binder,匿名binder也是这种关系--servicemangager清楚的知道所有实名binder的名称,binder服务所在的进程等等信息,但是对于匿名binder却一无所知,它甚至不知道匿名binder的存在。4.购买站台票不是想买就可以买的,需要持有火车票吃可以购买站台票;那对于实名binder和匿名binder也是如此,匿名binder不是想创建就能创建的,首先需要得到一个实名binder,通过实名binder得到匿名binder,具体例子可以参考bindService得到一个匿名binder的实现。5.对于检票口,不管是火车票还是站台票都会从检票口通过,所以检票口可以记录一些信息;同样,对于binderdriver,不管是实名binder还是匿名binder都会通过binderdriver,binderdriver也会记录它们的信息。六.binderioctl指令七.binder命令binder命令按照命令的流向性分为两大类:1.BC_XXX2.BR_XXX从用户空间流向binderdriver的命令被称为BC_XXX,也就是bindercommand的简称;相应的从binderdriver流向用户空间进程的称为BR_XXX,也就是binderreturn的简称。如下图:对于所有的binder命令,可以用下面两张表来描述:具体可以参考./working_directory/kernel/goldfish/driver/staging/android/binder.henumBinderDriverCommandProtocol{
...}enumBinderDriverReturnProtocol{
...}3.BC_XXX通知binderdriver去释放一个binder_buff的内存,参数int表示此binder_buff在用户空间进程的虚拟映射地址。通知binderdriver增加目标service弱引用,参数int表示目标service的handle值,通知binderdriver增加目标service强引用,参数int表示目标service的handle值.通知binderdriver减少目标service强引用,参数int表示目标service的handle值.通知binderdriver减少目标service弱引用,参数int表示目标service的handle值.通知binderdriverBR_INCREFS命令执行完毕,一般由service进程在处理完binderdriver的BR_INCREFS命令后向binderdriver发出。因为binderdriver在请求service进程增加一个service组件的强引用之后,它需要等待service组件增加强引用计数的结果,它需要根据这个结果修改自己的一些状态。通知binderdriver此进程进入BINDER_LOOPER_STATE_REGISTERED状态,再经过一些处理就会进入就绪状态,可以处理进程的事务。此命令是binderdriver通知用户空间进程创建线程后,用户空间进程创建线程后会调用此命令,通知binderdriver此线程已经准备好。此命令和BC_REGISTER_LOOPER的区别就是BC_ENTER_LOOPER是用户空间进程主动通知binderdriver的,BC_REGISTER_LOOPER是binderdriver发现此用户空间进程的线程池无法响应binder通信,需要创建新线程;然后向目标用户空间进程发出请求创建线程命令用户空间进程创建线程完毕后,会调用BC_REGISTER_LOOPER通知binderdriver。通知binderdriver此线程退出client请求binderdriver注册目标service组件的死亡通知client通知binderdriver取消注册对某个servicebinder本地对象的死亡通知监听client通知binderdriver对某个service进程的binder本地对象死亡通知处理完毕。4.BR_XXX通知用户空间进程,binderdriver处理出现异常通知用户空间进程,binderdriver处理成功比如addService的时候,binderdriver请求servicemanager去注册一个service。binderdriver反馈目标binder对象已经死亡,返回错误。binderdriver请求用户空间进程增加指定binder本地对象的弱引用binderdriver请求用户空间进程增加指定binder本地对象的强引用binderdriver请求用户空间进程减少指定binder本地对象的强引用binderdriver请求用户空间进程减少指定binder本地对象的弱引用暂不支持没有什么操作binderdriver请求用户空间进程分配一个线程;这种情况一般是在用户空间进程线程池无法处理binderdriver间通信请求的情况下。通知用户空间进程所监听的binder本地对象已经销毁在用户空间进程请求BC_CLEAR_DEATH_NOTIFICATION命令后,binderdriver返回这个命令通知用户空间进程八.引用计数在第七点的表中,我们提到了所有的Binder协议命令,其中包括类似BC_INCREFS,BC_ACQUIRE,BC_RELEASE,BC_DECREFS之类的命令,表中解释是维护binder对象的引用计数;那为什么要进行这样的设计呢?那我们可以来做这样一种假设:client进程引用service进程的一个binder本地对象正在通信,如果这个时候service进程把这个binder本地对象回收了怎么办?为了解决这个问题,binder机制使用了引用的概念:在Java里面,我们知道维护一个对象的生命周期
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 华师大版初中科学7.2 人体生命活动的神经调节方式(24课件)
- 位移、速度、加速度、时间之间的关系
- 人教部编版四年级语文上册习作《记一次游戏》精美课件
- 【同步提优】部编版三语下第七单元各类阅读真题(含小古文、非连续性文本等)名师解析连载
- 福建省福州文博中学2024届下学期第二次月考高三数学试题试卷
- 算法设计与分析 课件 6.7-贪心法应用-多机调度问题
- 2024年宿州申请客运从业资格证版试题
- 2024年重庆客运从业资格证500题下载
- 2024年大同考客运从业资格证考试题目
- 2024年郴州道路客运输从业资格证培训考试资料
- 小学四年级数学三位数除以两位数过关考核口算题带答案
- 糖尿病健康知识宣教
- 2024年湖南湘潭市公安局招聘留置看护巡逻警务辅助人员28人历年高频难、易错点500题模拟试题附带答案详解
- (新版)糖尿病知识竞赛考试题库300题(含答案)
- 《创意改善生活》课件 2024-2025学年湘美版(2024)初中美术七年级上册
- 2024-2025学年 浙教版七年级数学上册期中(第1-4章)培优试卷
- CHT 1027-2012 数字正射影像图质量检验技术规程(正式版)
- 国际经济与贸易专业生涯人物访谈报告
- 《扣件式钢管脚手架安全技术规范》JGJ130-2023
- 教学成果奖培育思考
- 河北省廊坊市药品零售药店企业药房名单目录
评论
0/150
提交评论