在 Android 系统中含有大量的进程间通讯,Android 系统的开发人员为了进程间通讯使用起来更方便,在Linux内核里搞了一个 Binder 驱动,然后利用这个 Binder 驱动进行一些进程间的数据传输。在 Android 中对如何使用 Binder 驱动进行了封装,有C版本,也有C++和JAVA版本,将它们统称为 Binder 系统。有了这些封装之后,使用者只需要调用固定的接口,便可以达到其想要的目的。那么,本文来分析C语言版本的 Binder 系统内部实现。
Binder 系统是典型的C/S架构,有点像内核中的总线驱动模型,在内核中将一整个驱动程序可以分割为 device bus driver,在 Binder 中,则有 Client ServiceManager Server。
首先是 ServiceManager,它负责管理 Server 注册进来的 Service,也就是说 Server 要向 Servermanger 注册服务,那么 Server 如何知道谁是 ServiceManager 呢?在 Binder 系统中用一个整数 handle 来描述这些进程,对于 ServerManager 它的这个 handle 为 0,所以大家就知道谁是 ServerManager 了。
前面提到了 Server 会向 ServiceManager 注册 Service ,那么 Client 肯定就是向 ServiceManager 查询是否有某一个 Service,如果找到了,ServiceManager 就返回对应 Server 的 handle ,利用这个 handle ,clinet 和 Server 之间就可以直接利用 Binder 驱动进行进程间的通讯。
写到这里,又有一点领悟,ServiceManager 负责管理 Service,其实对于 Server 来说,ServiceManager 是一个特殊的“Server”,为什么呢?因为它的 handle 为 0,Server 通过 Binder 发送数据给它,调用它的注册 Service 函数。对于 Client 和 ServiceManager 之间的关系也是一样的。总之,他们三者之间两两都是C/S模型。
对于 Binder 驱动,其实原理很简单,它只不过是一个数据传输的通道,规定了数据格式,把数据发送给你想发送的handle
进程而已。
ServiceManager分析
Service_manager.c (frameworks\native\cmds\servicemanager)
int main(int argc, char **argv)
{
struct binder_state *bs;
// open binder 驱动,open("/dev/binder", O_RDWR);
bs = binder_open(128*1024);
// 告诉系统,我是 ServiceManager,ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
binder_become_context_manager(bs)
svcmgr_handle = BINDER_SERVICE_MANAGER;
// 陷入循环,接收并处理发送数据
binder_loop(bs, svcmgr_handle);
return 0;
}
void binder_loop(struct binder_state *bs, binder_handle func)
{
int res;
struct binder_write_read bwr;
uint32_t readbuf[32];
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
readbuf[0] = BC_ENTER_LOOPER;
binder_write(bs, readbuf, sizeof(uint32_t));
for (;;) {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
// 读数据,获取到的数据会存放在 binder_write_read 结构体中
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
// 解析数据
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
}
}
在这里,我们看到binder通信,使用的是一个 binder_write_read 结构体进行的数据封装,无论读还是写都用它
int binder_parse(struct binder_state *bs, struct binder_io *bio,
uintptr_t ptr, size_t size, binder_handle
func)
{
int r = 1;
uintptr_t end = ptr + (uintptr_t) size;
while (ptr < end) {
uint32_t cmd = *(uint32_t *) ptr;
ptr += sizeof(uint32_t);
switch(cmd) {
...
case BR_TRANSACTION: {
struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
binder_dump_txn(txn);//打印数据
if (func) {
unsigned rdata[256/4];
struct binder_io msg;
struct binder_io reply;
int res;
bio_init(&reply, rdata, sizeof(rdata), 4);
// 将收到的数据封构造成一个 binder_io
bio_init_from_txn(&msg, txn);
// 调用前面传入的
svcmgr_handle
处理,处理结果放到 binder_io reply 中
res = func(bs, txn, &msg, &reply);
// 将 reply 在封装成 binder_write_read 用 ioctl 发回去
binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
}
ptr += sizeof(*txn);
break;
}
...
}
return r;
}
至于 binder_write_read 和 binder_io 之间的转换细节,这里不是我们分析的重点,下面来看一下对于 ServiceManager 它是如何处理“业务”数据的。
int svcmgr_handle(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply)
{
struct svcinfo *si;
uint16_t *s;
size_t len;
uint32_t handle;
uint32_t strict_policy;
int allow_isolated;
strict_policy = bio_get_uint32(msg);
s = bio_get_string16(msg, &len);
// txn->code 表示调用Server的哪个函数
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
handle = do_find_service(bs, s, len, txn->sender_euid, txn->sender_pid);
if (!handle)
break;
bio_put_ref(reply, handle);
return 0;
case SVC_MGR_ADD_SERVICE:
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
handle = bio_get_ref(msg);
allow_isolated = bio_get_uint32(msg) ? 1 : 0;
if (do_add_service(bs, s, len, handle, txn->sender_euid,
allow_isolated, txn->sender_pid))
return -1;
break;
...
}
bio_put_uint32(reply, 0);
return 0;
}
先来看注册服务,
bio_get_string16(msg, &len); 从 binder_io 中取出一个字符串,这里应该就是服务的名字,然后不知道从那里搞到一个对应的 handle (
uint32_t
),最后把这个 service 注册进去。
获取服务的过程与之相反....
在 Client 和 Server 中,既然要利用 binder 进行通讯,那么,它们必然也是去 Open binder 驱动,然后构造 bind_write_read 结构体发送数据,然而对于构造这些数据我们可以简单的使用 bind_call 函数,下面来参考一个例子:
Bctest.c (native\cmds\servicemanager)
int svcmgr_publish(struct binder_state *bs, uint32_t target, const char *name, void *ptr)
{
int status;
unsigned iodata[512/4];
struct binder_io msg, reply;
bio_init(&msg, iodata, sizeof(iodata), 4);
bio_put_uint32(&msg, 0); // strict mode header
bio_put_string16_x(&msg, SVC_MGR_NAME);
bio_put_string16_x(&msg, name);
bio_put_obj(&msg, ptr);
if (binder_call(bs, &msg, &reply, target, SVC_MGR_ADD_SERVICE))
return -1;
status = bio_get_uint32(&reply);
binder_done(bs, &msg, &reply);
return status;
}
uint32_t svcmgr_lookup(struct binder_state *bs, uint32_t target, const char *name)
{
uint32_t handle;
unsigned iodata[512/4];
struct binder_io msg, reply;
bio_init(&msg, iodata, sizeof(iodata), 4);
bio_put_uint32(&msg, 0); // strict mode header
bio_put_string16_x(&msg, SVC_MGR_NAME);
bio_put_string16_x(&msg, name);
if (binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE))
return 0;
handle = bio_get_ref(&reply);
if (handle)
binder_acquire(bs, handle);
binder_done(bs, &msg, &reply);
return handle;
}
这里可以看到,无论是注册服务还是查询服务,均是构造了一个 binder_io 然后调用 binder_call 来发送数据,binder_call 的实质还是 bind_write_read 结构体通过 ioctl 发送
int binder_call(
struct binder_state *bs, // binder fd
struct binder_io *msg, // 调用远程函数时的参数,比如注册服务,服务名字就是一个参数
struct binder_io *reply, // 回复的数据存放在 reply
uint32_t target, // 哪个进程?也就是 handle ,如果是 ServiceManager 的话为 0
uint32_t code // 远程进程的函数编号,也就是要调用它的哪个函数,比如注册服务函数,查询服务函数
)
至于如何填充 binder_io ,以及取回的数据如何使用,下节分析!