Binder机制总结

在Linux系统里面,进程之间是相互隔离的,也就是说进程之间的各个数据是互相独立,互不影响,而若是一个进程崩溃了,也不会影响到另外一个进程。node

Android系统其底层是采用Linux做为基底,上层采用包含虚拟机的Java层以及Native层,经过系统调用(Syscall)连通系统的内核空间与用户空间。用户空间主要采用C++和Java代码,经过JNI技术打通用户空间的Java层和Native层(C++/C)。linux

每一个Android的进程,只能运行在本身进程所拥有的虚拟地址空间。对于用户空间,不一样进程之间彼此是不能共享的,而内核空间倒是可共享的。Client进程向Server进程通讯,偏偏是利用进程间可共享的内核内存空间来完成底层通讯工做的,Client端与Server端进程每每采用ioctl等方法跟内核空间的驱动进行交互。真正通讯的核心环节仍是在Binder Driver。git

用户空间/内核空间:

Linux Kernel是操做系统的核心,独立于普通的应用程序,能够访问受保护的内存空间,也有访问底层硬件设备的全部权限。缓存

对于Kernel这么一个高安全级别的东西,显然是不允许其它的应用程序随便调用或访问的,因此须要对Kernel提供必定的保护机制,这个保护机制用来告诉那些应用程序,你只能够访问某些许可的资源,不准可的资源是拒绝被访问的,因而就把Kernel和上层的应用程序抽像的隔离开,分别称之为Kernel Space和User Space。安全

用户空间访问内核空间的惟一方式就是系统调用;经过这个统一入口接口,全部的资源访问都是在内核的控制下执行,以避免致使对用户程序对系统资源的越权访问,从而保障了系统的安全和稳定。网络

为何要使用Binder

Linux现有的全部进程间IPC方式:数据结构

  1. 管道:在建立时分配一个page大小的内存,缓存区大小比较有限;
  2. 消息队列:信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通讯;
  3. 共享内存:无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操做系统没法实现,必须各进程利用同步工具解决; 4.套接字:做为更通用的接口,传输效率低,主要用于不通机器或跨网络的通讯;
  4. 信号量:常做为一种锁机制,防止某进程正在访问共享资源时,其余进程也访问该资源。所以,主要做为进程间以及同一进程内不一样线程之间的同步手段。
  5. 信号: 不适用于信息交换,更适用于进程中断控制,好比非法内存访问,杀死某个进程等;

Android使用的Linux内核拥有着很是多的跨进程通讯机制,好比管道,System V,Socket等;为何还须要单独搞一个Binder出来呢?主要有两点,性能和安全。在移动设备上,普遍地使用跨进程通讯确定对通讯机制自己提出了严格的要求;Binder相对出传统的Socket方式,更加高效;另外,传统的进程通讯方式对于通讯双方的身份并无作出严格的验证,只有在上层协议上进行架设;好比Socket通讯ip地址是客户端手动填入的,均可以进行伪造;而Binder机制从协议自己就支持对通讯双方作身份校检,于是大大提高了安全性。这个也是Android权限模型的基础。架构

为何 Android 要采用 Binder 做为 IPC 机制?并发

Binder是什么

  • 一般意义下,Binder指的是一种通讯机制;咱们说AIDL使用Binder进行通讯,指的就是Binder这种IPC机制。
  • 对于Server进程来讲,Binder指的是Binder本地对象
  • 对于Client来讲,Binder指的是Binder代理对象,它只是Binder本地对象的一个远程代理;对这个Binder代理对象的操做,会经过驱动最终转发到Binder本地对象上去完成;对于一个拥有Binder对象的使用者而言,它无须关心这是一个Binder代理对象仍是Binder本地对象;对于代理对象的操做和对本地对象的操做对它来讲没有区别。
  • 对于传输过程而言,Binder是能够进行跨进程传递的对象;Binder驱动会对具备跨进程传递能力的对象作特殊处理:自动完成代理对象和本地对象的转换。在驱动中,Binder本地对象的表明是一个叫作binder_node的数据结构,Binder代理对象是用binder_ref表明的

