4. Android FrameWork之 Binder总结,深入剖析 Android Binder:从 Java 到内核的完整通信图谱

之前做Android五年,对于binder的理解一直停留在aidl的java层,无论怎么看源码都很难深入理解,直到后面学习了 c++,把binder驱动看了,才真正理解binder机制核心原理,融入java和驱动层解析binder!

之前写了2篇,这次是完全总结

1.Android FrameWork之Binder图解 从 Java层解析Binder通信全流程拆解

juejin.cn/post/753864...

2. Android FrameWork之Binder图解 从binder驱动层详细解析binder通信

juejin.cn/post/753980...

1.binder 4大主体Client、 Server、ServiceManager, binder驱动

架构分层解析

这张图从上到下分为四个层次,体现了Android系统的层次结构:

  1. Framework层 (Java框架层)

    • Client端 :包含 BinderProxy 类。这是客户端持有的对象,是服务在客户端的代理(Proxy)。它对应用程序开发者是透明的,但所有对服务的远程调用都通过它完成。
    • Server端 :包含 Binder 类。服务端必须继承这个类,并实现 onTransact() 方法来处理 incoming 的调用。它是服务的本地对象(Stub)。
    • ServiceManager :这是一个特殊的Java类,提供了获取和注册系统服务的接口(如 getService, addService)。
  2. JNI层 (Java本地接口层)

    • 这一层是连接Java世界和Native(C++)世界的桥梁。
    • Android.util.binder & AndroidRuntime:这些是Framework层Binder类对应的JNI代码,负责将Java的Binder调用转换为Native调用。
    • JavaBBinder :这是一个非常关键的角色。它是Native层 BBinder 在JNI层的代表。当Native层的调用需要传递到Java层时,JavaBBinderonTransact() 方法会通过JNI回调到Java层 Binder 类的 execTransact() 方法。
  3. Native层 (C++层)

    • 这是C++实现的Binder核心库,性能更高,系统服务大多运行在这一层。
    • BpBinder (Binder Proxy Binder) :这是Native层的客户端代理。它内部持有一个句柄(handle) ,用于标识远程服务。BinderProxy 在JNI层的底层就是 BpBinder
    • BBinder (Binder Base Binder) :这是Native层的服务端基类。所有Native服务都继承自它并实现 onTransact() 方法。Java层的 Binder 对象在Native层的代表是 JavaBBinder,而 JavaBBinder 继承自 BBinder
    • Service Manager:这是Native层的服务管理器进程,是所有Binder服务的"大管家"。它本身就是一个Binder服务(句柄恒为0)。
  4. 内核空间层 (Binder驱动)

    • /dev/binder:这是用户进程访问Binder驱动的设备节点。

    • Binder驱动:这是整个机制的核心,位于Linux内核中。它负责:

      • 进程间数据拷贝 :通过 mmap 内存映射机制,实现一次拷贝,高效地在进程间传递数据。
      • 线程管理:管理每个进程的Binder线程池,处理多线程并发请求。
      • 路由与转发:根据句柄(handle)找到对应的目标进程,将请求传递过去。
      • 内核安全:负责进程间通信的安全检查。

1.1 工作流程详解

流程 1: 注册服务 (Server -> ServiceManager)

目标:服务进程告诉ServiceManager:"我在这里,我的名字是X,来找我就用这个Binder引用。"

  1. Server端(一个系统服务,如振动服务)启动。
  2. 它通过Framework或JNI层的调用,最终到达Native层,获得一个代表自身的 BBinder 对象。
  3. 服务端调用 ServiceManageraddService() 方法。
  4. 这个调用通过 ioctl 系统调用进入到 Binder驱动
  5. 驱动识别出目标句柄是 0(ServiceManager),于是将请求转发给ServiceManager进程。
  6. ServiceManager 进程收到请求,将服务名(如 "vibrator")和对应的Binder引用(驱动内部管理的)记录到自己的服务列表中。
  7. 注册完成。
