解剖麻雀:Binder 通信的整体架构全景图

在上一篇中,我们解决了"为什么"的问题。今天,我们要把 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: 获得结果,继续执行                  |                        |                   |
      |                                                |                                                |

关键节点解读

  1. 步骤 4 (BpBinder) : 客户端代理,持有 mHandle (句柄),它不知道服务具体在哪,只知道发给内核哪个 ID。
  2. 步骤 5 & 8 (ioctl) : 唯一的"过路费"站点。用户态数据在这里跨越边界。Client 在此挂起 ,Server 在此被唤醒。
  3. 步骤 7 (Kernel Copy) : 这里的内存拷贝是 Binder 高效的核心------只拷贝一次(从 Client 用户空间到 Kernel 空间,然后映射给 Server 用户空间,无需再拷一份到 Server 用户空间,而是直接映射访问)。
  4. 步骤 10 (BBinder) : 服务端本体。onTransact 是真正的业务分发入口,AIDL 生成的 BnXXX 类就在此处通过 switch(code) 调用具体方法。
  5. 步骤 12 (Reply) : 返回路径并非自动发生,Server 线程必须显式地再次发起一次 ioctl (携带 BC_REPLY) 来通知内核"我办完了",内核才会去唤醒 Client。

总结

通过上面的分析,我们可以看到 Binder 设计的精妙之处:

  1. 统一接口 :无论远近,统统通过 IBinder::transact 交互。

  2. 职责分离:

    • ProcessState 管内存 (mmap)。
    • IPCThreadState 管通信 (ioctl)。
    • BpBinder 管发包。
    • BBinder 管拆包和执行。
  3. 透明代理 :BpBinder 的存在,让客户端完全感觉不到对面是一个远程进程。

下一篇,我们将不再满足于伪代码,而是真正深入到 Linux 内核驱动 (binder.c) 的核心逻辑中,去看看那个神秘的 binder_proc 结构体长什么样,以及内核是如何管理那些成千上万个 binder_thread 的。那才是 Binder 真正的"黑盒"内部。

相关推荐
千里马学框架4 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台4 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone4 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc4 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo4 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077004 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼5 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone5 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen5 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone5 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui