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

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

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/6293.html

(0)
上一篇 2021年7月17日
下一篇 2021年7月17日

相关推荐

发表回复

登录后才能评论