流程 2: 获取服务 (Client -> ServiceManager -> Server)

目标:客户端进程向ServiceManager查询:"名字叫X的服务在哪里?给我它的引用。"

  1. Client端(一个App)需要某个服务(如振动服务)。
  2. 它通过 ServiceManagergetService("vibrator") 方法发起查询。
  3. 这个调用同样通过 ioctl 进入 Binder驱动,并被路由到ServiceManager进程。
  4. ServiceManager 在自己的列表中找到服务名对应的Binder引用。
  5. ServiceManager将这个引用(一个句柄值,如 H5)通过驱动返回给客户端。
  6. 客户端收到这个引用后,在Native层创建一个 BpBinder(H5) 对象,并在Java层封装成一个 BinderProxy 对象。
  7. 现在,客户端就持有了服务端的代理。

流程 1.2: 使用服务 (Client -> Server)

目标:客户端通过代理对象调用远程服务的方法。

  1. Client端 调用 BinderProxy 的方法(如 vibrate(100))。

  2. BinderProxy 通过JNI调用到Native层的 BpBinder::transact()

  3. BpBinder::transact() 将方法标识和参数打包,通过 ioctl 发送给 Binder驱动 。数据包中包含了目标句柄 H5

  4. Binder驱动 根据句柄 H5 找到之前注册服务的 Server端 进程。

  5. 驱动将数据包从客户端进程的缓冲区拷贝到服务端进程的缓冲区(一次拷贝)。

  6. 驱动唤醒服务端进程中的一个空闲Binder线程,将请求传递给它。

  7. Server端

    • 如果服务是Native的(C++),请求会直接由 BBinder::onTransact() 处理。
    • 如果服务是Java的,请求会传递给 JavaBBinder,再通过JNI回调到Framework层 Binder 类的 onTransact() 方法。
  8. 服务端的 onTransact() 方法根据调用标识,解析出参数,并执行真正的业务逻辑(如启动振动器)。

  9. 执行结果沿原路返回:Server -> 驱动 -> Client。客户端 BinderProxytransact() 调用返回,流程结束。

1.3 client如何拿到server的binder

ServiceManager

1.4 client如何从ServiceManger中拿到binder

binder驱动

1.5 client如何知道是ServiceManager

当C、S想要获取SM的Binder引用,只需要获取handle=0的Binder即可。

1.6 ServiceManager注册进Binder

1.7 svcInfo里面的handle和name,是干嘛的?

svcInfo(或类似结构,如 svcinfo)通常指的是 ServiceManager 进程内部用来记录已注册服务信息的一个数据结构。

它的核心作用就是维护一张 "服务名称""Binder 引用" 的映射表。我们可以把它理解成 ServiceManager 的 "服务通讯录"


svcInfo (或 svcinfo) 结构体的定义

虽然具体实现可能略有不同,但其核心成员通常包含以下两个字段:

arduino 复制代码
// 这是一个简化版的概念结构,用于帮助理解
struct svcinfo {
    struct svcinfo *next; // 指向下一个节点的指针(ServiceManager用链表管理所有服务)
    void *ptr;            // 指向Binder引用的指针(在驱动中对应的binder_ref)
    unsigned handle;      // 服务的句柄值 (handle) - 【关键字段】
    char name[0];         // 服务的名称 (name)     - 【关键字段】
    // ... 可能还有其他管理性字段
};

1.7.1. name (服务名称)

  • 作用服务的唯一标识符,是一个字符串

  • 功能:这就是客户端查找服务时所用的"钥匙"。它通常是一个描述服务功能的字符串。

  • 举例

    • "activity" -> ActivityManagerService
    • "window" -> WindowManagerService
    • "vibrator" -> VibratorService
    • "my_custom_app_service" -> 你自己应用注册的一个服务
  • 特点name 必须是唯一的。如果两个服务尝试用同一个 name 注册,后一个通常会覆盖前一个,或者注册失败。