Binder架构

binder在framework层,采用JNI技术来调用native(C/C++)层的binder架构,从而为上层应用程序提供服务。在native层中,binder是C/S架构,分为Bp端(BpBinder - Client)和Bn端(BBinder - Server)。app

  • 图中红色表明整个framework层binder架构(C/S架构);Binder类表明Server端,BinderProxy类代码Client端;
  • 图中蓝色表明Native层Binder架构相关组件,分为Bp端(BpBinder - Client)和Bn端(BBinder - Server);
  • Binder 在 framework 层进行了封装,经过 JNI 技术调用 Native(C/C++)层的 Binder 架构。
  • Binder 在 Native 层以 ioctl 的方式与 Binder 驱动通信。

上层framework层的Binder逻辑是创建在Native层架构基础之上的,核心逻辑都是交予Native层方法来处理。也只有真正理解了Native层Binder架构,才能算掌握的Binder。

Client进程只不过是持有了Server端的代理;代理对象协助驱动完成了跨进程通讯。

native层的Binder通讯采用C/S架构,包含client、server、serviceManager、Binder驱动,client / server / serviceManager 位于用户空间,Binder驱动位于内核空间。

  • Native层的ServiceManager(C++),用于管理系统中的各类服务,是整个Binder通讯机制的大管家。

  • BpBinder(客户端)BBinder(服务端) 都是从IBinder类中派生而来的务。

    client端:BpBinder.transact()来发送事务请求;
      server端:BBinder.onTransact()会接收到相应事务。
    复制代码

Binder原理

Native层的ServiceManager是整个Binder通讯机制的大管家,是Android进程间通讯机制Binder的守护进程, 要掌握Binder机制,首先须要了解系统是如何首次启动ServiceManager。当Service Manager启动以后,Client端和Server端通讯时都须要先获取Service Manager接口,才能开始通讯服务。

图中的Client、Server、ServiceManager之间交互都是虚线表示,是因为它们彼此之间不是直接交互的,而是都经过与Binder Driver进行交互的,从而实现IPC通讯方式。其中Binder驱动位于内核空间,Client、Server、ServiceManager位于用户空间。

Client、Server、ServiceManager都位于不一样的进程之中,是基于Binder机制通讯,那么一样也是C/S架构,则图中的3大步骤都有相应的Client端与Server端。这3大过程每一次都是一个完整的Binder IPC过程。

一、注册服务:首先AMS注册到ServiceManager。该过程:AMS所在进程(system_server)是客户端,ServiceManager是服务端。

二、获取服务:Client进程使用AMS前,须先向ServiceManager中获取AMS的代理类AMP。该过程:AMP所在进程(app process)是客户端,ServiceManager是服务端。

三、使用服务: app进程根据获得的代理类AMP,即可以直接与AMS所在进程交互。该过程:AMP所在进程(app process)是客户端,AMS所在进程(system_server)是服务端。

  • 首先须要注册服务端,只有注册了服务端,客户端才有通信的目标,服务端经过 ServiceManager 注册服务,注册的过程就是向 Binder 驱动的全局链表 binder_procs 中插入服务端的信息(binder_proc 结构体,每一个 binder_proc 结构体中都有 todo 任务队列),而后向 ServiceManager 的 svcinfo 列表中缓存一下注册的服务。

  • 有了服务端,客户端就能够跟服务端通信了,通信以前须要先获取到服务,拿到服务的代理,也能够理解为引用。好比下面的代码://获取WindowManager服务引用

    WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);

    获取服务端的方式就是经过 ServiceManager 向 svcinfo 列表中查询一下返回服务端的代理,svcinfo 列表就是全部已注册服务的通信录,保存了全部注册的服务信息。

  • 有了服务端的引用咱们就能够向服务端发送请求了,经过 BinderProxy 将咱们的请求参数发送给 ServiceManager,经过共享内存的方式使用内核方法 copy_from_user() 将咱们的参数先拷贝到内核空间,这时咱们的客户端进入等待状态,而后 Binder 驱动向服务端的 todo 队列里面插入一条事务,执行完以后把执行结果经过 copy_to_user() 将内核的结果拷贝到用户空间(这里只是执行了拷贝命令,并无拷贝数据,binder只进行一次拷贝),唤醒等待的客户端并把结果响应回来,这样就完成了一次通信。

