zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

Android Binder原理从开始到放弃详解手机开发

Android原理手机开发 详解 开始 放弃 Binder
2023-06-13 09:20:15 时间

Binder是Android系统IPC通信的一块基石,不管是AMS、WMS都可以看到Binder 的身影,如果搞不清楚的话,看Android源码的时候就会搞不清,比如为什么需要各种代理?怎么突然就跳跃了?

Binder内部细节太多了,然后个人只想搞清楚整体的机制而又不想深究内部的细节。不过一些东西又必须要弄清楚,比如IBinder、BBinder、BPBinder、Binder Proxy、ServiceManager等,所以尽可能以流程图的形式帮助理解,但是也包含一些关键数据结构。

同时,以下是个人学习binder的一个记录,有很多不足的地方,望谅解。

1. IPC机制

IPC(Inter-Process Communication), 进程之间相互通信的机制,目前 Android支持的一些IPC的手段有:Bundle(四大组件通信)、文件共享、AIDL、Messneger、ContentProvider、Socket。

2. Binder的优势

Client-Server 结构
安全性高,带有调用方的Pid、Uid
传输性能高,利用匿名共享内存,数据拷贝只要一次,

参考:https://www.zhihu.com/question/39440766/answer/89210950

3. Binder的使用:AIDL

在Android中,可以很方便的使用AIDL和Service来建立一个Binder跨进程通信,文章希望可以从AIDL入手,从Binder的应用,慢慢往内部实现层层剖析,加深对Binder的理解。

定义一个AIDL接口:

interface IPlusService {

