美文网首页Android开发Android开发经验谈Android技术知识
Android架构中的Binder是什么?如何理解?

Android架构中的Binder是什么?如何理解?

作者: 程序老秃子 | 来源:发表于2022-06-07 16:31 被阅读0次

    Android 的整体架构

    为了让大家更好的理解Binder机制,我们先来看下Android的整体架构;这样大家就知道在Android架构中Binder处于什么地位

    下面是官网上的图片:

    从下往上依次为:

    内核层: Linux内核和各类硬件设备的驱动,这里需要注意的是,Binder IPC驱动也是这一层的实现,比较特殊

    硬件抽象层: 封装"内核层"硬件驱动,提供可供"系统服务层"调用的统一硬件接口

    系统服务层: 提供核心服务,并且提供可供"应用程序框架层"调用的接口

    Binder IPC 层: 作为"系统服务层"与"应用程序框架层"的IPC桥梁,相互传递接口调用的数据,实现跨进程的通信

    应用程序框架层: 这一层可以理解为Android SDK,提供四大组件,View绘制等平时开发中用到的基础部件

    Binder是Android系统进程间通信(IPC)最重要的方式,要想了解Android的系统原理,必须要先对Binder框架有一定的理解

    Binder 是什么?

    Binder 可以理解为能在进程间进行 "通信" 的对象,这个通信不是指在不同进程中操作同一个对象,而应理解为一种通信协议

    Binder 的引入背景

    传统的进程间通信方式有管道,消息队列,共享内存等

    其中管道,消息队列采用存储—转发方式; 即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程

    共享内存虽然无需拷贝,但控制复杂,难以使用; socket作为一款通用接口,其传输效率低,开销大,主要用在跨网络的进程间通信和本机上进程间的低速通信;Binder通过内存映射的方式,使数据只需要在内存进行一次读写过程

    内存映射,简而言之就是将用户空间的一段内存区域映射到内核空间; 映射成功后,用户对这段内存区域的修改可以直接反映到内核空间,相反,内核空间对这段区域的修改也直接反映用户空间;那么对于内核空间<---->用户空间两者之间需要大量数据传输等操作的话效率是非常高的

    Binder 的通信模型

    主要分为4个部分 Binder 驱动,Client,Server,ServiceManager(SM)

    其中Client,Server,ServiceManager(SM)指的是用户空间; 并且分别在不同的进程。Binder驱动是内核空间,Binder驱动是一段c语言实现的代码

    服务分系统服务和本地服务; 系统服务一般指设备开机时就创建供所有应用使用的服务,如AMS,PMS等,本地服务一般指本地创建的Service,一般供当前应用使用

    由上图可以看出Binder通信的流程是这样的:

    ● 首先不同的服务Server一般为service需要先在SM上进行注册

    ● Client想获取服务时,先到SM上通过服务名请求服务

    ● 得到对应的服务的引用,通过这个引用进行进一步的通信

    由于Client、Server、SM不在同一个进程,所以都需要借助Binder驱动完成通信; 这其中有几个关键点。先明确几个概念,Binder Server对象是指真正的进行服务的对象,Biner内核对象是根据Binder Server对象在内核空间的一种表述方式,因为语言层面不同,所以具体的表现形式也不同

    比如用应用层表现为Java类或C++类,在内核层表现为结构体了;也可以称为实体,主要是和引用区分开,实体只有一个,而引用可以有很多

    ● Client、Server、SM不在同一个进程,最初Client和Server怎么得到SM的通信的?

    ● 具体的注册流程是怎样的?

    ● Binder内核对象,BinderProxy对象是同一个对象吗,有什么联系?

    第一个问题:

    ● 首先ServiceManage进程本身就是也是采用Binder通信,在系统初始化的时候使用BINDER_SET_CONTEXT_MGR命令将自己注册为ServiceManage,这个过程会在内核空间产生一个ServiceManage的Binder实体

    ● 而这个Binder就在内核的0号引用,其他进程通过0号引用找到ServiceManage的Binder实体,通过内存映射就可以和SM建立联系

    第二个问题:

    ● 首先拥有服务名的Server进程向SM注册时需要借助Binder驱动,此时创建了一个在内核的实体对象,和Server的对象是不同的结构,但拥有Server实体对象的关键信息,比如可以提供的方法等

    ● 这个实体对象有一项数据保存Server实体的引用(在内存中可以理解为映射的地址),借助Binder驱动将内核中的实体对像的引用和服务名注册到SM中,这样就有这样一条关系链,SM中服务名—>内核的实体对象的引用—>Server实体对象

    第三个问题:

    ● Binder内核对象,BinderProxy对象是同一个对象可定不是同一个对象,因为对应不同的空间,语言层面都有不一样,BinderProxy是系统根据Binder内核对象在Cilent进程新建(new)的一个Binder对象,里面包含Binder内核对象的关键信息,比如所能提供的方法等,这个对象就和真正远程的Server实体对象很相似了,这样Client对象就能像操作Server实体对象一样进行操作,不用考虑Server实在远程还是本地

    ● 但具体方法的实现还要通过代理的方式调用远程对象来实现;一般通过transact()和onTransact()来实现代理的调用

    上层调用

    Binder 驱动在系统内核中,从用户空间应用程序到内核空间 binder 驱动,会经过一些 ProcessState、IPCThreadState 等中间的操作封装类,然后通过系统调用陷入系统内核

    ProcessState 是进程单例; 主要负责打开 binder 驱动设备及 mmap,binder 驱动的 binder_open()、binder_mmap() 函数在 ProcessState 的构造函数中被调用,如下:

    <pre mdtype="fences" cid="n78" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">ProcessState::ProcessState()
     : mDriverFD(open_driver()) //打开 binder 设备,binder_open()
     , mVMStart(MAP_FAILED)
     ...
     , mMaxThreads(DEFAULT_MAX_binder_THREADS)
     , mThreadPoolSeq(1){
     if (mDriverFD >= 0) {
     // 将应用进程虚拟内存空间与 binder 驱动映射,binder_mmap()
     mVMStart = mmap(0, binder_VM_SIZE, PROT_READ,
     MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
     if (mVMStart == MAP_FAILED) {
     close(mDriverFD);
     mDriverFD = -1;
     }
     }
    }</pre>
    

    IPCThreadState 为线程单例,负责与 binder 驱动进行具体的命令通信。在其 talkWithDriver() 方法中,调用了 binder 驱动的 binder_ioctl() 函数:

    <pre mdtype="fences" cid="n83" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">// binder_ioctl
    ioctl(mProcess->mDriverFD, binder_WRITE_READ, &bwr)</pre>
    

    Binder 驱动注册

    Binder 驱动运行在内核态,向上层提供 /dev/binder 设备节点,并不对应真实的硬件设备;Binder 驱动的注册逻辑在 Binder.c 中:

    <pre mdtype="fences" cid="n90" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">//drivers/staging/android/Binder.c
    static init __init binder_init(void){
    
     ...
     ret = misc_register(&binder_miscdev); //注册为 misc 驱动
    }</pre>
    

    Binder_miscdev 即 Binder 设备描述如下:

    <pre mdtype="fences" cid="n96" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">static struct miscdevice binder_miscdev = {
     .minor = MISC_DYNAMIC_MINOR, //自动分配次设备号
     .name = "binder", //驱动名称
     .fops = &binder_fops //binder 驱动支持的文件操作
    }</pre>
    

    Binder_fops 为 Binder 设备支持的操作函数,如下:

    <pre mdtype="fences" cid="n101" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">static const struct file_operations binder_fops = {
    .owner = THIS_MODULE,
    .poll = binder_poll,
    .unlocked_ioctl = binder_ioctl,
    .mmap = binder_mmap,
    .open = binder_open,
    .flush = binder_flush,
    .release = binder_release,
    };</pre>
    

    Binder_open

    用户应用程序通过 Binder 通信时,需先调用 Binder_open() 方法打开 Binder 驱动,Binder_open() 中主要做了两个工作,对应的分为两部分来看:

    <pre mdtype="fences" cid="n108" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">//binder.c
    static int binder_open(struct inode *nodp, struct file *filp)
    {
     struct binder_proc *proc;
     ...
     proc = kzalloc(sizeof(*proc), GFP_KERNEL); //创建 binder_proc
     if (proc == NULL)
     return -ENOMEM;
     get_task_struct(current);
     proc->tsk = current;
     INIT_LIST_HEAD(&proc->todo); //初始化 todo 队列
     init_waitqueue_head(&proc->wait); //初始化 todo 队列
     proc->default_priority = task_nice(current);</pre>
    

    上面代码的主要工作是 「创建及初始化 binder_proc」,binder_proc 就是用来存放 binder 相关数据的结构体,每个进程独有一份

    <pre mdtype="fences" cid="n114" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> binder_lock(__func__);
     binder_stats_created(BINDER_STAT_PROC);
     hlist_add_head(&proc->proc_node, &binder_procs);
     proc->pid = current->group_leader->pid;
     INIT_LIST_HEAD(&proc->delivered_death);
     filp->private_data = proc;
     binder_unlock(__func__);
     ...
    }</pre>
    

    第二个主要工作是 「将 Binder_proc 记录起来」,方便后续使用,如上代码所示,通过 hlist_add_head() 方法将 Binder_proc 记录到了内核的 Binder_procs 表中,另外还将 Binder_proc 存放在 filp 的 private_data 域,以便于在后续调用 mmap、ioctl 等方法时获取

    Binder_ioctl

    Binder 驱动并不提供常规的 read()、write() 等文件操作,全部通过 binder_ioctl() 实现,所以 binder_ioctl() 是 binder 驱动中工作量最大的一个,它承担了 binder 驱动的大部分业务; 下面列出 binder_ioctl() 支持的命令列表

    其中 BINDER_WRITE_READ 最为关键,分为若干子命令:

    其中 BINDER_WRITE_READ 最为关键,分为若干子命令:

    以上均为 Binder 驱动作为接收方 Binder_ioctl() 方法接收的命令,还有一些与之对应的 BR_ 开头的命令,由 Binder 驱动主动发出,比如 BR_TRANSACTION、BR_REPLY,在一次 IPC 调用中是这样应用的

    尾述

    技术是无止境的,你需要对自己提交的每一行代码、使用的每一个工具负责,不断挖掘其底层原理,才能使自己的技术升华到更高的层面

    Android 架构师之路还很漫长,与君共勉

    PS:有问题欢迎指正,可以在评论区留下你的建议和感受;

    欢迎大家点赞评论,觉得内容可以的话,可以转发分享一下

    相关文章

      网友评论

        本文标题:Android架构中的Binder是什么?如何理解?

        本文链接:https://www.haomeiwen.com/subject/pseemrtx.html