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的调用流程:

  1. 客户端获取IBinder接口,即Binder的代理对象,调用add服务。

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

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

从上面的Binder 的使用流程我们可以知道几个事情:

  1. 服务端和客户端是两个进程,客户端调用transact(),服务端实现onTransact()
  2. Binder的调用,在客户端看来是同步的,transact()从调用到获取结果的过程。
  3. Binder的被调用,在服务端看来是异步,支持多个客户端调用,服务端底层是线程池。所以可能需要注意同步的问题。
  4. 如果服务端和客户端在同一个进程,是不会发生Binder通信的,而是在asInterface返回服务端的对象。
  5. onTransact可以做权限验证,拒绝调用。
  6. 客户端在调用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_node

struct binder_node {
    
    // debug调试用的 
    int debug_id; 
    struct binder_work work;  //binder驱动中进程要处理的工作项  
 
    /** 每一个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的大部分代码,都是以流程的形式讲述。

  1. binder驱动初始化

    1. 每个进程启动的时候,都会打开binder驱动
      在binder驱动中会创建对应的进程的binder_proc对象,并把当前进程等信息保存到binder_proc对象,并加入到全局链表
    2. 调用mmap()分配内存映射空间,驱动层创建binder_buffer,并放入当前binder_proc的buffers 链表
    3. 创建一个线程池(Binder主线程),负责处理Binder驱动发送上来的一些请求或返回值,线程的创建和销毁是在用户空间进行的,但是线程是受驱动层控制。可以说,这些线程就是真正负责干活的。
    4. 如果是ServerManager进程,驱动会创建binder_node,通过BINDER_SET_CONTEXT_MGR命令,成为ServerManager,ServiceManager进入循环,不断监听客户端的请求,没有请求时ioctl将阻塞,有请求到达是就分析请求类型,做出相应的处理
  2. Server怎么注册服务

    1. server 通过flat_binder_object(内部含有binder对象),经内核驱动时会创建一个binder_node,挂载到对应的进程的binder_proc;
    2. 内核会创建一个binder_ref,挂在binder_node下,同时提供给ServiceManager,注册完成;
    3. server 的线程会在binder_proc的wait队列上等待,等待binder_work。
  3. Client怎么调用服务

    1. 我们先假设client跟ServiceManager拿到了Server的BpBinder了,
    2. client调用transact()时候,在内核驱动会根据BpBinder指向的binder_ref,找到binder_node,创建了一个binder_transaction插入到对应server进程binder_prc的todo队列,唤醒server进程下的binder_thread。
    3. 同时将client的调用线程阻塞放在wait队列,等待结果
    4. server端的thread从binder_proc的todo列表取出,最终执行on_transact(),
    5. 处理完成以后,向内核发送命令,内核构造一个binder_transaction放在client调用线程的todo中,同时唤醒client调用线程。
    6. 被唤醒的client线程,处理todo的binder_transaction。

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

总体架构

不足

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

  1. 个人对binder的了解不够深入;
  2. 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


发布评论
IT虾米网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!

View.post(Runnble)的一点小问题详解
你是第一个吃螃蟹的人
发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。