在上一篇中,我们解决了"为什么"的问题。今天,我们要把 Binder 的架构"摊开",不仅要看清它的分层结构,更要直接面对源码。
一、三层架构:从 Java 到 Kernel 的映射
首先,再次确认我们的战场。Binder 的魅力在于它让上层的 Java 代码感觉像是在调用本地方法,而底层却在疯狂地穿越进程边界。
scss
+-------------------------------------------------------+
| Application / Framework (Java) |
| [MyService.java] [MyProxy.java] |
| | JNI (android_os_Binder.cpp) |
+---------v---------------------------------------------+
| Native Framework (C++) |
| [ProcessState] [IPCThreadState] |
| [BBinder] [BpBinder] |
| | System Call (ioctl /dev/binder) |
+---------v---------------------------------------------+
| Linux Kernel (Driver) |
| [binder_proc] [binder_thread] |
| [binder_node] [binder_buffer] |
+-------------------------------------------------------+
二、核心角色源码大揭秘
为了让你直观感受这些类在源码中的样子,我们将源码中的核心逻辑,简化成了的C++ 风格伪代码。
1. IBinder:通信的"身份证"接口
一切的开始。无论是服务端还是客户端,手里拿的都是 IBinder 指针。它定义了通信的最基本动作:transact。
arduino
// frameworks/native/libs/binder/include/binder/IBinder.h
class IBinder {
public:
// 核心方法:发起一次事务
// code: 命令码 (比如 ADD_BOOK = 1)
// data: 输入数据包 (Parcel)
// reply: 输出数据包 (Parcel)
// flags: 标志位 (比如 FLAG_ONEWAY 异步调用)
virtual status_t transact(
uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0) = 0;
// 获取服务信息 (如调试用的描述符)
virtual const char16_t* getInterfaceDescriptor() const = 0;
// 检查服务是否存活
virtual bool pingBinder() = 0;
// ... 其他方法
};
解读:
- 这是一个纯虚类(接口)。
- 你不需要关心它怎么实现,你只需要知道:只要拿到
IBinder对象,就能调用transact发送数据。 - 具体的实现类有两个分支:
BBinder(服务端)和BpBinder(客户端)。
2. ProcessState:进程的"入场券"
每个进程在进入 Binder 世界前,必须初始化 ProcessState。它负责打开驱动 /dev/binder 并建立内存映射(mmap)。
csharp
// frameworks/native/libs/binder/ProcessState.cpp
class ProcessState {
private:
int mDriverFD; // /dev/binder 的文件描述符
void* mVMStart; // mmap 映射的起始地址
size_t mMapSize; // 映射区域大小 (默认 1MB - 4KB)
// 单例模式,每个进程只有一个实例
static ProcessState* gProcessState;
public:
static sp<ProcessState> self() {
if (gProcessState != nullptr) {
return gProcessState;
}
// 第一次调用时初始化
gProcessState = new ProcessState("/dev/binder");
gProcessState->startDriver(); // 打开驱动
gProcessState->setupBinderContext(); // 设置 mmap
return gProcessState;
}
void startDriver() {
mDriverFD = open("/dev/binder", O_RDWR | O_CLOEXEC);
// ... 错误处理
}
void setupBinderContext() {
// 关键!执行 mmap,将内核缓冲区映射到用户空间
// 这就是 Binder "一次拷贝" 的物理基础
mMapSize = DEFAULT_BINDER_SIZE;
mVMStart = mmap(nullptr, mMapSize, PROT_READ, MAP_PRIVATE, mDriverFD, 0);
// ...
}
// 获取线程状态
sp<IPCThreadState> getThreadPool();
};
解读:
- 单例:一个进程只需要一次初始化。
- mmap :注意
setupBinderContext中的mmap调用。这块内存是后续接收数据的"收件箱"。如果没有这一步,进程就收不到来自内核的数据。
3. IPCThreadState:线程的"通讯员"
这是最忙碌的类。每个绑定到 Binder 的线程都有一个 IPCThreadState 实例。它维护着读写缓冲区,并执行真正的 ioctl 系统调用。
arduino
// frameworks/native/libs/binder/IPCThreadState.cpp
class IPCThreadState {
private:
const int mProcess; // 所属进程 ID (实际上是 driver fd)
const pthread_t mMyThreadId; // 当前线程 ID
Parcel mOut; // 输出缓冲区 (写给内核的数据)
Parcel mIn; // 输入缓冲区 (从内核读到的数据)
// 单例 (线程局部存储 TLS)
static IPCThreadState* self();
public:
// 核心:执行 transact 请求
status_t talkWithDriver(bool doReceive = true) {
binder_write_read_struct bwr;
// 准备数据:如果有输出数据,填入结构体
if (mOut.dataPosition() > 0) {
bwr.write_buffer = (uintptr_t)mOut.data();
bwr.write_size = mOut.dataPosition();
}
// 准备接收:如果有空间接收,填入结构体
if (doReceive) {
bwr.read_buffer = (uintptr_t)mIn.data();
bwr.read_size = mIn.dataCapacity();
}
// 【关键一步】陷入内核!
// 相当于告诉内核:"我有数据要发,同时也想看看有没有新消息"
ioctl(mProcess, BINDER_WRITE_READ, &bwr);
return NO_ERROR;
}
// 封装好的 transact 方法,供 BpBinder 调用
status_t writeTransactionData(int cmd, uint32_t handle,
uint32_t code, const Parcel& data) {
// 将命令和数据打包进 mOut 缓冲区
mOut.writeInt32(cmd);
mOut.writeInt32(handle);
mOut.writeInt32(code);
data.writeToParcel(&mOut);
// 触发与内核的交互
return talkWithDriver();
}
// 处理从内核收到的命令 (如 BC_TRANSACTION)
status_t executeCommand(int32_t cmd);
};
解读:
- 双缓冲区 :
mOut和mIn是用户态的临时仓库。 - ioctl :
talkWithDriver中的ioctl是用户态和内核态切换的关口。一旦调用,线程可能阻塞,直到内核处理完毕或有新数据。
4. BBinder vs BpBinder:服务端与客户端的分野
这是 Binder 多态的精髓。同样的 transact 调用,在不同对象上有完全不同的行为。
A. BBinder (Server 端)
代表本地拥有的服务对象。当内核把请求送过来时,由它来处理。
arduino
// frameworks/native/libs/binder/Binder.cpp
class BBinder : public IBinder {
protected:
// 子类必须实现此方法来处理具体业务
virtual status_t onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags = 0);
public:
// 作为 Server,transact 是被调用的入口
virtual status_t transact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags = 0) override {
// 1. 权限检查 (Security Check)
// 2. 解包数据
// 3. 调用虚函数 onTransact 分发给具体实现
status_t err = onTransact(code, data, reply, flags);
// 4. 如果需要返回结果且没有异常,标记成功
if (reply != nullptr && err == NO_ERROR) {
reply->writeInt32(NO_ERROR);
}
return err;
}
};
解读 :BBinder::transact 是被动 的。它等待内核唤醒线程,然后被 IPCThreadState 调用,进而调用 onTransact 执行业务。
B. BpBinder (Client 端)
代表远程服务的代理。当你调用它时,它负责把请求发出去。
arduino
// frameworks/native/libs/binder/BpBinder.cpp
class BpBinder : public IBinder {
private:
uint32_t mHandle; // 内核中对应服务的句柄 (Handle ID)
public:
// 作为 Client,transact 是发起调用的入口
virtual status_t transact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags = 0) override {
// 1. 如果连接断了,先尝试重连
if (mAlive) {
// 2. 委托给 IPCThreadState 去干活
// 这里会构造 BC_TRANSACTION 命令
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
// 3. 处理死 Binder 通知等特殊情况
if (status == DEAD_OBJECT || status == FAILED_TRANSACTION) {
mAlive = false;
}
return status;
}
return DEAD_OBJECT;
}
};
解读 :BpBinder::transact 是主动 的。它拿到数据后,立刻找 IPCThreadState 打包并发送给内核。它持有 mHandle,这就是它在内核中定位目标服务的"门牌号"。
5. ServiceManager:全局注册表
它是一个特殊的 BBinder 子类,运行在 PID=1 的进程中。它的逻辑相对独立,主要负责维护一个哈希表。
arduino
// system/core/libcutils/service_management.c (简化逻辑)
class ServiceManager : public BBinder {
private:
// 存储 map: 服务名 -> IBinder 指针
std::map<String16, sp<IBinder>> mServiceMap;
public:
virtual status_t onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags) override {
switch(code) {
case ADD_SERVICE: {
String16 name = data.readString16();
sp<IBinder> service = data.readStrongBinder();
mServiceMap[name] = service; // 注册服务
reply->writeInt32(0); // 成功
break;
}
case GET_SERVICE: {
String16 name = data.readString16();
auto it = mServiceMap.find(name);
if (it != mServiceMap.end()) {
reply->writeStrongBinder(it->second); // 返回 Binder 引用
} else {
reply->writeStrongBinder(nullptr); // 没找到
}
break;
}
}
return NO_ERROR;
}
};
解读:
- 它本质上也是一个 Binder 服务。
- 所有进程想要获取其他服务,第一步都是向这个特定的 Handle(通常是 0)发送
GET_SERVICE请求。
三、串联起来:一次调用的代码旅行
现在,让我们把上面的伪代码串联起来,看一次完整的 proxy.doSomething() 是如何在代码层面流动的。
场景 :Client 调用 Server 的 doSomething (Code = 10),这是一次同步调用。
arduino
[ Client 进程 ] [ Kernel 空间 ] [ Server 进程 ]
| | |
| 1. Java: proxy.doSomething() | |
| | | |
| v | |
| 2. JNI: android_os_Binder_transact | |
| | | |
| v | |
| 3. C++: BpBinder::transact(code, data) | |
| | (持有 mHandle) | |
| v | |
| 4. C++: IPCThreadState::transact | |
| | - 组装 BC_TRANSACTION 命令 | |
| | - 写入 mOut 缓冲区 | |
| v | |
| 5. C++: IPCThreadState::talkWithDriver | |
| | (准备 binder_write_read 结构体) | |
| | | |
| +-------------------> 6. ioctl(BINDER_WRITE_READ) ------------------+
| | (用户态 -> 内核态) | |
| | [线程阻塞/睡眠] | 7. binder_ioctl 处理 |
| | | - 解析 BC_TRANSACTION |
| | | - 根据 mHandle 查找 binder_node |
| | | - 内存拷贝 (User -> Kernel Buffer) |
| | | - 查找 Server 进程的空闲线程 |
| | | - 构造 BR_TRANSACTION |
| | | - 唤醒 Server 线程 (wait_queue) |
| | | | |
| | | v |
| | | 8. Server 线程苏醒 |
| | | | |
| | | v |
| | | <--------- ioctl 返回 (BR_TRANSACTION) -----+
| | | |
| | | 9. C++: IPCThreadState::executeCommand |
| | | - 解析 BR_TRANSACTION |
| | | - 读取数据到 mIn 缓冲区 |
| | | - 调用 target->transact (BBinder) |
| | | | |
| | | 10. C++: BBinder::transact |
| | | - 调用 onTransact(code, data, reply) |
| | | | |
| | | 11. C++: MyService::onTransact |
| | | - [执行业务逻辑 doSomething] |
| | | - 结果写入 reply Parcel |
| | | | |
| | | 12. C++: IPCThreadState::sendReply |
| | | - 组装 BC_REPLY 命令 |
| | | - 写入 mOut 缓冲区 |
| | | - 再次 ioctl(BINDER_WRITE_READ) |
| | | | |
| | | 13. Kernel: 处理 BC_REPLY |
| | | - 找到等待的 Client 线程 |
| | | - 拷贝 reply 数据 |
| | | - 唤醒 Client 线程 |
| | | | |
| <----+ (ioctl 返回 BR_REPLY) -----------------+ | |
| | | | |
| 14. C++: IPCThreadState::waitForResponse | | |
| - 解析 BR_REPLY | | |
| - 从 mIn 读取 reply 数据 | | |
| | | | |
| v | | |
| 15. C++: BpBinder::transact 返回 | | |
| | | | |
| v | | |
| 16. JNI: 返回 Java 层 | | |
| | | | |
| v | | |
| 17. Java: 获得结果,继续执行 | | |
| | |
关键节点解读
- 步骤 4 (BpBinder) : 客户端代理,持有
mHandle(句柄),它不知道服务具体在哪,只知道发给内核哪个 ID。 - 步骤 5 & 8 (ioctl) : 唯一的"过路费"站点。用户态数据在这里跨越边界。Client 在此挂起 ,Server 在此被唤醒。
- 步骤 7 (Kernel Copy) : 这里的内存拷贝是 Binder 高效的核心------只拷贝一次(从 Client 用户空间到 Kernel 空间,然后映射给 Server 用户空间,无需再拷一份到 Server 用户空间,而是直接映射访问)。
- 步骤 10 (BBinder) : 服务端本体。
onTransact是真正的业务分发入口,AIDL 生成的BnXXX类就在此处通过switch(code)调用具体方法。 - 步骤 12 (Reply) : 返回路径并非自动发生,Server 线程必须显式地再次发起一次
ioctl(携带BC_REPLY) 来通知内核"我办完了",内核才会去唤醒 Client。
总结
通过上面的分析,我们可以看到 Binder 设计的精妙之处:
-
统一接口 :无论远近,统统通过
IBinder::transact交互。 -
职责分离:
ProcessState管内存 (mmap)。IPCThreadState管通信 (ioctl)。BpBinder管发包。BBinder管拆包和执行。
-
透明代理 :
BpBinder的存在,让客户端完全感觉不到对面是一个远程进程。
下一篇,我们将不再满足于伪代码,而是真正深入到 Linux 内核驱动 (binder.c) 的核心逻辑中,去看看那个神秘的 binder_proc 结构体长什么样,以及内核是如何管理那些成千上万个 binder_thread 的。那才是 Binder 真正的"黑盒"内部。