1.7.2. handle (服务句柄)

  • 作用一个整数值,代表 ServiceManager 进程内部对目标Binder实体的引用
  • 功能 :这是 ServiceManager 进程内部用来找到目标服务的"内部票据"或"快捷方式"。当客户端查询服务名时,ServiceManager 就会返回这个 handle 对应的值(注意:这个值会经过驱动转换,客户端拿到的不一定是这个值)。
  • 本质 :这个 handle从 ServiceManager 进程的视角出发 的。它是 Binder 驱动在 ServiceManager 进程的上下文中,为某个真正的Binder实体(binder_node)分配的引用标识 。驱动根据这个 handle,就能在 ServiceManager 进程中找到对应的 binder_ref,继而找到目标服务的 binder_node

它们是如何协同工作的?------ 以 getService 为例

让我们通过客户端查找服务的过程,看看 namehandle 是如何发挥作用的:

  1. 客户端请求 :客户端调用 getService("activity")。这个请求通过Binder驱动发送给ServiceManager。

  2. ServiceManager 查表 :ServiceManager 进程收到请求后,遍历它维护的 svcinfo 链表。

    • 查找 Key :它用 "activity" 这个 name 作为键,在链表中进行字符串匹配。
  3. 找到记录 :找到了 name"activity"svcinfo 节点。

  4. 返回引用 :ServiceManager 准备回复数据,它会将这个 svcinfo 节点中的 handle 值(比如 0x5)放入回复数据包中。

  5. 驱动介入(关键步骤) :回复数据包在返回给客户端的路上必须经过Binder驱动

    • 驱动知道这个回复包里的 handle (0x5)相对于ServiceManager进程的
    • 驱动需要为客户端进程创建一个对同一个Binder实体(ActivityManagerService)的新引用。
    • 驱动在客户端进程的上下文中分配一个新的、可能不同的句柄 (比如 0x3),并用这个新句柄替换掉数据包中原有的句柄 0x5
  6. 客户端接收 :客户端进程最终收到回复,它拿到的是一个句柄值 0x3。这个句柄 0x3在它自己进程内有效的 ,它用这个句柄来创建 BpBinder 代理对象,后续所有通过这个代理的调用都会通过驱动路由到真正的ActivityManagerService。

总结

字段 所在进程 作用 类比
name ServiceManager 服务的全局唯一标识符,用于客户端查找和ServiceManager记录。 通讯录中的联系人姓名(如"张三")
handle ServiceManager ServiceManager内部对服务的引用句柄,是驱动为ServiceManager分配的。 ServiceManager手机里存的张三的电话号码(138-xxxx)
client_handle 客户端 客户端进程内对服务的引用句柄,由驱动在返回查询结果时动态分配。 驱动帮你把张三的号码转存后,在你手机里显示的电话号码(可能是139-xxxx)

所以,svcInfo 结构体中的 namehandle 是 ServiceManager 用来建立和管理服务注册表 的核心数据结构,它们共同使得 "按名查找" 这一强大的功能得以实现。而Binder驱动则智能地管理着不同进程中句柄的映射关系,保证了过程的透明性和正确性。

1.7 完整的总结

1).Binder 驱动的全局链表 binder_procs 中插入服务端的信息(binder_proc 结构体,每个 binder_proc 结构体中都有 todo 任务队列),然后向 ServiceManager 的 svcinfo 列表中缓存一下注册的服务。

2). 有了服务端,客户端就可以跟服务端通讯了,通讯之前需要先获取到服务,拿到服务的代理,也可以理解为引用

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

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

2.重要的几个方法:

2.Binder 基于驱动层的理解

juejin.cn/post/753980...

驱动层核心: 纵观Binder机制设计,最核心的点是handle。

2.1 Binder驱动的作用:

1). 进程间通信的核心枢纽 (路由与转发)

