1. Binder驱动与设备节点初探
任务说明
本案例通过一个最小可运行的C语言模拟程序,分析Binder通信中四个角色——Client、Server、ServiceManager、Binder驱动——的职责划分与一次完整跨进程调用的交互流程。代码不依赖真实Android内核,而是用用户态进程模拟四者关系,使角色边界与调用时序可观测、可验证。
四角色职责与交互流程
Binder通信的本质是:Server向ServiceManager注册服务并等待调用;Client通过ServiceManager查询服务句柄;Binder驱动负责跨进程传递数据并维护线程等待/唤醒。四个角色在一次调用中的时序为:
- Server向ServiceManager发送
ADD_SERVICE请求,注册名为"demo"的服务; - Client向ServiceManager发送
GET_SERVICE请求,获取该服务的代理句柄; - Client通过该句柄向Server发送
TRANSACTION调用(携带整数参数); - 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
验证方法与判定标准
运行程序后,观察标准输出是否按以下顺序出现四阶段日志:
[Server]注册服务,随后[驱动]打印分配句柄;[Client]查询服务,[驱动]返回同一句柄;[Client]发起调用,[驱动]转发并打印参数;[驱动]返回结果,[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
===== 模拟结束 =====