Binder通讯简述

前面说过Binder机制的3大步骤(注册服务、获取服务、使用服务)都有相应的client端和server端。

Client进程经过RPC与Server通讯,能够简单地划分为三层,驱动层、IPC层、业务层。demo()即是Client端和Server共同协商好的统一方法;handle、RPC数据、代码、协议这4项组成了IPC层的数据,经过IPC层进行数据传输;而真正在Client和Server两端创建通讯的基础设施即是Binder Driver。

例如,当名为BatteryStatsService的Server向ServiceManager注册服务的过程当中,IPC层的数据组成为:Handle=0,RPC代码为ADD_SERVICE_TRANSACTION,RPC数据为BatteryStatsService,Binder协议为BC_TRANSACTION。

Binder通讯协议

Binder协议包含在IPC数据中,分为两类:

  • BINDER_COMMAND_PROTOCOL:binder请求码,以”BC_“开头,简称BC码,用于从IPC层传递到Binder Driver层;

  • BINDER_RETURN_PROTOCOL :binder响应码,以”BR_“开头,简称BR码,用于从Binder Driver层传递到IPC层;

Binder IPC通讯至少是两个进程的交互:

  • client进程执行binder_thread_write,根据BC_XXX命令,生成相应的binder_work;

  • server进程执行binder_thread_read,根据binder_work.type类型,生成BR_XXX,发送到用户空间处理。

Binder通讯过程

其中binder_work.type共有6种类型:

  • BINDER_WORK_TRANSACTION //最多见类型
  • BINDER_WORK_TRANSACTION_COMPLETE
  • BINDER_WORK_NODE
  • BINDER_WORK_DEAD_BINDER
  • BINDER_WORK_DEAD_BINDER_AND_CLEAR
  • BINDER_WORK_CLEAR_DEATH_NOTIFICATION

一次完整的通信模型以下:

  • BC码,用于从IPC层传递到Binder Driver层;
  • BR码,用于从Binder Driver层传递到IPC层;

oneway与非oneway: 都是须要等待Binder Driver的回应消息BR_TRANSACTION_COMPLETE。 主要区别在于oneway的通讯收到BR_TRANSACTION_COMPLETE则返回,而不会再等待BR_REPLY消息的到来。另外,oneway的binder IPC则接收端没法获取对方的pid。

Binder内存模型

虚拟进程地址空间(vm_area_struct)和虚拟内核地址空间(vm_struct)都映射到同一块物理内存空间。当Client端与Server端发送数据时,Client(做为数据发送端)先从本身的进程空间把IPC通讯数据copy_from_user拷贝到内核空间,而Server端(做为数据接收端)与内核共享数据,再也不须要拷贝数据,而是经过内存地址空间的偏移量,便可获悉内存地址,整个过程只发生一次内存拷贝。通常地作法,须要Client端进程空间拷贝到内核空间,再由内核空间拷贝到Server进程空间,会发生两次拷贝。

对于进程和内核虚拟地址映射到同一个物理内存的操做是发生在数据接收端,而数据发送端仍是须要将用户态的数据复制到内核态。到此,可能有读者会好奇,为什么不直接让发送端和接收端直接映射到同一个物理空间,那样就连一次复制的操做都不须要了,0次复制操做那就与Linux标准内核的共享内存的IPC机制没有区别了,对于共享内存虽然效率高,可是对于多进程的同步问题比较复杂,而管道/消息队列等IPC须要复制2两次,效率较低。这里就不先展开讨论Linux现有的各类IPC机制跟Binder的详细对比,总之Android选择Binder的基于速度和安全性的考虑。