这是驱动最根本的作用。它像一个高效的交换机路由器

  • 基于句柄的路由 :驱动维护着庞大的映射表。当客户端发出一个请求(ioctl调用)时,请求包中会包含一个目标句柄(handle) 。驱动根据这个句柄,就能精确地找到:

    1. 目标服务在哪个进程(binder_proc)。
    2. 目标进程中的哪个Binder实体对象(binder_node)。
  • 精确投递:驱动将请求数据包从客户端进程的缓冲区投递到服务端进程的缓冲区,并唤醒服务端进程中等待的线程来处理这个请求。整个过程对应用程序是透明的。

2). 高效的内存管理 (一次拷贝的核心)

这是Binder相比其他IPC(如Socket、管道)性能更高的关键原因。

  • 内存映射(mmap) :在进程启动并打开 /dev/binder 后,驱动会要求进程执行一次 mmap 操作。这将驱动内核的一块内存区域映射到用户进程的空间。

  • 一次拷贝机制

    1. 发送方(客户端)将数据放入自己的发送缓冲区。
    2. 驱动通过 copy_from_user() 将数据仅拷贝一次到内核的映射区(这次拷贝无法避免)。
    3. 由于接收方(服务端)的接收缓冲区也映射到了同一块内核内存 ,所以它可以直接读取这块内存中的数据,无需再进行一次从内核到用户空间的数据拷贝
    4. 这就实现了传统的两次拷贝(用户->内核->用户)变为一次拷贝(用户->内核),极大提升了性能。

从 Java 层来看就像访问本地接口一样,客户端基于 BinderProxy 服务端基于 IBinder 对象,从 native 层来看来看客户端基于 BpBinder 到 ICPThreadState 到 binder 驱动,服务端由 binder 驱动唤醒 IPCThreadSate 到 BbBinder 。跨进程通信的原理最终是要基于内核的,所以最会会涉及到 binder_open 、binder_mmap 和 binder_ioctl这三种系统调用。

Binder 的完整定义

  • 从进程间通信的角度看,Binder 是一种进程间通信的机制;
  • 从 Server 进程的角度看,Binder 指的是 Server 中的 Binder 实体对象;
  • 从 Client 进程的角度看,Binder 指的是 Binder 代理对象,是 Binder 实体对象的一个远程代理;
  • 从传输过程的角度看,Binder 是一个可以跨进程传输的对象;Binder 驱动会对这个跨越进程边界的对象对一点点特殊处理,自动完成代理对象和本地对象之间的转换。

问题: binder驱动有什么用?

答:维护一个链表,能找到对应映射的区域。

2.2 从handler的角度理解一次Binder通信的过程

scss 复制代码
-   通过handle构造Client端的BpBinder(Native层),与此对应的是Java层的BinderProxy

-   通过handle,驱动找到Server端进程,进而调用BBinder(Native层),与此对应的是Java层的Binder

-   通过handle的一系列中转,Client.transact()成功调用了Server.onTransact(),一次Binder通信就过程就完成了

2.3 如何找到SM

Handle是0

  • ProcessState里维护了一个单例,每个进程只有一个ProcessState对象,创建ProcessState时候就会去打开Binder驱动,同时会设置Binder线程池里线程个数等其它参数

  • Native层构造BpBinder(handle=0表示该BpBinder是ServiceManager在客户端的引用),再构造BinderProxyNativeData持有BpBinder。

  • 构造BinderProxy对象并持有BinderProxyNativeData,也就是间接持有BpBinder

  • 最后构造了ServiceManagerProxy对象,它实现了IServiceManager接口,它的成员变量mRemote指向了BinderProxy

2.4 添加服务到ServiceManager

驱动建立服务handle和BBinder对象指针的映射关系,并将服务的名字和服务的handle传递给ServiceManager(通过ServiceManager handle查找)。

ServiceManager拿到消息后建立映射关系,等待其它进程的请求。

至此,进程添加服务到ServiceManager过程已经分析完毕,用图表示如下:

2.5 Binder 驱动加载过程中有哪些重要的步骤?

open 是打开驱动

