Android Binder 笔记

如果说View体系是Android系统的"脸面",负责和用户打交道;那么Binder机制就是Android系统的"骨架和神经",它支撑起整个系统内所有跨进程的通信。

在Android的世界里,每个进程都活在自己的"安全屋"(虚拟地址空间)里,无法直接访问他人。但系统服务和App之间又需要频繁交互,比如一个App要启动Activity,就得通知系统服务ActivityManagerService。这时,就需要一座安全高效的桥梁------这就是Binder

Binder的设计非常精妙,接下来我将从设计初衷、核心模型、一次完整的通信过程、以及上层使用这几个维度,为你详细拆解这套机制。


一、设计初衷:为什么是Binder?

Linux系统自带的IPC(进程间通信)方式不少,比如管道、Socket、共享内存等。但Android最终选择自建Binder,主要是基于性能、安全性和易用性的综合考量。

  • 🚀 性能卓越(一次拷贝) :传统的IPC方式(如管道、Socket)通常需要两次数据拷贝 :先从发送方用户空间拷贝到内核空间,再从内核空间拷贝到接收方用户空间。而Binder利用内存映射(mmap) 技术,仅需一次拷贝,性能上仅次于共享内存。
  • 🔒 安全性高(身份识别):传统IPC无法获得对方进程可靠的UID/PID标识,只能由用户自己在数据包里填写,容易被篡改。Binder机制由内核管理身份,每个App都有唯一的UID,通信时内核会自动附加调用方的PID/UID,安全性极高。
  • 🧩 易用性(C/S架构):Binder基于Client-Server模型,对开发者而言,调用远程服务就像调用本地方法一样简单(通过代理),无需关心底层细节。

二、Binder核心设计:分层架构与四大角色

Binder机制的实现是分层设计的,贯穿了从应用层到Linux内核。可以借助下面的分层架构图来理解:

在这个架构中,四个核心角色各司其职:

  1. Binder驱动 :通信的路由器。位于内核空间,负责各个进程间Binder通信的建立、数据在内核中的传递、线程管理等最底层的活。
  2. ServiceManager :服务注册中心的DNS域名服务器。它维护着一张"服务名 → Binder引用"的映射表。系统服务(如AMS)在启动时,会向它注册;客户端需要使用服务时,首先向它查询。
  3. Server :服务的提供方 。例如系统进程中的ActivityManagerService,它通过Binder驱动将自己注册到ServiceManager,并等待处理客户端请求。
  4. Client :服务的使用方。例如某个App进程,它通过Binder驱动向ServiceManager查询服务,拿到服务的代理后,再通过驱动与真正的Server通信。

三、一次完整的Binder通信过程

下面,我们以一个最简单的场景------App调用系统服务的startService方法为例,从宏观和微观两个层面拆解一次Binder通信的全过程。

3.1 宏观流程:三步走战略

任何服务的使用,都遵循"注册 → 获取 → 使用"这三步。

  1. 注册服务:系统服务(Server)启动后,通过Binder驱动将自己的"联系方式"注册到ServiceManager这个大管家那里。
  2. 获取服务 :App进程(Client)想要使用startService功能,它需要先通过Binder驱动向ServiceManager查询,并拿到系统服务的代理对象
  3. 使用服务 :App通过这个代理对象发起startService请求。Binder驱动接手这个请求,找到真正的系统服务进程,唤醒它并让它执行startService方法,最后将执行结果原路返回给App。

3.2 微观核心:一次拷贝的秘密

那么,Binder引以为傲的"一次拷贝"究竟是如何实现的呢?这要归功于内存映射(mmap) 机制。

