Android Binder机制理解

来源:互联网 发布:联想电脑mac地址查询 编辑:程序博客网 时间:2024/05/18 08:47

Android 初学者,近日研究了一下Android进程通信Binder机制,看了老罗的源码分析,被里面的各种函数和数据结构体搞得大脑一度短路,但是仔细分析相信终能看懂,同时结合老罗推荐的两篇文章,对Binder机制也有了自己的一些总结,现将机制学习的过程记录下来,同时也起到敦促自己可以耐心将这部分内容完全看懂的作用,给自己加个油!

       对于Binder机制,我先看了    Android 深入浅出 ,耐着性子 看了一遍,有了大致的了解,再看的Android Binder设计与实现 有了总体的印象,从理论上了解到Android Binder的实现机制,然后再一步步根据老罗的博客去分析里面的细致代码。

      下面是我自己的总结,里面穿插了对上面提到的博客文章的借鉴,后续继续分析中。

1、是Android进程间通信的机制,相比传统IPC,有很多优点,在安全上,复杂性上,传输性能上。具体是在启用远程Service,用intent开启另外一个进程的Activity,获得另外一个进程的ContentProvider提供的内容时,感觉AIDL方式就是对Binder机制的封装实现吧。

2、Binder机制四个组件,Client    Server    ServiceManager   Binder Driver , 前三个都在用户空间上,最后一个是在内核空间上。  四者关系大体为:

盗用 老罗的图 

3、从 Service Manager 入手:  SM 作为Client 和 Server通信过程中的桥梁,必然要与这两者进行通信,而这个过程依然是binder的通信机制,只不过采用了特殊默认的方式《直接使用了handle == 0的引用号为0的binder来通信,源码分析后续继续》
      SM的main文件分析:
      关键函数为:  
  •  binder_open() ;   打开 /dev/binder 文件<misc 设备注册>,获得一个文件描述符的参数,在后续中会用到;同时这个函数出镜率较高,是同driver打交道时的必经函数,可以理解为要通信了。
  •  mmap();   在binder_open() 中的函数,进行内存空间映射的,感觉是开辟了一段内存,与driver共享的。         
  •  binder_become_context_manager();  binder 上下文管理者的告知,SM成为了binder的守护进程<运行后台的独立进程,进行服务>
  •  binder_loop();  循环,接受 针对SM为“server”的所有client的请求, 阻塞在这里等待唤醒                            
  •  binder_ioctl(); 是从ioctl() 函数进入的,ioctl函数可以控制文件的I/O通道,根据参数中的cmd,会执行不同的代码,在SM中,cmd是先是BINDER_WRITE_READ,进入 binder_thread_write()函数,根据bwr中的参数值BC_ENTER_LOOPER,使线程进入循环状态。再进入binder_ioctl,进入binder_thread_read,在这里分阻塞或者非阻塞状态
    注: 用户空间程序和Binder驱动程序交互大多数都是通过BINDER_WRITE_READ命令的
      关键数据结构:
  •  binder_state 
  •  binder_proc 
  •  binder_thread 
  •  binder_buffer
  •  binder_node,它表示一个binder实体
  •  binder_transaction_data
<有关数据结构的含义非常重要,涉及很多参数以及后续用到的承载传送数据的结构,binder实体的结构,一点点再啃,再补充>

      因为Client是通过SM得到想要的service<通过SM的接口调用getService("servicename")>,所以Service也要事先告诉SM有我这么一个service,具体是怎样的告诉(注册啊,添加啊之类的),后续分析。那么Client和Server都是需要得到SM的接口的,很典型的IPC,而此时还不是真正的Service与Client之间的IPC,正如上面所说的,是利用了一个特殊的Binder引用,其引用句柄是0
     关键函数:
  • defaultServiceManager()
               interface_cast<IServiceManager>(ProcessState::self()->getContextObject(NULL))
                                  分析一系列的类继承,IBinder  BpBinder ,BpBinder类通过IPCThreadState类来和Binder驱动程序并互,而IPCThreadState又通过它的成员变量mProcess来打开/dev/binder设备文件,mProcess成员变量的类型为ProcessState。ProcessState类打开设备/dev/binder之后,将打开文件描述符保存在mDriverFD成员变量中,以供后续使用。
  • IServiceManager::asInterface
                           gDefaultServiceManager = new BpServiceManager(new BpBinder(0));

     注:SM接口实质是一个BpServiceManager,包含了一个句柄值(引用号)为0的binder引用
     Server 与SM接口交涉具体调用 addService()函数,通过driver驱动,新建自己的实体加入到SM的列表中,以供client查询;
     Client 与SM接口交涉具体调用 getService()函数,通过driver驱动,得到Service接口。