mmap 是映射驱动

ioctl 是操作驱动

close 是关闭驱动

3.Binder 基于java层的理解

JAVA层理解的核心: 要进行进程通信的核心是能拿到另一个进程暴露出来的IBinder引用

3.1 从java的角度AIdl理解通信过程:

juejin.cn/post/753864...

进程相互隔离,为了安全,不能直接通讯,所以binder驱动来了

1.server会把自己注册到serverManager(通过binder驱动):表示可以提供服务了,创建了实体和servermanger对实体的引用

2.当Client 进程向ServiceManager 查询Server 进程(通过binder驱动)Binder 实体的引用,后面通过这个引用就能实现和Server 进程的通信。(transact)

3.将Server进程中的真实对象转换成代理对象,返回这个代理对象给Client 进程

4.Client进程拿到了这个代理对象,然后调用这个代理对象的方法(通过binder驱动) Binder驱动继续发挥他的使命,它会通知Server进程执行计算工作 (onTransact)

5.将Server进程中的真实对象执行的结果返回给了Client进程,这样Client进程还是如愿的得到了自己想要。跨进程通信完毕

3.2 核心原理

服务端和客户端可以通信的核心原理,就是能够拿到ibinder对象,那么就可以调用接口和方法了!双向通信的原理也是如此 要进行进程通信的核心是能拿到另一个进程暴露出来的IBinder引用

3.2.1 AIDL

客户端:先构造待发送的数据,然后调用IBinder transact(xx)发送数据给服务端。

服务端:继承自Binder,并重写onTransact(xx),该方法里获取了来自客户端(进程A)的消息,将消息提取出来并交给业务接口IMyServer调用。

Binder准备好之后,需要将Binder传递给客户端,传递的方法是通过Service传递。