 int add(int a, int b); 

IPlusService有一个add方法,相加获取结果,我们需要在A进程调用B进程的add服务,并且获取得到结果。

在这里我们把服务的调用方称为客户端,就是A;服务的提供方称为服务端,就是B。

使用Android Studio的build,默认会帮助我们构造一个文件IPlusService.java,打开看一下:

public interface IPlusService extends IInterface {


// Stub供服务端实现 public static abstract class Stub extends Binder implements IPlusService { // binder的唯一标识 private static final String DESCRIPTOR = "com.jscheng.sweather.IPlusService"; public Stub() { this.attachInterface(this, DESCRIPTOR); // 提供给客户端:将IBinder转化成IPlusService接口 public static IPlusService asInterface(IBinder obj) { if ((obj==null)) { return null; // 如果是客户端跟服务端同一个进程,直接返回server端的binder对象 IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null) (iin instanceof IPlusService))) { return ((IPlusService)iin); // 若不同进程,则生成一个binder的代理 return new IPlusService.Stub.Proxy(obj); // 底层对应BBinder @Override public IBinder asBinder() { return this; // 运行在服务端:客户端调用transact(),会引发服务端onTransact() // code 表示客户端请求方法标志 // data 表示参数 // reply 表示写入返回值 // 返回值:客户端请求结果,可以做权限控制 @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { switch (code) { case INTERFACE_TRANSACTION: reply.writeString(DESCRIPTOR); return true; case TRANSACTION_add: data.enforceInterface(DESCRIPTOR); // 从Parcel读取客户端传过来的数据 int _arg0 = data.readInt(); int _arg1 = _arg1 = data.readInt(); // 调用服务端的add() int _result = this.add(_arg0, _arg1); reply.writeNoException(); // 写入返回给客户端的数据 reply.writeInt(_result); return true; return super.onTransact(code, data, reply, flags); // 客户端:服务的代理类 private static class Proxy implements IPlusService { private IBinder mRemote; Proxy(IBinder remote) { mRemote = remote; // 底层对应 BinderProxy @Override public IBinder asBinder() { return mRemote; public String getInterfaceDescriptor() { return DESCRIPTOR; // 客户端:打包数据,调用代理binder的transact(),会引发服务端transact() @Override public int add(int a, int b) throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); int _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(a); _data.writeInt(b); // 代理类,调用transact() mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); } finally { _reply.recycle(); _data.recycle(); return _result; static final int TRANSACTION_add = (IBinder.FIRST_CALL_TRANSACTION + 0); // 真正实现服务的地方 public int add(int a, int b) throws RemoteException;

在Activity中,作为binder的客户端,我们这样使用:

private ServiceConnection conn = new ServiceConnection() {

 @Override 

 public void onServiceConnected(ComponentName name, IBinder binderProxy) {

 try {

 // 将Service传过来的IBinder转化成IPlusService 

 IPlusService plusServiceProxy = IPlusService.Stub.asInterface(binderProxy); 

 // 调用add()并同步得到结果 

 int result = plusServiceProxy.add(1, 2); 

 } catch (RemoteException e) {

 e.printStackTrace(); 

 ... 

而在Service端,作为Binder的服务端实现起来也很方便,记得要在Manifest文件中,给MainService的声明加上 process= :remote ,表示当前的Service启动在另一个进程。

public class MainService extends Service {

 @Override 

 public IBinder onBind(Intent intent) {

 return new PlusServiceImpl(); 

 public class PlusServiceImpl extends IPlusService.Stub {

 @Override 

 public int add(int a, int b) throws RemoteException {

 return a + b; 

AIDL的使用起来很简单,因为工具帮我们生成了代码框架,我们只需要重写一下服务端的接口和客户端的调用,中间的传输细节基本透明化了。

server-client

整个IPC的调用流程:


客户端会将参数打包成parcel对象,Binder的代理对象会调用transact(),同时会阻塞当前线程,等待transact()结束,返回结果。


服务端会在onTransact()收到调用方的调用,通过code知道调用的是add服务,通过传过来的parcel解析出参数,最后返回服务结果给客户端。


服务端和客户端是两个进程,客户端调用transact(),服务端实现onTransact() Binder的调用,在客户端看来是同步的,transact()从调用到获取结果的过程。 Binder的被调用,在服务端看来是异步,支持多个客户端调用,服务端底层是线程池。所以可能需要注意同步的问题。 如果服务端和客户端在同一个进程,是不会发生Binder通信的,而是在asInterface返回服务端的对象。 onTransact可以做权限验证,拒绝调用。 客户端在调用add的时候,内部是调用transact,在等待回应的时候将会被挂起,所以若是耗时操作的话需要开启子线程。 Parcelable 和 Parcel

Parcelable是一个接口、用来实现序列化,实现了Parcelable的类就可以在进程间通过binder传输,比如Intent;

Parcel用于在进程间传递数据,在内存上序列和反序列化,实现Parcelable的类可以调用writeToParcel()把Parcelable数据写入到Parcel中,调用createFromParcel读取数据。

参考:https://www.jianshu.com/p/f5e103674953

IInterface、IBinder、Binder、BinderProxy、BBinder、BPBinder

IBinder表示具备跨进程传输的能力,只要实现了IBinder接口可以进行跨进程传递这个对象,由驱动底层支持的。

IInterface对应的是AIDL声明的接口,表示提供怎么样的服务。

Binder类代表Binder本地对象,继承自IBinder,具有跨进程传输的能力。

BinderProxy类是Binder类的内部类,代表远程进程的Binder对象的代理,继承自IBinder,具有跨进程传输的能力。

跨进程通信中,Binder驱动会自动抓换Binder和BinderProxy。

public interface IInterface {

 public IBinder asBinder(); 

声明的binder service接口必须继承自IInterface,它提供了将服务或者服务代理类转为IBinder类型

public interface IBinder {

 public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) 

 throws RemoteException; 

IBinder接口是提供远程调用的接口,描述了与远程对象进行交互的抽象协议。如果我们需要定制一个服务,建议继承Binder类,而不是直接实现这个接口。

public class Binder implements IBinder {

 public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, 

 int flags) throws RemoteException {

 ... 

 boolean r = onTransact(code, data, reply, flags); 

 ... 

 protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, 

 int flags) throws RemoteException {


Binder 实现了IBinder接口,是真正的binder对象,内部有两个方法,transact()和onTransact(),其实transact()内部也只是转化调用了onTransact()

public class BinderProxy implements IBinder {

 public boolean transact(int code, Parcel data, Parcel reply, int flags) 

 throws RemoteException {

BinderProxy 是 Binder的内部类,同时也实现了IBinder接口,它表示远程Binder 的本地代理。
这里我们把client端持有的是Binder的本地代理BinderProxy,调用了BinderProxy.transact(),这时候远程server的Binder.transact()是响应了Client发出的远程调用,调用onTransact()处理任务。

也许你会很奇怪,这里怎么突然冒出了个BinderProxy?

因为Binder在客户端和服务端的表现是不一样的。

在server端的IPlusService.Stub的asBinder()返回的IBinder,底层jni对应的是BBinder对象。

而在client端的IPluService.Stub.Proxy的asBinder()返回的mRemote,其实是BinderProxy,它是由底层的jni BpBinder创建的一个Java服务对象,并保存在BpBinder中,一个BpBinder关联多个BinderProxy。

下面就更奇怪了,怎么突然又出现了BBinder和BpBinder了呢?

BBinder和BpBinder都是IBinder的实现类,在server端表现为BBinder,是真正的binder,而在client端是binder的引用对象BpBinder,为了提供给上层使用生成了代理类BinderProxy,这样client端通过引用对象就可以通知到远程的server端了。

在这里插入图片描述

小结:

下面问题来了BpBinder怎么跟BBinder通信的呢?

4. Binder的总体概览

cs
BInder采用了CS的结构,Client和Server是存在于用户空间,Client和Server通信实现是由Binder驱动在内核的实现的。

从Binder组件中,包含Client、Server、ServiceManager以及binder驱动。

1. ServiceManager

其中ServiceManager负责管理系统中的各种服务,比如Client需要某个服务,可以通过ServiceManager查询获取服务,Server也可以向ServiceManager注册服务。ServiceManager是Android进程间通信机制Binder的守护进程。

ServiceManager也是在一个独立的进程中,那么Client进程或者Server进程怎么跟ServiceManager通信呢?其实也是利用了binder机制。

ServiceManager进程在启动的时候,调用Binder驱动时,使用使BINDER_SET_CONTEXXT_MGR命令将自己注册成ServiceManager时,Binder驱动会自动创建Binder实体。

即使ServiceManager在驱动中注册了binder实体,那其他client怎么拿到他的binder的引用呢?因为从上面的流程来看,server端有binder实体BBinder,client端需要拿到BpBinder,就可以实现通信。

这里有一个机巧的设置,client端中handle值为0的binder引用(BpBinder)就是指向ServiceManager,这样一来client端就直接拿到BpBinder。

2.Server向ServiceManager注册服务

Server 利用handle值为0的引用向ServiceManager,注册Binder的引用。ServiceManager在表中增加Service的名字和Binder 的引用。

3.Client从ServiceManager中获得Service

Client也利用了handle值为0的引用向ServiceManager请求访问某个Service。ServiceManager在查找表中找到Binder的引用打包发送给Client。这样Clinet就拿到了对应服务的BpBinder。

在这里插入图片描述

小结

ServerManager做为守护进程,在启动的时候注册成为ServerManager。
Server通过ServerManager注册服务。
Client通过ServerManger获取得到服务。

5. 重要的数据结构

其实,以上还是没有讲述到BBinder和BpBinder怎么实现通信的。果然还是要从内核驱动的视角出发去理解。

binder_proc

struct binder_proc {

 //上述全局hash表中一个节点,用以标记该进程 

 struct hlist_node proc_node; 

 // 进程组ID 

 int pid; 

 // 任务控制模块 

 struct task_struct *tsk; 

 // 文件结构体数组 

 struct files_struct *files; 

 /** Binder线程池每一个Binder进程都有一个线程池,由Binder驱动来维护,Binder线程池中所有线程由一个红黑树来组织,RB树以线程ID为关键字 */ 

 //上述红黑树的根节点 

 struct rb_root threads; 

 /** 一系列Binder实体对象(binder_node)和Binder引用对象(binder_ref) */ 

 /** 在用户控件:运行在Server端称为Binder本地对象,运行在Client端称为Binder代理对象*/ 

 /** 在内核空间:Binder实体对象用来描述Binder本地对象,Binder引用对象来描述Binder代理对象 */ 

 // Binder实体对象列表(RB树),关键字 ptr 

 struct rb_root nodes; 

 // Binder引用对象,关键字 desc 

 struct rb_root refs_by_desc; 

 // Binder引用对象,关键字 node 

 struct rb_root refs_by_node; 

 // 这里有两个引用对象,是为了方便快速查找 

 /** 进程可以调用ioctl注册线程到Binder驱动程序中,当线程池中没有足够空闲线程来处理事务时,Binder驱动可以主动要求进程注册更多的线程到Binder线程池中 */ 

 // Binder驱动程序最多可以请求进程注册线程的最大数量 

 int max_threads; 

 // Binder驱动每主动请求进程添加注册一个线程的时候,requested_threads+1 

 int requested_threads; 

 // 进程响应Binder要求后,requested_thread_started+1,request_threads-1,表示Binder已经主动请求注册的线程数目 

 int requested_threads_started; 

 // 进程当前空闲线程的数目 

 int ready_threads; 

 // 线程优先级,初始化为进程优先级 

 long default_priority; 

 //进程的整个虚拟地址空间 

 struct mm_struct *vma_vm_mm; 

 /** mmap 内核缓冲区*/ 

 // mmap——分配的内核缓冲区 用户控件地址(相较于buffer) 

 struct vm_area_struct *vma; 

 // mmap——分配内核缓冲区,内核空间地址(相交于vma) 两者都是虚拟地址 

 void *buffer; 

 // mmap——buffer与vma之间的差值 

 ptrdiff_t user_buffer_offset; 

 /** buffer指向的内核缓冲区,被划分为很多小块进行性管理;这些小块保存在列表中,buffer就是列表的头部 */ 

 // 内核缓冲列表 

 struct list_head buffers; 

 // 空闲的内存缓冲区(红黑树) 

 struct rb_root free_buffers; 

 // 正在使用的内存缓冲区(红黑树) 

 struct rb_root allocated_buffers; 

 // 当前可用来保存异步事物数据的内核缓冲区大小 

 size_t free_async_space; 

 // 对应用于vma 、buffer虚拟机地址,这里是他们对应的物理页面 

 struct page **pages; 

 // 内核缓冲区大小 

 size_t buffer_size; 

 // 空闲内核缓冲区大小 

 uint32_t buffer_free; 

 /** 进程每接收到一个通信请求,Binder将其封装成一个工作项,保存在待处理队列to_do中 */ 

 //待处理队列 

 struct list_head todo; 

 // 等待队列,存放一些睡眠的空闲Binder线程 

 wait_queue_head_t wait; 

 // hash表,保存进程中可以延迟执行的工作项 

 struct hlist_node deferred_work_node; 

 // 延迟工作项的具体类型 

 int deferred_work; 

 //统计进程相关数据,具体参考binder_stats结构体 

 struct binder_stats stats; 

 // 表示 Binder驱动程序正在向进程发出死亡通知 

 struct list_head delivered_death; 

 // 用于debug 

 struct dentry *debugfs_entry; 

 // 连接 存储binder_node和binder_context_mgr_uid以及name 

 struct binder_context *context; 

binder_proc 表示进程信息,是内核驱动创建的,表示当前binder所在的进程。

这里我们需要关注的,binder_proc 包含了以下这些重要的信息:

Binder实体对象binder_node,表示当前进程提供了哪些binder服务; Binder引用对象binder_ref,表示哪些进程持有当前进程的binder的引用; Binder线程池,每一个进程都有一个Binder线程池,由Binder驱动来维护,线程接受驱动的命令; todo工作项,驱动会将binder_transaction,加入todo列表,被唤醒之后的线程就可以取出处理;
/** 每一个binder进程都由一个binder_proc来描述,binder进程内部所有Binder实体对象, 由一个红黑树来进行组织(struct rb_root nodes) ; rb_node 则对应nodes的一个节点 */ union { //用于本节点连接红黑树 struct rb_node rb_node; // 如果Binder 实体对象对应的进程死亡,销毁节点时需要将rb_node从红黑树中删除, //如果本节点还没有引用切断,则用dead_node将其隔离到另一个链表中, //直到通知所有进程切断与该节点的引用后,该节点才能销毁 struct hlist_node dead_node; // 指向该Binder实体对象 对应的进程,进程由binder_proc描述 struct binder_proc *proc; // 该 Binder实体对象可能同时被多个Client组件引用,所有指向本实体对象的引用都 //保存在这个hash队列中refs表示队列头部;这些引用可能隶属于不同进程,遍历该 //hash表能够得到这些Client组件引用了这些对象 struct hlist_head refs; //远程强引用计数 int internal_strong_refs; //实际上代表了一个binder_node与多少个binder_ref相关联 //本地弱引用技数 int local_weak_refs; //本地强引用计数 int local_strong_refs; unsigned has_strong_ref:1; unsigned pending_strong_ref:1; unsigned has_weak_ref:1; unsigned pending_weak_ref:1; /** 用来描述用户控件中的一个Service组件 */ // 描述用户控件的Service组件,对应Binder实体对应的Service在用户控件的(BBinder)的引用 binder_uintptr_t ptr; // 描述用户空间的Service组件,Binder实体对应的Service在用户控件的本地Binder(BBinder)地址 binder_uintptr_t cookie; // 异步事务处理,单独讲解 unsigned has_async_transaction:1; struct list_head async_todo; // 表示该Binder实体对象能否接收含有该文件描述符的进程间通信数据。当一个进程向 //另一个进程发送数据中包含文件描述符时,Binder会在目标进程中打开一个相同的文件 //故设为accept_fds为0 可以防止源进程在目标进程中打开文件 unsigned accept_fds:1; // 处理Binder请求的线程最低优先级 unsigned min_priority:8;

binder_node 是binder实体,由驱动创建,对应着每个服务,内部包含进程信息binder_proc,还有binder的引用binder_ref。

binder_ref

struct binder_ref {

 //debug 调试用的 

 int debug_id; 

 /** binder_proc中使用红黑树(对应两个rb_root变量) 来存储器内部所有引用对象, 

 *下面的rb_node则是红黑树中的节点 

 //Binder引用的宿主进程 

 struct binder_proc *proc; 

 //对应 refs_by_desc,以句柄desc索引 关联到binder_proc- refs_by_desc红黑树 

 struct rb_node rb_node_desc; 

 //对应refs_by_node,以Binder实体对象地址作为关键字关联到binder_proc- refs_by_node红黑树 

 struct rb_node rb_node_node; 

 /** Client通过Binder访问Service时,仅需指定一个句柄,Binder通过该desc找到对应的binder_ref, 

 * 再根据该binder_ref中的node变量得到binder_node(实体对象),进而找到对应的Service组件 

 // 对应Binder实体对象中(hlist_head) refs引用对象队列中的一个节点 

 struct hlist_node node_entry; 

 // 引用对象所指向的Binder实体对象 

 struct binder_node *node; 

 // Binder引用的句柄值,Binder驱动为binder驱动引用分配一个唯一的int型整数(进程范围内唯一) 

 // ,通过该值可以在binder_proc- refs_by_desc中找到Binder引用,进而可以找到Binder引用对应的Binder实体 

 uint32_t desc; 

 // 强引用 计数 

 int strong; 

 // 弱引用 计数 

 int weak; 

 // 表示Service组件接受到死亡通知 

 struct binder_ref_death *death; 

Binder的引用对象,每一个Clinet在驱动中都有一个binder_ref和他对应,内部有对应的进程信息binder_proc和binder_node信息。

client端拿着BpBinder引用对象,在驱动层对应着binder_ref,就可以找到binder_proc和binder_node信息,找到指定的进程可以实现交互。

binder_buffer

struct binder_buffer {

 //entry对应内核缓冲区列表的buffers(内核缓冲区列表) 

 struct list_head entry; /* free and allocated entries by address */ 

 //结合free,如果free=1,则rb_node对应free_buffers中一个节点(内核缓冲区) 

 //如果free!=1,则对应allocated_buffers中的一个节点 

 struct rb_node rb_node; /* free entry by size or allocated entry */ 

 /* by address */ 

 unsigned free:1; 

 /** Binder将事务数据保存到一个内核缓冲区(binder_transaction.buffer),然后交由Binder 

 * 实体对象(target_node) 处理,而target_node会将缓冲区的内容交给对应的Service组件 

 * (proc) 来处理,Service组件处理完事务后,若allow_user_free=1,则请求Binder释放该 

 * 内核缓冲区 

 unsigned allow_user_free:1; 

 // 描述一个内核缓冲区正在交给那个事务transaction,用以中转请求和返回结果 

 struct binder_transaction *transaction; 

 // 描述该缓冲区正在被那个Binder实体对象使用 

 struct binder_node *target_node; 

 //表示事务时异步的;异步事务的内核缓冲区大小是受限的,这样可以保证事务可以优先放到缓冲区 

 unsigned async_transaction:1; 

 //调试专用 

 unsigned debug_id:29; 

 /** 

 * 存储通信数据,通信数据中有两种类型数据:普通数据与Binder对象 

 * 在数据缓冲区最后,有一个偏移数组,记录数据缓冲区中每一个Binder 

 * 对象在缓冲区的偏移地址 

 // 数据缓冲区大小 

 size_t data_size; 

 // 偏移数组的大小(其实也是偏移位置) 

 size_t offsets_size; 

 // 用以保存通信数据,数据缓冲区,大小可变 

 uint8_t data[0]; 

 // 额外缓冲区大小 

 size_t extra_buffers_size; 

进程间,用户空间的数据不可共享,所以用户空间 = 不可共享空间
进程间,内核空间的数据可共享,所以内核空间 = 可共享空间

binder利用mmap()将用户空间映射到内核空间,使得数据的交互只需要一次。client在每次发起请求的时候会构造一个binder_buffer,放置在binder_transaction.buffer。

binder_thread

进程在启动时会创建一个binder线程池,并向其中注册一个Binder线程;驱动发现没有空闲binder线程时会向Server进程注册新的的binder线程。对于一个Server进程有一个最大Binder线程数限制,默认为16个binder线程。对于所有Client端进程的binder请求都是交由Server端进程的binder线程来处理的。

binder_work

一次进程请求的工作,会被封装在transaction里面,放置在对应进程的todo中。

binder_transaction

struct binder_transaction {

 //调试调用 

 int debug_id; 

 // 用来描述的处理的工作事项,这里会将type设置为BINDER_WORK_TRANSACTION,具体结构看binder_work 

 struct binder_work work; 

 /** 源线程 */ 

 // 源线程,即发起事务的线程 

 struct binder_thread *from; 

 // 源线程的优先级 

 long priority; 

 //源 线程的用户 ID 

 kuid_t sender_euid; 

 /** 目标线程*/ 

 // 目标进程:处理该事务的进程 

 struct binder_proc *to_proc; 

 // 目标线程:处理该事务的线程 

 struct binder_thread *to_thread; 

 // 表示另一个事务要依赖事务(不一定要在同一个线程中) 

 struct binder_transaction *from_parent; 

 // 目标线程下一个需要处理的事务 

 struct binder_transaction *to_parent; 

 // 标志事务是同步/异步;设为1表示同步事务,需要等待对方回复;设为0异步 

 unsigned need_reply:1; 

 /* unsigned is_dead:1; */ /* not used at the moment */ 

 /* 参考binder_buffer中解释,指向Binder为该事务分配内核缓冲区 

 * code与flag参见binder_transaction_data 

 struct binder_buffer *buffer; 

 unsigned int code; 

 unsigned int flags; 

 /** 目标线程设置事务钱,Binder需要修改priority;修改前需要将线程原来的priority保存到 

 * saved_priority中,用以处理完事务回复到原来优先级 

 * 优先级设置:目标现场处理事务时,优先级应不低于目标Serivce要求的线程优先级,也 

 * 不低于源线程的优先级,故设为两者的较大值。 

 long saved_priority; 

transact() 调用在驱动里都会生产一个binder_transaction,放在对应server进程的todo队列里,然后唤醒server的binder线程来最终完成onTransact()调用。我们这里说的进程间通信其实就是发送端把binder_transaction,放到目标进程或其子线程的todo队列中,等目标进程或线程不断循环地从todo队列中取出数据并进行相应的操作。

binder_transaction_data

struct binder_transaction_data {

 union {

 /* target descriptor of command transaction */ 

 __u32 handle; 

 /* target descriptor of return transaction */ 

 binder_uintptr_t ptr; 

 } target; 

 //Binder实体带有的附加数据 

 binder_uintptr_t cookie; /* target object cookie */ 

 // code是一个命令,描述了请求Binder对象执行的操作,表示要对目标对象请求的命令代码 

 __u32 code; /* transaction command */ 

 // 事务标志,详细看transaction_flag结构体 

 __u32 flags; 

 // 发起请求的进程PID 

 pid_t sender_pid; 

 // 发起请求的进程UID 

 uid_t sender_euid; 

 // data.buffer缓冲区的大小,命令的真正要传输的数据就保存data.buffer缓冲区 

 binder_size_t data_size; /* number of bytes of data */ 

 // data.offsets缓冲区的大小 

 binder_size_t offsets_size; /* number of bytes of offsets */ 

 union {

 struct {

 /* transaction data */ 

 binder_uintptr_t buffer; 

 /* offsets from buffer to flat_binder_object structs */ 

 binder_uintptr_t offsets; 

 } ptr; 

 __u8 buf[8]; 

 } data; 

binder_transaction_data 是进程一次传输的数据,data.buffer可能是普通数据或者是flat_binder_object(封装了binder引用或者binder对象)。binder驱动需要对此做处理。

flat_binder_object

struct flat_binder_object {

 unsigned long type; 

 unsigned long flags; 

 union {

 void *binder; //BBinder,可以找到对应的binder_node 

 signed long handle; //BpBinder,可以找到对应的binder_ref 

 void *cookie; 

在进程将传输过程中,binder对象会被封装到flat_binder_object进行传输。

驱动根据type来判断,如果是BINDER_TYPE_BINDER,表示传输的是binder实体,会在内核创建binder_node,并创建binder_ref的flat_binder_object给目标进程,因为给远程的只能是binder_ref;如果是BINDER_TYPE_HANDLE,表示传输的是binder引用。

总结

以上注释的资料来自:https://www.jianshu.com/p/5740a8447324

6. Binder的基本流程

这里不罗列Binder的大部分代码,都是以流程的形式讲述。


每个进程启动的时候,都会打开binder驱动
在binder驱动中会创建对应的进程的binder_proc对象,并把当前进程等信息保存到binder_proc对象,并加入到全局链表 调用mmap()分配内存映射空间,驱动层创建binder_buffer,并放入当前binder_proc的buffers 链表 创建一个线程池(Binder主线程),负责处理Binder驱动发送上来的一些请求或返回值,线程的创建和销毁是在用户空间进行的,但是线程是受驱动层控制。可以说,这些线程就是真正负责干活的。 如果是ServerManager进程,驱动会创建binder_node,通过BINDER_SET_CONTEXT_MGR命令,成为ServerManager,ServiceManager进入循环,不断监听客户端的请求,没有请求时ioctl将阻塞,有请求到达是就分析请求类型,做出相应的处理
server 通过flat_binder_object(内部含有binder对象),经内核驱动时会创建一个binder_node,挂载到对应的进程的binder_proc; 内核会创建一个binder_ref,挂在binder_node下,同时提供给ServiceManager,注册完成; server 的线程会在binder_proc的wait队列上等待,等待binder_work。
我们先假设client跟ServiceManager拿到了Server的BpBinder了, client调用transact()时候,在内核驱动会根据BpBinder指向的binder_ref,找到binder_node,创建了一个binder_transaction插入到对应server进程binder_prc的todo队列,唤醒server进程下的binder_thread。 同时将client的调用线程阻塞放在wait队列,等待结果 server端的thread从binder_proc的todo列表取出,最终执行on_transact(), 处理完成以后,向内核发送命令,内核构造一个binder_transaction放在client调用线程的todo中,同时唤醒client调用线程。 被唤醒的client线程,处理todo的binder_transaction。

那么其实client同ServiceManager查询服务也是一样的道理,通过0号引用,默认拿到了ServiceManager的BpBinder了,后续的流程也是类似的。

总体架构

不足

本篇文章写得很凌乱,主要有两个原因:

个人对binder的了解不够深入; binder涉及的东西太多了,还有很多东西没有涉及到;

后续要再好好学习,重新整理。

参考

https://www.moyokoo.com/p/25/
http://gityuan.com/2015/11/28/binder-summary/
https://www.jianshu.com/p/3c71473e7305
https://www.cnblogs.com/everhad/p/6246551.html

6293.html

app程序应用开发手机开发无线开发移动端开发