下面这图是从Binder在进程间数据通讯的流程图,从图中更能明了Binder的内存转移关系。

Binder线程池建立

Android系统启动完成后,ActivityManager、PackageManager等各大服务都运行在system_server进程里,app进程若是须要使用系统服务须要经过binder,来完成进程之间的通讯。但无论是system_server进程,仍是app进程,都是经过zygote fork 出来的,而这个fork的过程,也就是建立新进程时会执行onZygoteInit(),以启动binder线程池。

Binder线程的建立:在其所在进程的建立过程当中,Binder线程也随之产生。Java层进程的建立都是经过Process.start()方法,向Zygote进程发出建立进程的socket消息,Zygote收到消息后会调用Zygote.forkAndSpecialize()来fork出新进程,在新进程中会调用到RuntimeInit.nativeZygoteInit方法,该方法通过jni映射,最终会调用到app_main.cpp中的onZygoteInit()中。

每一个应用进程只容许启动一个binder线程池,且本次建立的是binder主线程(isMain=true),而且主线程是不会退出的;其他binder线程池中的线程都是由Binder驱动根据IPC通讯需求来控制建立的,当线程执行binder_thread_read的过程当中,发现当前没有空闲线程,且没有达到上限,则建立新的binder线程。

每次由Zygote fork出新进程的过程当中,伴随着建立binder线程池,调用spawnPooledThread来建立binder主线程。当线程执行binder_thread_read的过程当中,发现当前没有空闲线程,没有请求建立线程,且没有达到上限,则建立新的binder线程。

Binder进程与线程

对于底层Binder驱动,经过binder_procs链表记录全部建立的binder_proc结构体,binder驱动层的每个binder_proc结构体都与用户空间的一个用于binder通讯的进程一一对应,且每一个进程有且只有一个ProcessState对象,这是经过单例模式来保证的。在每一个进程中能够有不少个线程,每一个线程对应一个IPCThreadState对象,IPCThreadState对象也是单例模式,即一个线程对应一个IPCThreadState对象,在Binder驱动层也有与之相对应的结构,那就是Binder_thread结构体。在binder_proc结构体中经过成员变量rb_root threads,来记录当前进程内全部的binder_thread。

Binder线程池:每一个Server进程在启动时会建立一个binder线程池,并向其中注册一个Binder线程;以后Server进程也能够向binder线程池注册新的线程,或者Binder驱动在探测到没有空闲binder线程时会主动向Server进程注册新的的binder线程。对于一个Server进程有一个最大Binder线程数限制,默认为16个binder线程。对于全部Client端进程的binder请求都是交由Server端进程的binder线程来处理的。

Binder传输过程

Binder IPC机制,就是指在进程间传输数据(binder_transaction_data),一次数据的传输,称为事务(binder_transaction)。对于多个不一样进程向同一个进程发送事务时,这个同一个进程或线程的事务须要串行执行,在Binder驱动中为binder_proc和binder_thread都有todo队列。

也就是说对于进程间的通讯,就是发送端把binder_transaction节点,插入到目标进程或其子线程的todo队列中,等目标进程或线程不断循环地从todo队列中取出数据并进行相应的操做。

在Binder驱动层,每一个接收端进程都有一个todo队列,用于保存发送端进程发送过来的binder请求,这类请求能够由接收端进程的任意一个空闲的binder线程处理;接收端进程存在一个或多个binder线程,在每一个binder线程里都有一个todo队列,也是用于保存发送端进程发送过来的binder请求,这类请求只能由当前binder线程来处理。binder线程在空闲时进入可中断的休眠状态,当本身的todo队列或所属进程的todo队列有新的请求到来时便会唤醒,若是是由所需进程唤醒的,那么进程会让其中一个线程处理响应的请求,其余线程再次进入休眠状态。

Binder死亡回调

死亡通知是为了让Bp端(客户端进程)进能知晓Bn端(服务端进程)的生死状况,当Bn端进程死亡后能通知到Bp端。