当客户端绑定这个Service的时候就能拿到该IBinder引用(注意:此处客户端拿到的引用与服务端不是同一个引用,后续会分析此流程

3.3 Stub和Proxy表格对比

Stub 与 Proxy 对比表格

特性 Stub Proxy
角色 服务端基类(服务实际实现逻辑) 客户端代理(透明调用远程服务)
继承关系 继承 Binder 并实现 AIDL 接口 直接实现 AIDL 接口
关键方法 onTransact() transact()
数据流方向 接收客户端请求 → 反序列化 → 执行服务逻辑 序列化参数 → 发送请求 → 接收结果并反序列化
Binder 对象处理 自身是 Binder 对象(服务端 Binder 实体) 持有 IBinder 引用(指向服务端 Stub)
跨进程调用入口 通过 onTransact() 解析请求 通过 transact() 发送请求
是否需开发者实现 需继承并实现业务逻辑(Stub 的子类) 完全自动生成,无需修改
同进程优化 同进程时直接调用本地方法 同进程时通过 Stub.asInterface() 返回 Stub 自身

3.4 数据流 in out inout

标签 数据流向 客户端发送 服务端接收 服务端修改后 性能 使用频率
in 客户端 -> 服务端 序列化对象 反序列化新对象 回传 中等 极高(默认)
out 服务端 -> 客户端 发送空容器 接收空/初始对象 序列化并回传 较低 中等
inout 客户端 <-> 服务端 序列化对象 反序列化新对象 序列化并回传 极低(避免使用

3.5 oneway的原理

ini 复制代码
boolean _status = mRemote.transact(Stub.TRANSACTION_send, 
_data, null, android.os.IBinder.FLAG_ONEWAY);

该接口定义还多了个标识: "oneway"

这个字段最终会影响IBinder.transact(xx)里的最后一个形参

FLAG_ONEWAY 表示transact(xx)不是阻塞调用,也就是说客户端调用该方法后立即返回,不等待。仔细想想其实也并不用等待,因为send(xx)没有返回值,又是in 修饰形参,数据流不能从服务端流入客户端。

4.binder从java层到binder驱动层,整个过程

4.1 整个流程描述了 一个系统服务(如振动服务)的启动、注册到ServiceManager(SM),以及客户端查找并调用该服务 的完整Binder IPC过程。流程分为服务端(Server)ServiceManager(SM)客户端(Client) 三条主线。

右边部分:服务启动与注册(服务端进程)
  1. 进程启动:振动服务(或其他系统服务)的进程启动。

  2. 开启Binder线程池 :进程启动后,初始化Binder机制,打开Binder驱动(/dev/binder),进行内存映射(mmap),并启动Binder线程池。这些线程会阻塞,等待从驱动读取请求。

  3. 循环读取 :Binder线程在一个循环中,通过 ioctl 调用等待Binder驱动发送过来的命令或数据。

  4. 注册服务:服务进程通过Binder调用,请求将自己添加到ServiceManager中。

    • getServiceManager() 获取到SM的Binder代理(一个特殊的 BpBinder,其句柄为0)。
    • 调用 BpBinder.transact() 向SM发送 ADD_SERVICE_TRANSACTION 请求,请求中包含服务名(如"vibrator")和代表服务本身的Binder实体对象。
中间部分:ServiceManager 处理注册(SM进程)
  1. SM接收请求 :ServiceManager进程的Binder线程从驱动读取到注册请求(BR_TRANSACTION)。
  2. SM处理请求:SM解析请求,提取出服务名和对应的Binder引用。
  3. 存储映射 :SM将服务名(如"vibrator")和这个Binder引用(在SM内部是一个句柄值)存储到自己的服务查询表中。
左边部分:客户端调用服务(客户端进程)
  1. 查找服务:客户端(如一个App)需要调用振动服务时,首先向SM查询。

    • 通过 getService("vibrator") 发起请求。内部同样是先获取SM的 BpBinder,然后调用其 transact() 方法。
  2. SM返回句柄:SM进程收到查询请求,在自己的表中根据名字找到对应的句柄,然后将这个句柄通过Binder驱动返回给客户端。

  3. 客户端获取代理 :客户端收到SM的回复,得到了一个指向振动服务的Binder引用(句柄)。这个句柄被包装成一个 BinderProxy 对象(Java层)。

  4. 调用服务接口 :客户端拿着这个 BinderProxy 对象,调用其 transact() 方法来发起具体的业务调用(例如vibrate())。

  5. 驱动路由 :客户端的 transact() 调用最终通过 ioctl 进入Binder驱动。驱动根据句柄找到之前注册的服务端进程。

  6. 服务端执行 :驱动将请求数据拷贝到服务端进程,并唤醒服务端的一个Binder线程。该线程执行 onTransact() 方法,最终调用到振动服务真正的实现逻辑。

  7. 返回结果 :执行结果再通过原路返回给客户端。客户端从 transact() 调用中返回,完成一次完整的IPC调用。

4.2 关键步骤解读 (对应流程图中的编号):

  1. 客户端发起查询 (getService("vibrator")) : 客户端首先需要获取到振动服务的代理对象。
  2. 客户端调用SM代理 (BpBinder::transact) : 通过获取到的ServiceManager代理(一个特殊的BinderProxy,句柄为0),向SM发送一个查询请求。
  3. 驱动路由: Binder驱动根据目标句柄(0)将请求路由到ServiceManager进程。
  4. SM处理请求: ServiceManager进程的Binder线程从驱动读取请求,解析出要查找的服务名(如"vibrator")。
  5. SM查找映射表: SM在其内部维护的服务注册表中查找该服务名对应的Binder引用(句柄)。
  6. 返回结果给驱动: SM将找到的句柄通过Binder驱动返回。
  7. 驱动传回句柄: 驱动将句柄数据返回给客户端进程。
  8. 客户端创建代理对象 : 客户端收到句柄后,将其封装成一个代表振动服务的BinderProxy对象。
  9. 客户端调用服务 (BinderProxy.transact) : 客户端通过得到的BinderProxy对象,调用其transact方法发起具体的业务调用(如vibrate)。
  10. 驱动再次路由: 驱动根据此次调用中的句柄(即振动服务的句柄),将请求路由到正确的服务端进程。
  11. 服务端处理请求: 服务端进程的Binder线程被唤醒,读取请求。
  12. 执行真实逻辑 (onTransact) : 服务端解析请求,调用真正的业务方法实现(例如让手机振动的代码)。
  13. 返回结果给驱动: 方法执行完毕后,将结果通过驱动返回。
  14. 驱动传回结果: 驱动将结果数据返回给客户端进程。
  15. 客户端收到结果 : 客户端的transact调用返回,完成整个IPC过程。

这个流程图和解读展示了Binder IPC的核心:驱动作为中枢,负责在所有进程间精确地路由消息。ServiceManager作为一个特殊的服务,维护着服务的"通讯录"。

问题:client是否会注册到Binder驱动中?

不会。客户端进程本身不会像服务端那样,向Binder驱动"注册"一个服务以供他人查找。

4.3 BBinder作用

BBinder 是Native层(C++)所有Binder服务实体的基类。它的角色与Java层的 android.os.Binder 类完全对应,是Binder通信的服务端核心

它的主要作用可以概括为以下几点:

  1. 代表Binder实体(Binder Object)

    • 在Binder驱动中,一个 BBinder 对象对应着一个 binder_node 结构。这个 binder_node 就是内核中代表的Binder实体。它标志着这个对象是"可以接收远程请求"的终点。
    • 当服务端通过 addService 将自己(一个 BBinder 子类对象)注册到ServiceManager时,驱动就在内核中为它创建了这个 binder_node
  2. 处理 incoming 事务(Transaction)

    • 核心方法是 virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0)
    • 当Binder驱动将客户端发来的请求数据包传递到服务端进程后,最终会调用到这个 BBinder 对象的 onTransact() 方法。
    • code: 一个由用户定义的交易码,用于标识客户端想调用哪个方法(例如 START_SERVICEVIBRATE)。
    • data: 包含调用参数的 Parcel 数据包。
    • reply: 用于写入返回结果的 Parcel 数据包。
    • 子类必须重写这个方法,根据 code 来执行不同的业务逻辑。
  3. 提供对象引用的生命周期管理

    • BBinder 继承自 RefBase,参与Android的强/弱引用计数管理。
    • 当服务端进程不再需要某个Binder服务,或者进程终止时,相关的 BBinder 对象会被销毁,驱动中对应的 binder_node 也会被清理。
  4. 作为JNI的桥梁(对于Java服务)

    • 当服务实现是在Java层(继承自 android.os.Binder)时,在Native层会有一个对应的 JavaBBinder 对象。
    • JavaBBinder 本身继承自 BBinder。当它的 onTransact() 被调用时,它会通过JNI回调到Java层 Binder 对象的 execTransact() 方法,从而将调用传递到Java世界。

简单来说,BBinder就是Native层Binder服务的"肉身"或"处理器" 。它存在于服务提供者的进程空间中,负责执行具体的任务。

4.4 handle和Binder对象的关联

什么是 Handle(句柄)?
  • Handle 是一个32位的整数(int)。
  • 它代表了一个对远程Binder对象的引用
  • 对于客户端来说,它并不直接操作远在另一个进程的 BBinder 对象,而是通过一个句柄来间接操作。
  • 句柄是进程相关的 。同一个Binder实体在不同进程中的句柄值可能完全不同。例如,ServiceManager在所有进程中的句柄都是0,但一个振动服务在进程A中的句柄可能是5,在进程B中的句柄可能是8。
什么是 Binder 对象(实体)?
  • 这里指的是Native层的 BBinder 对象及其在内核中的代表 binder_node
  • 它是全局唯一的,是服务的真实实现所在。
它们是如何关联的?

这种关联是由 Binder驱动 来建立和维护的。整个过程就像一个地址翻译系统

1. 注册与映射的建立(以服务注册为例)

  • 服务端有一个 BBinder 对象(实体)。
  • 服务端调用 defaultServiceManager()->addService("vibrator", myBinder)
  • 驱动收到这个请求后:
    a. 为 myBinder 这个实体在内核 中创建一个 binder_node 结构。
    b. 因为要将这个实体告诉ServiceManager,所以驱动需要为ServiceManager进程创建一个引用
    c. 驱动在ServiceManager进程的"引用列表"中创建一个 binder_ref 结构,这个结构指向那个 binder_node
    d. 驱动给ServiceManager进程分配一个句柄值(handle) (比如 0x5),并将这个句柄和 binder_ref 关联起来。
    e. ServiceManager将这个句柄 0x5 和名字 "vibrator" 一起保存到它的服务列表中。

至此,一个关联建立了:

  • 服务端进程myBinder (对象) <--> binder_node (内核)
  • ServiceManager进程handle=0x5 <--> binder_ref (内核) <--> binder_node (内核)

2. 查找与使用(以客户端调用为例)

  • 客户端调用 defaultServiceManager()->getService("vibrator")
  • ServiceManager从表中找到名字 "vibrator" 对应的句柄 0x5,返回给客户端。
  • 驱动收到这个返回数据后,需要为客户端进程 创建对这个服务的引用:
    a. 驱动根据句柄 0x5 和源进程(ServiceManager),找到对应的 binder_node
    b. 驱动检查客户端进程的"引用列表",看是否已经为这个 binder_node 创建过引用。
    c. 如果没有,就创建一个新的 binder_ref 结构指向那个 binder_node,并为客户端进程分配一个新的句柄值 (比如 0x3)。这个值很可能与ServiceManager中的 0x5 不同。
    d. 驱动将这个新的句柄 0x3 返回给客户端进程。
  • 客户端拿到句柄 0x3,用它创建一个 BpBinder(0x3) 代理对象。
  • 当客户端调用 BpBinder(0x3)->transact(...) 时,驱动收到句柄 0x3 和请求。
  • 驱动根据当前进程(客户端) 和句柄 0x3,在自己的映射表里找到对应的 binder_ref,再找到 binder_node,最终将请求路由到正确的服务端进程的 BBinder 对象上。
总结关联模型

你可以把这种关联想象成:

  • Binder实体 (BBinder/binder_node) :是一个唯一的物理地址
  • 句柄 (Handle) :是每个进程独有的通讯录里的电话号码
  • Binder驱动 :是电话交换机

参考博客:

Android Binder 原理换个姿势就顿悟了(图文版)前言 Binder机制可谓是Android 知识体系里的重中 - 掘金

blog.csdn.net/tkwxty/arti...

blog.csdn.net/freekiteyu/...

blog.csdn.net/fdsafwagdag...

xujiajia.blog.csdn.net/article/det...

相关推荐
牧天白衣.1 分钟前
CSS中linear-gradient 的用法
前端·css
军军36016 分钟前
Git大型仓库的局部开发:分步克隆 + 指定目录拉取
前端·git
前端李二牛21 分钟前
Vue3 特性标志
前端·javascript
coding随想26 分钟前
JavaScript事件处理程序全揭秘:从HTML到IE的各种事件绑定方法!
前端
搞个锤子哟27 分钟前
关键词匹配,过滤树
前端
掘金安东尼1 小时前
字节前端三面复盘:基础不花哨,代码要扎实(含高频题解)
前端·面试·github
吃奥特曼的饼干1 小时前
React useEffect 清理函数:别让依赖数组坑了你!
前端·react.js
烛阴1 小时前
TypeScript 函数重载入门:让你的函数签名更精确
前端·javascript·typescript
前端老鹰1 小时前
HTML <meta name="color-scheme">:自动适配系统深色 / 浅色模式
前端·css·html
Keya2 小时前
MacOS端口被占用的解决方法
前端·后端·设计模式