sequenceDiagram participant Client as Client进程(用户空间) participant Driver as Binder Driver(内核空间) participant Server as Server进程(用户空间) Note over Server: 1. 初始化:通过mmap()
将内核空间的一块物理内存
同时映射到Server的用户空间 Client->>Driver: 2. 发起请求(数据在Client用户空间) Driver->>Driver: 3. 在Server的内核空间中
分配一块缓存区 Driver->>Driver: 4. 执行一次拷贝:
copy_from_user()
将Client数据直接拷贝到
Server的内核缓存区 Note over Driver,Server: 5. 由于mmap映射,
Server内核缓存区
与Server用户空间是"共享"的 Driver->>Server: 6. 通知Server有数据到来 Server->>Server: 7. 直接在自己的用户空间
读取数据,无需再次拷贝

详细步骤说明:

  1. 预备阶段 :在服务端(Server)进程启动时,它会通过mmap()系统调用,将内核空间的一块物理内存同时映射到自己的内核虚拟地址和用户虚拟地址。这就相当于在内核里为Server开辟了一块"共享缓冲区"。
  2. 发送数据 :当客户端(Client)要发送数据时,它通过ioctl()系统调用进入内核态。
  3. 内核中继 :Binder驱动接到请求,会先在Server的内核空间中分配一块缓存区(这块区域就是通过mmap准备好的)。
  4. 一次拷贝 :驱动执行copy_from_user(),将Client用户空间的数据,直接拷贝到刚才分配的Server内核空间缓存区中 。这是整个过程中唯一一次数据拷贝。
  5. 唤醒Server:数据拷贝完成后,Binder驱动会唤醒Server进程,并通知它来处理数据。
  6. 直接读取:Server在自己的用户空间,通过指针直接访问那块与内核共享的内存区域,读取数据。至此,通信完成。

正是因为mmap让Server的内核空间和用户空间共享了同一块物理内存,所以数据只需从Client拷贝到Server的内核空间,Server就能直接在自己的用户空间访问,从而实现了"一次拷贝"。

四、Binder在应用层的体现:AIDL

作为上层App开发者,我们很少直接操作底层的openmmapioctl。Android为我们封装好了更简单的工具------AIDL

当你在两个进程间通过AIDL定义接口时,系统会自动帮我们生成两端(Client和Server)的代码。

  • Client端 :拿到的是一个代理对象 (在Java层是BinderProxy,在Native层是BpBinder)。你调用的接口方法,会被代理对象打包成Parcel数据,然后通过transact()方法发送出去。
  • Server端 :真正的服务对象继承自Binder类(Native层是BBinder)。它在一个Binder线程池中等待请求,当请求到来时,它的onTransact()方法会被调用,它负责解包数据并调用你真正实现的服务逻辑,最后将返回值打包发回给Client。

整个过程,对于开发者来说几乎是透明的,我们只需要关心接口的定义和业务逻辑的实现即可。

总结

Binder机制是Android的"任督二脉",理解它就能对整个系统的运行有更深的把握。我们可以用一个公式来总结它:

Binder = 高效的"一次拷贝"(基于mmap) + 安全的"实名认证"(UID/PID) + 简单的"代理调用"(C/S架构)

它通过在内核中加载一个专门的驱动,并利用内存映射技术,实现了用户空间进程间的高效、安全通信,是所有系统服务和跨进程数据交换的基石。

相关推荐
峥嵘life1 小时前
五一南昌第三天游玩记录:梅景寻芳,母校忆旧,摩天轮揽夜
android
qq_452396233 小时前
第三篇:《JMeter断言:验证接口响应正确性》
android·jmeter
aqi003 小时前
一文速览 HarmonyOS 6.0.1 引入的十个新特性
android·华为·harmonyos·鸿蒙·harmony
橙子199110164 小时前
Android 第三方框架 相关
android
赏金术士4 小时前
JetPack Compose 弹窗、菜单、交互组件(五)
android·kotlin·交互·android jetpack·compose
海天鹰5 小时前
高版本安卓老应用下面空白
android
猫的玖月5 小时前
(七)函数
android·数据库·sql
秋96 小时前
java中对操作mysql8.0.46与MySQL9.7.0有什么区别,并举例说明
android·java·adb