Binder驱动

Binder Driver是Android专有的,但底层的驱动架构与Linux驱动是同样。运行在内核空间的,负责各个用户进程经过Binder进行通讯的内核模块。

  • 将misc设备(字符驱动设备)注册在设备目录/dev下,做为虚拟字符设备,用户经过dev/binder访问它;
  • 但它并无直接操做硬件,只是对设备内存的处理。
  • 工做在内核态,提供了驱动设备的初始化(binder_init)、打开(binder_open)、映射(binder_mmap)、数据操做(binder_ioctl)等标准文件操做。

用户态的程序调用Kernel层驱动是须要陷入内核态,进行系统调用(syscall),好比打开Binder驱动方法的调用链为: open-> __open() -> binder_open()。当用户空间调用open()/mmap()/ioctl()方法,最终会调用binder驱动的binder_open()/binder_mmap()/binder_ioctl(),从用户态进入内核态。

binder核心方法:

  • binder_init :注册misc设备,建立dev/binder设备节点

  • binder_open *:打开binder驱动设备,获取Binder Driver的文件描述符。建立binder_proc对象,并把当前进程等信息保存到binder_proc对象,该对象管理IPC所需的各类信息并拥有其余结构体的根结构体;再把binder_proc对象保存到文件指针filp,以及把binder_proc加入到全局链表binder_procs。

  • binder_mmap :首先在内核虚拟地址空间,申请一块与用户虚拟内存相同大小的内存;而后再申请1个page大小的物理内存,再将同一块物理内存分别映射到内核虚拟地址空间和用户虚拟内存空间,从而实现了用户空间的Buffer和内核空间的Buffer同步操做的功能。

  • binder_ioctl :负责在两个进程间收发IPC数据和IPC reply数据。

serviceManager

ServiceManager是Binder IPC通讯过程当中的守护进程,是由init进程经过解析init.rc文件而建立的,其所对应的可执行程序/system/bin/servicemanager,进程名为/system/bin/servicemanager。servicemanager的核心工做就是注册服务和查询服务。

ServiceManger集中管理系统内的全部服务,经过权限控制进程是否有权注册服务,经过字符串名称来查找对应的Service; 因为ServiceManger进程跟全部向其注册的服务创建死亡通知, 那么当服务所在进程死亡后, 都将告知ServiceManager. 每一个Client经过查询ServiceManager可获取Server进程的状况,下降全部Client进程直接检测会致使负载太重。

  • ServiceManager 分为 framework 层和 native 层,framework 层只是对 native 层进行了封装方便调用,图上展现的是 native 层的 ServiceManager 启动过程。

ServiceManager的启动流程

ServiceManager#main()

一、经过系统调用陷入内核,打开Binder设备驱动,在Binder驱动层建立一个binder_proc对象,同时放入全局链表binder_procs,再经过ioctl()检验当前binder版本与Binder驱动层的版本是否一致;调用mmap()进行内存映射,同理mmap()方法通过系统调用,对应于Binder驱动层的binder_mmap()方法,该方法会在Binder驱动层建立Binder_buffer对象,并放入当前binder_proc的proc->buffers链表。

二、注册成为binder服务的大管家,通知binder驱动使其成为守护进程:binder_become_context_manager;

三、验证selinux权限,判断进程是否有权注册或查看指定服务;

四、进入无限循环,处理client端发来的请求:binder_loop;

ServiceManager的获取过程

当进程注册服务(addService)或 获取服务(getService)的过程以前,都须要先调用defaultServiceManager()方法来获取gDefaultServiceManager对象。对于gDefaultServiceManager对象(单例),若是存在则直接返回;若是不存在则建立该对象。

gDefaultServiceManager的建立过程,可分解为如下3个步骤:

defaultServiceManager 等价于 new BpServiceManager(new BpBinder(0));defaultServiceManager()返回的是BpServiceManager对象,用于跟servicemanager进程通讯;

一、 ProcessState::self():用于获取ProcessState对象(单例),每一个进程有且只有一个ProcessState对象(一个进程只能打开binder设备一次,其中ProcessState的成员变量mDriverFD记录binder驱动的fd,用于访问binder设备。),若是存在则直接返回,不存在则建立,ProcessState的建立过程:

  • 调用open(),打开/dev/binder驱动设备;

  • 再利用mmap(),建立大小为1M-8K的内存地址空间,映射内核的地址空间,用来接收事物;

  • 设定当前进程最大的最大并发Binder线程个数为16。

二、getContextObject(): 获取BpBinder对象,对于handle=0的BpBinder对象(在整个Binder系统中handle=0表明ServiceManager所对应的BBinder),若是存在则直接返回,不存在才建立。BpBinder经过handler来指向所对应BBinder, 。

三、interface_cast():用于获取BpServiceManager对象,

注册服务

  • 注册服务: do_add_service():先检查权限,检查selinux权限是否知足;服务检索,根据服务名来查询匹配的服务;当查询到已存在同名的服务,则先清理该服务信息,再将当前的服务加入到服务列表svclist(记录服务名和handle信息)

  • 注册 MediaPlayerService 服务端,咱们经过 ServiceManager 的 addService() 方法来注册服务。

  • 首先 ServiceManager 向 Binder 驱动发送 BC_TRANSACTION 命令(ioctl 的命令,BC 能够理解为 binder client 客户端发过来的请求命令)携带 ADD_SERVICE_TRANSACTION 命令,同时注册服务的线程进入等待状态 waitForResponse()。 Binder 驱动收到请求命令向 ServiceManager 的 todo 队列里面添加一条注册服务的事务。事务的任务就是建立服务端进程 binder_node 信息并插入到 binder_procs 链表中。

  • 事务处理完以后发送 BR_TRANSACTION 命令,ServiceManager 收到命令后向 svcinfo 列表中添加已经注册的服务。最后发送 BR_REPLY 命令唤醒等待的线程,通知注册成功。

获取服务

  • 查询服务: do_find_service():查询到目标服务,并返回该服务所对应的handle。从svclist服务列表中,根据服务名遍历查找是否已经注册。当服务已存在svclist,则返回相应的服务名,不然返回NULL。当找到服务的handle, 则调用bio_put_ref(reply, handle),将handle封装到reply

  • 获取服务的过程与注册相似,相反的过程。经过 ServiceManager 的 getService() 方法来注册服务。

  • 首先 ServiceManager 向 Binder 驱动发送 BC_TRANSACTION 命令携带 CHECK_SERVICE_TRANSACTION 命令,同时获取服务的线程进入等待状态 waitForResponse()。

  • Binder 驱动收到请求命令向 ServiceManager 的发送 BC_TRANSACTION 查询已注册的服务,查询到直接响应 BR_REPLY 唤醒等待的线程。若查询不到将与 binder_procs 链表中的服务进行一次通信再响应。

使用服务

  • 咱们在使用 Binder 时基本都是调用 framework 层封装好的方法,AIDL 就是 framework 层提供的傻瓜式是使用方式。假设服务已经注册完,咱们来看看客户端怎么执行服务端的方法。

  • 首先咱们经过 ServiceManager 获取到服务端的 BinderProxy 代理对象,经过调用 BinderProxy 将参数,方法标识(例如:TRANSACTION_test,AIDL中自动生成)传给 ServiceManager,同时客户端线程进入等待状态。

  • ServiceManager 将用户空间的参数等请求数据复制到内核空间,并向服务端插入一条执行执行方法的事务。事务执行完通知 ServiceManager 将执行结果从内核空间复制到用户空间,并唤醒等待的线程,响应结果,通信结束。

参考:

Gityuan的 Binder系列 (基于 Android 6.0)

一篇文章了解相见恨晚的 Android Binder 进程间通信机制

Binder学习指南

老罗的 Android进程间通讯(IPC)机制Binder简要介绍和学习计划 系列

相关文章
相关标签/搜索