0
点赞
收藏
分享

微信扫一扫

Android面试:挂了三次的ServiceManager-工作原理,这次怎么也得整的明明白白

追风骚年 2022-03-20 阅读 51

其中三个函数对应了 ServiceManager 初始化的三个关键工作:

  1. binder_open():打开 binder 驱动并映射内存块大小为 128KB
  2. binder_become_context_manager():将自己设置为 Binder "DNS" 管理者
  3. binder_loop():进入循环,等待 binder 驱动发来消息

下面分别来分析这三个函数,首先来看 binder_open() 是怎么打开 binder 驱动并映射内存的:

struct binder_state binder_open(size_t mapsize){
struct binder_state
bs;
struct binder_version vers;
bs = malloc(sizeof(*bs));
...
//打开 binder 驱动,最终调用 binder_open() 函数
bs->fd = open("/dev/binder", O_RDWR | O_CLOEXEC);
...
//获取 Binder 版本,最终调用 binder_ioctl() 函数
ioctl(bs->fd, BINDER_VERSION, &vers)
...
//将虚拟内存映射到 Binder,最终调用 binder_mmap() 函数
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
...
return bs;
}

再来看 binder_become_context_manager() 是怎么将自己设置为 Binde

Android面试:挂了三次的ServiceManager-工作原理,这次怎么也得整的明明白白

r "DNS 管理者的":

int binder_become_context_manager(struct binder_state *bs){
//发送 BINDER_SET_CONTEXT_MGR 命令,最终调用 binder_ioctl() 函数
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

最后来看 binder_loop() 是怎么循环等待并处理 binder 驱动发来的消息:

void binder_loop(struct binder_state *bs, binder_handler func){
int res;
//执行 BINDER_WRITE_READ 命令所需的数据格式:
struct binder_write_read bwr;
uint32_t readbuf[32]; //每次读取数据的大小
readbuf[0] = BC_ENTER_LOOPER;
//先将 binder 驱动的进入循环命令发送给 binder 驱动:
binder_write(bs, readbuf, sizeof(uint32_t));
for (;;) { //进入循环
bwr.read_size = sizeof(readbuf);
//读取到的消息数据存储在 readbuf
bwr.read_buffer = (uintptr_t) readbuf;
//执行 BINDER_WRITE_READ 命令读取消息数据
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
if (res < 0) {
ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
break;
}
//处理读取到的消息数据
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
...
}
}

BINDER_WRITE_READ 命令既可以用来读取数据也可以写入数据,具体是写入还是读取依赖 binder_write_read 结构体的 write_size 和 read_size 哪个大于 0,上面代码通过 bwr.read_size = sizeof(readbuf) 赋值,所以是读取消息。

binderparse() 方法内部处理由 binder 驱动主动发出的、一系列 BR 开头的命令,包括上面提到过的 BR_TRANSACTION、BR_REPLY 等,简化后的代码如下:

int binder_parse(struct binder_state bs, struct binder_io bio,
uintptr_t ptr, size_t size, binder_handler func){
switch(cmd) {
case BR_TRANSACTION: {
...
res = func(bs, txn, &msg, &reply); //处理消息
//返回处理结果
inder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
...
break;
}
case BR_REPLY: {...}
case BR_DEAD_BINDER: {...}
...
}
}

对于 BR_TRANSACTION 命令主要做了两个工作,一是调用 func() 具体处理消息;二是调用 inder_send_reply() 将消息处理结果告知给 binder 驱动,注意这里的 func 是由 service_manager.c main 函数中传过来的方法指针,也就是 svcmgr_handler() 方法。

服务注册与查询

经过上面 ServiceManager 服务启动的过程分析,已经知道由 binder 驱动主动发过来的 BR_TRANSACTION 命令最终在 service_manager.c 的 svcmgr_handler() 方法中处理,那服务的注册与查询请求想必就是在这个方法中实现的了,确实如此,简化后的关键代码如下:

int svcmgr_handler(struct binder_state bs,
struct binder_transaction_data
txn,
struct binder_io msg,
struct binder_io
reply){
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
//查询服务,根据 name 查询 Server Handle

最后

我见过很多技术leader在面试的时候,遇到处于迷茫期的大龄程序员,比面试官年龄都大。这些人有一些共同特征:可能工作了7、8年,还是每天重复给业务部门写代码,工作内容的重复性比较高,没有什么技术含量的工作。问到这些人的职业规划时,他们也没有太多想法。

其实30岁到40岁是一个人职业发展的黄金阶段,一定要在业务范围内的扩张,技术广度和深度提升上有自己的计划,才有助于在职业发展上有持续的发展路径,而不至于停滞不前。

不断奔跑,你就知道学习的意义所在!

举报

相关推荐

0 条评论