Binder的实现原理
Binder实现的远程调用是一种面向对象的远程调用。相比于面向过程的远程调用,面向对象的远程调用功能更加强大,它可以创建多个Binder实体对象服务多个客户,每个对象之间数据封闭,互不影响,安全性更加可靠,而这些面向过程的远程调用是无法实现的。但是越强大的功能实现起来就会越复杂,需要解决的问题也会越多。首先需要解决的问题就是如果跨进程管理对象的生命周期。服务端的Binder死亡,客户端的Binder也要自动删除。Binder中通过ProcessState类集中管理进程中的引用对象,同时Binder驱动中维护这一个引用对象和实体对象的关联表。Binder使用自己的引用计数方式,来维护客户实体对象或引用对象与Binder驱动之间的数据一致性。其次Binder需要解决的是参数的传递,普通的IPC传递对象可以将对象序列化,而Binder需要将实体对象转换成引用对象,这就需要在Binder驱动内部去实现。普通IPC传递数据需要两次数据复制,一次从调用者的数据缓冲区复制到内核的数据缓冲区,一次从内核的数据缓冲区复制到接收进程的数据缓冲区,而Binder指需要一次数据复制,因为第二次接收进程共享的是内核的数据缓冲区,这就减少了一次数据复制。最后Binder服务端的线程池设计也是一个问题,Binder线程池第一个线程主动创建,其他线程在驱动的请求下自动创建。既能保证线程的使用数量最低,也能保证有线程可用。
Binder调用过程
(1)客户端从某个线程发起调用,将参数打包通过ioctl()函数传递给驱动。
(2)客户端挂起并等待ioctl()返回函数的结果
(3)驱动记录下调用线程的信息,然后根据调用的Binder对象信息寻找服务所在的进程
(4)驱动找到服务进程后先查看进程中是否有空闲线程,没有则通知服务进程创建
(5)得到空闲进程后根据驱动中保存的BBinder对象的指针调用相应的函数。
(6)函数返回后会再通过ioctl()把结果打包传递给驱动。
(7)驱动根据返回信息查找调用者线程。
Binder对象的传递
Binder的对象传递是通过Parcel类来完成的。在服务进程中将IBinder加入到Parcel对象后,Parcel对象会打包数据,并把数据类型标记为BINDER_TYPE_BINDER,然后把BpBinder的指针放进cookie字段,最后通过ioctl把Parcel对象的数据传递到Binder驱动中。Binder驱动会检查传递的数据,如果发现包含了标记为BINDER_TYPE_BINDER的数据,会查找和服务进程相关的“Binder实体对象表”,如果表中还没有这个实体对象的记录,则创建新的节点并保存信息。然后驱动会查看客户进程的“Binder对象引用表”,如果没有引用对象的记录,同样会创建新的节点,并让这个节点中某个字段指针指向服务进程的“Binder实体对象表”中的节点。接下来驱动会对Parcel中的数据进行改动,把数据类型从BINDER_TYPE_BINDER改为BINDER_TYPE_HANEL,同时把handle的值设为“Binder对象引用表”中节点的序号。最后改动的数据传到了客户进程中。接收到数据的客户进程发现收到的数据中的Binder类型为BINDER_TYPE_HANEL后,将使用其中的handle值作为参数调用ProcessState类中的函数getStrongProxyForHandle()来得到BpBinder对象。这样,客户进程就得到了Binder的引用对象。