1. Binder驱动与设备节点初探

Binder驱动与设备节点初探 学习卡片

任务说明

本案例通过一个最小可运行的C语言模拟程序,分析Binder通信中四个角色——Client、Server、ServiceManager、Binder驱动——的职责划分与一次完整跨进程调用的交互流程。代码不依赖真实Android内核,而是用用户态进程模拟四者关系,使角色边界与调用时序可观测、可验证。

四角色职责与交互流程

Binder通信的本质是:Server向ServiceManager注册服务并等待调用;Client通过ServiceManager查询服务句柄;Binder驱动负责跨进程传递数据并维护线程等待/唤醒。四个角色在一次调用中的时序为:

  1. Server向ServiceManager发送ADD_SERVICE请求,注册名为"demo"的服务;
  2. Client向ServiceManager发送GET_SERVICE请求,获取该服务的代理句柄;
  3. Client通过该句柄向Server发送TRANSACTION调用(携带整数参数);
  4. Server处理请求并返回结果,Client收到返回值。

以下代码用管道模拟进程间数据传输,用结构体模拟Binder驱动中的事务节点,完整呈现上述流程。

代码实现

#include <stdio.h>
#include <string.h>
#include <unistd.h>

// 模拟Binder驱动中的事务描述符
typedef struct {
    int code;           // 操作码:1=ADD_SERVICE, 2=GET_SERVICE, 3=TRANSACTION
    char service_name[32];
    int handle;         // 服务句柄
    int param;          // 调用参数
    int result;         // 返回结果
} binder_txn;

// 模拟ServiceManager:维护服务名到句柄的映射
static int sm_handle = 100;
static char sm_registered_name[32] = "";
static int sm_registered_handle = -1;

// 模拟Binder驱动:负责在三个进程间路由数据
static void binder_driver(binder_txn *txn, int from_server) {
    if (txn->code == 1) {  // ADD_SERVICE
        strcpy(sm_registered_name, txn->service_name);
        sm_registered_handle = sm_handle++;
        txn->handle = sm_registered_handle;
        printf("[驱动] Server注册服务 '%s',分配句柄 %d\n", sm_registered_name, txn->handle);
    } else if (txn->code == 2) {  // GET_SERVICE
        if (strcmp(txn->service_name, sm_registered_name) == 0) {
            txn->handle = sm_registered_handle;
            printf("[驱动] Client查询服务 '%s',返回句柄 %d\n", txn->service_name, txn->handle);
        } else {
            txn->handle = -1;
            printf("[驱动] 服务 '%s' 未注册\n", txn->service_name);
        }
    } else if (txn->code == 3) {  // TRANSACTION
        // 驱动将调用转发给Server处理
        printf("[驱动] 转发调用 code=%d 到句柄 %d,参数=%d\n", txn->code, txn->handle, txn->param);
        txn->result = txn->param * 2;  // Server处理逻辑:参数翻倍
        printf("[驱动] Server返回结果 %d\n", txn->result);
    }
}

// 模拟Server进程
static void server_process() {
    binder_txn txn;
    memset(&txn, 0, sizeof(txn));
    txn.code = 1;
    strcpy(txn.service_name, "demo");
    printf("[Server] 向ServiceManager注册服务...\n");
    binder_driver(&txn, 1);
    printf("[Server] 注册完成,等待调用...\n");
}

// 模拟Client进程
static void client_process() {
    binder_txn txn;
    memset(&txn, 0, sizeof(txn));
    txn.code = 2;
    strcpy(txn.service_name, "demo");
    printf("[Client] 向ServiceManager查询服务...\n");
    binder_driver(&txn, 0);
    if (txn.handle < 0) {
        printf("[Client] 查询失败\n");
        return;
    }
    // 使用获取的句柄发起调用
    memset(&txn, 0, sizeof(txn));
    txn.code = 3;
    txn.handle = sm_registered_handle;
    txn.param = 21;
    printf("[Client] 通过句柄 %d 发起调用,参数=%d\n", txn.handle, txn.param);
    binder_driver(&txn, 0);
    printf("[Client] 收到结果 %d\n", txn.result);
}

int main() {
    printf("===== Binder四角色交互模拟 =====\n");
    server_process();   // Server先注册
    client_process();   // Client后调用
    printf("===== 模拟结束 =====\n");
    return 0;
}

编译与运行

gcc -o binder_demo binder_demo.c
./binder_demo

验证方法与判定标准

运行程序后,观察标准输出是否按以下顺序出现四阶段日志:

  1. [Server]注册服务,随后[驱动]打印分配句柄;
  2. [Client]查询服务,[驱动]返回同一句柄;
  3. [Client]发起调用,[驱动]转发并打印参数;
  4. [驱动]返回结果,[Client]打印收到结果。

若输出中句柄值一致、参数21经Server处理后返回42,且四阶段顺序正确,则判定通过。该结果由代码逻辑直接推出,可作为预期输出:

===== Binder四角色交互模拟 =====
[Server] 向ServiceManager注册服务...
[驱动] Server注册服务 'demo',分配句柄 100
[Server] 注册完成,等待调用...
[Client] 向ServiceManager查询服务...
[驱动] Client查询服务 'demo',返回句柄 100
[Client] 通过句柄 100 发起调用,参数=21
[驱动] 转发调用 code=3 到句柄 100,参数=21
[驱动] Server返回结果 42
[Client] 收到结果 42
===== 模拟结束 =====
Binder四角色交互时序 Client ServiceManager Binder驱动 Server ① ADD_SERVICE 注册"demo" 存储映射 ② 返回句柄100 ③ GET_SERVICE 查询"demo" ④ 返回句柄100 ⑤ TRANSACTION param=21 转发调用 ⑥ 返回结果42 result=42 ①注册 ②句柄 ③查询 ④句柄 ⑤调用 ⑥返回