概述
最近在学习 Binder 机制,在网上查阅了大量的材料,也看了老罗的 Binder 系列的博客和 Innost 的深刻了解 Binder 系列的博客,都是从底层开始讲的,全是 C 代码,尽管之前学过 C 和 C ++,然而各种函数之间花式跳转,看的我都狐疑人生。毫不夸大的讲每看一遍都是新的内容,跟没看过一样。起初又看到了 Gityuan 的博客看到了一些图解好像发现了新大陆。
上面就以图解的形式介绍下 Binder 机制,置信你看这篇文章,肯定有所播种。
什么是 Binder?
Binder 是 Android 零碎中过程间通信(IPC)的一种形式,也是 Android 零碎中最重要的个性之一。Android 中的四大组件 Activity,Service,Broadcast,ContentProvider,不同的 App 等都运行在不同的过程中,它是这些过程间通信的桥梁。正如其名“粘合剂”一样,它把零碎中各个组件粘合到了一起,是各个组件的桥梁。
了解 Binder 对于了解整个 Android 零碎有着十分重要的作用,如果对 Binder 不理解,就很难对 Android 零碎机制有更深刻的了解。
1. Binder 架构
- Binder 通信采纳 C/S 架构,从组件视角来说,蕴含 Client、Server、ServiceManager 以及 Binder 驱动,其中 ServiceManager 用于管理系统中的各种服务。
- Binder 在 framework 层进行了封装,通过 JNI 技术调用 Native(C/C++)层的 Binder 架构。
- Binder 在 Native 层以 ioctl 的形式与 Binder 驱动通信。
2 . Binder 机制
- 首先须要注册服务端,只有注册了服务端,客户端才有通信的指标,服务端通过 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 机制的次要通信形式,上面咱们来看看具体实现。
3 . Binder 驱动
咱们先来理解下用户空间与内核空间是怎么交互的。
先理解一些概念
用户空间 / 内核空间
具体解释能够参考 Kernel Space Definition;简略了解如下:
Kernel space 是 Linux 内核的运行空间,User space 是用户程序的运行空间。为了平安,它们是隔离的,即便用户的程序解体了,内核也不受影响。
Kernel space 能够执行任意命令,调用零碎的所有资源;User space 只能执行简略的运算,不能间接调用系统资源,必须通过零碎接口(又称 system call),能力向内核收回指令。
零碎调用 / 内核态 / 用户态
尽管从逻辑上抽离出用户空间和内核空间;然而不可避免的的是,总有那么一些用户空间须要拜访内核的资源;比方应用程序拜访文件,网络是很常见的事件,怎么办呢?
Kernel space can be accessed by user processes only through the use of system calls.
用户空间拜访内核空间的惟一形式就是零碎调用;通过这个对立入口接口,所有的资源拜访都是在内核的管制下执行,免得导致对用户程序对系统资源的越权拜访,从而保障了零碎的平安和稳固。用户软件参差不齐,要是它们乱搞把零碎玩坏了怎么办?因而对于某些特权操作必须交给安全可靠的内核来执行。
当一个工作(过程)执行零碎调用而陷入内核代码中执行时,咱们就称过程处于内核运行态(或简称为内核态)此时处理器处于特权级最高的(0 级)内核代码中执行。当过程在执行用户本人的代码时,则称其处于用户运行态(用户态)。即此时处理器在特权级最低的(3 级)用户代码中运行。处理器在特权等级高的时候能力执行那些特权 CPU 指令。
内核模块 / 驱动
通过零碎调用,用户空间能够拜访内核空间,那么如果一个用户空间想与另外一个用户空间进行通信怎么办呢?很天然想到的是让操作系统内核增加反对;传统的 Linux 通信机制,比方 Socket,管道等都是内核反对的;然而 Binder 并不是 Linux 内核的一部分,它是怎么做到拜访内核空间的呢?Linux 的动静可加载内核模块(Loadable Kernel Module,LKM)机制解决了这个问题;模块是具备独立性能的程序,它能够被独自编译,但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行。这样,Android 零碎能够通过增加一个内核模块运行在内核空间,用户过程之间的通过这个模块作为桥梁,就能够实现通信了。
在 Android 零碎中,这个运行在内核空间的,负责各个用户过程通过 Binder 通信的内核模块叫做 Binder 驱动;
驱动程序个别指的是设施驱动程序(Device Driver),是一种能够使计算机和设施通信的非凡程序。相当于硬件的接口,操作系统只有通过这个接口,能力管制硬件设施的工作;
驱动就是操作硬件的接口,为了反对 Binder 通信过程,Binder 应用了一种“硬件”,因而这个模块被称之为驱动。
相熟了下面这些概念,咱们再来看下下面的图,用户空间中 binder\_open(), binder\_mmap(), binder\_ioctl() 这些办法通过 system call 来调用内核空间 Binder 驱动中的办法。内核空间与用户空间共享内存通过 copy\_from\_user(), copy\_to\_user() 内核办法来实现用户空间与内核空间内存的数据传输。Binder 驱动中有一个全局的 binder\_procs 链表保留了服务端的过程信息。
4 . 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 线程,例如 Android 的 system\_server 过程就存在 16 个线程。对于所有 Client 端过程的 binder 申请都是交由 Server 端过程的 binder 线程来解决的。
5 . ServiceManager 启动
理解了 Binder 驱动,怎么与 Binder 驱动进行通信呢?那就是通过 ServiceManager,好多文章称 ServiceManager 是 Binder 驱动的守护过程,大管家,其实 ServiceManager 的作用很简略就是提供了查问服务和注册服务的性能。上面咱们来看一下 ServiceManager 启动的过程。
- ServiceManager 分为 framework 层和 native 层,framework 层只是对 native 层进行了封装不便调用,图上展现的是 native 层的 ServiceManager 启动过程。
- ServiceManager 的启动是零碎在开机时,init 过程解析 init.rc 文件调用 service\_manager.c 中的 main() 办法入口启动的。native 层有一个 binder.c 封装了一些与 Binder 驱动交互的办法。
- ServiceManager 的启动分为三步,首先关上驱动创立全局链表 binder\_procs,而后将本人以后过程信息保留到 binder\_procs 链表,最初开启 loop 一直的解决共享内存中的数据,并解决 BR\_xxx 命令(ioctl 的命令,BR 能够了解为 binder reply 驱动解决完的响应)。
6 . ServiceManager 注册服务
- 注册 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 命令唤醒期待的线程,告诉注册胜利。
7 . ServiceManager 获取服务
- 获取服务的过程与注册相似,相同的过程。通过 ServiceManager 的 getService() 办法来注册服务。
- 首先 ServiceManager 向 Binder 驱动发送 BC\_TRANSACTION 命令携带 CHECK\_SERVICE\_TRANSACTION 命令,同时获取服务的线程进入期待状态 waitForResponse()。
- Binder 驱动收到申请命令向 ServiceManager 的发送 BC\_TRANSACTION 查问已注册的服务,查问到间接响应 BR\_REPLY 唤醒期待的线程。若查问不到将与 binder\_procs 链表中的服务进行一次通信再响应。
8 . 进行一次残缺通信
- 咱们在应用 Binder 时根本都是调用 framework 层封装好的办法,AIDL 就是 framework 层提供的傻瓜式是应用形式。假如服务曾经注册完,咱们来看看客户端怎么执行服务端的办法。
- 首先咱们通过 ServiceManager 获取到服务端的 BinderProxy 代理对象,通过调用 BinderProxy 将参数,办法标识(例如:TRANSACTION\_test,AIDL 中主动生成)传给 ServiceManager,同时客户端线程进入期待状态。
- ServiceManager 将用户空间的参数等申请数据复制到内核空间,并向服务端插入一条执行执行办法的事务。事务执行完告诉 ServiceManager 将执行后果从内核空间复制到用户空间,并唤醒期待的线程,响应后果,通信完结。
总结
好了,这里只是从实现逻辑上简略介绍了下 Binder 机制的工作原理,想要深刻了解 Binder 机制,还得本人下功夫,看源码,只管这个过程很苦楚。一遍看不懂就再来一遍,说实话自己理解能力比拟差,跟着博客思路看了不下十遍。致力总会有播种,好好观赏 native 层各办法之间花式跳转的魅力吧。最初你将发现新世界的大门在向你敞开。
本文转自 https://blog.csdn.net/freekiteyu/article/details/70082302,如有侵权,请分割删除。
相干视频:
Android 高级 UI 性能优化——View 的 Measure 原理利用与 xml 解析过程原理解说
Android 进阶零碎学习——Gradle 入门与我的项目实战_哔哩哔哩_bilibili
Android 网络架构搭建与原理解析(一)——通过一个网络申请一步一步见证网络模块的成长_哔哩哔哩_bilibili
【Android 进阶课程】——colin Compose 的绘制原理解说(一)_哔哩哔哩_bilibili
【Android 进阶教程】——Framework 面试必问的 Handler 源码解析_哔哩哔哩_bilibili
【Android 进阶教程】——热修复原理解析_哔哩哔哩_bilibili
【Android 进阶教程】——如何解决 OOM 问题与 LeakCanary 原理解析_哔哩哔哩_bilibili