4、从Server入手:  大部分以MediaPlayerService 为例,继承BnMediaPlayerService
     原理描述:实际上,BnMediaPlayerService并不是直接接收到Client处发送过来的请求,而是使用了IPCThreadState接收Client处发送过来的请求,而IPCThreadState又借助了ProcessState类来与Binder驱动程序交互,IPCThreadState接收到了Client处的请求后,就会调用BBinder类的transact函数,并传入相关参数,BBinder类的transact函数最终调用BnMediaPlayerService类的onTransact函数,于是,就开始真正地处理Client的请求了
     关键函数:
  • sp<ProcessState> proc(ProcessState::self())   在processstate的构造函数里面,有open_driver() 函数,这里打开/dev/binder 并通过 iotcl函数与binder交互
                     instantiate()                           
                addService(): 将当前service添加到SM中,重要的函数,是加深Binder机制理解的重要角色, BpServiceManager->addService()里面 ,定义了Parcel类的数据,用来序列化进程间通信数据的,在这个数据中写入了有关此service的标示字符串和名字
                                  writeStrongBinder()  重点关注对象:写一个binder对象到Parcel中,在这里是将new出来的service对象(MediaPlayService)写入parcel类数据中
                        flatten_binder() : 构造了一个flat_binder_object对象,此数据结构的cookie成员变量中存入了指向binder实体的指针
                            finish_flatten_binder() 调下面的函数,将flat_binder_object写入到Parcel  
                               writeObject()  同时记录flat_binder_object 在parcel类里的偏移位置
                    transact()BpBinder的trancact() -> IPCThreadState的trancact() 承接上面几个函数,参数里的data在经过上面的处理,已经是含有binder对象的Parcel对象,将此对象进行传递
                        writeTransactionData()建立了binder_transaction_data 对象,用上面的parcel对象给其初始化,然后将数据保存到了IPCThreadState的成员变量mOut中
                        waitForResponse() reply 不为空?为什么,明明只是在data那里声明了一下啊?在reply不为空的时候进入下面的函数
                             talkWithDriver()   与driver驱动程序交互 定义了 binder_write_read类型的bwr变量,记录mout 和 min中的数据,先执行写操作,后执行读操作
                                 ioctl()  通过具体的命令进行  这里带着bwr参数与binder驱动进行binder_ioctl(),因为bwr.write_size>0 ,所以进入下面的函数
                                    binder_thread_write() 将参数拷贝到本地的 binder_transaction_data<copy_from_user()> 调用binder_transaction函数,
                                        binder_transaction() 得到binder_context_mgr_node -> 在SM启动时进行初始化的,之所以能得到这个是,是因为当前事物的target.handle == 0 ,说明是向守护进程SM进行IPC,所以可以得到                     target_pro 就是SM进程,接下来的copy_from_user,就在SM的进程空间中开辟内存来保存传过来的参数了;同时对数据里的binder实体,binder驱动还要进行一番处理,首先在本地建立flat_binder_object得到binder实体,然后为这个binder新建node,并为SM建立对此binder实体的引用,并用新建的信息更新binder实体,因为这个binder实体是被传递给SM的,SM要拥有对其的句柄值等才可以对其进行引用(有client发出请求时才能够识别),上面一些列的动作是加入到了一个事务列表上,将在binder_thread_read函数出阻塞的SM进程唤醒,由它处理。
                                    binder_thread_read()函数 不去理会SM进程,先继续分析读时候的状况 下回分析 
                               唤醒了SM进程, 将数据浅拷贝到SM进程的用户控件<通过offset变量等>  关键问题:怎么样用户空间和内核空间的虚拟地址同时指向同一个物理地址呢
                         后面各种大脑不够用,待分析。只知道就是在SM中的列表中添加了有名字的Service,使得SM中有了Service的binder引用
 
  关键数据结构
          struct flat_binder_object  表示一个传输中的binder 对象,是在跟driver进行传输的数据结构,   里面的offsets  变量很重要
                 struct binder_transaction_data   在trancact() 函数里建立的数据结构k

     5、 从Client入手, client的分析和Server应该都是相同的,能够看懂 第四部分的大部分代码,想必这里不成问题
            关键函数
     6、 在Java端自己写service,并进行调用。与AIDL方式的应该类似吧,一个是自己去写,一个是利用系统封装好的方式

0 0
原创粉丝点击