本章学习内容:学习 ServiceManager 的启动流程
前言
ServiceManager 是 Binder 进程间通信机制的核心组件之一, 它扮演者 Binder 进程间通信机制上下文管理者的角色, 同时负责管理系统中的 Service 组件, 并且向 Client 组件提供获取 Service 代理对象的服务.
ServiceManager (下文都称 SM) 运行在一个独立的进程中, 因此, Service 组件和 Client 组件也需要通过进程间通信机制来和它进行交互, 而采用的进程间通信机制整好也是 Binder 机制. 这样看来 SM 除了是 Binder 进程间通信机制的上下文管理者外, 还是一个特殊的 Service 组件.
为了阅读方便, 将会去掉一些无关紧要的代码, 只保留关键代码. 不懂 C/C++ 代码也没关系, 这里只看流程. 不做具体的分析, 需要了解具体分析的朋友可以去看老罗的的 Android 系统源码情景分析 一书. 本文也是参考这本书来学习的. 只不过省去了一些步骤.
一. ServiceManager 的启动
1. servicemanager.rc
SM 是由 init 进程通过解析 init.rc 文件而创建的, 在 Android 7.0 以后, 独立出了 servicemanager.rc, 位于 /frameworks/native/cmds/servicemanager/servicemanager.rc
脚本如下
service servicemanager /system/bin/servicemanager
class core animation
user system
group system readproc
critical
onrestart restart healthd
onrestart restart zygote
onrestart restart audioserver
onrestart restart media
onrestart restart surfaceflinger
onrestart restart inputflinger
onrestart restart drm
onrestart restart cameraserver
writepid /dev/cpuset/system-background/tasks
第一行的关键字 service 表明 SM 是以服务的形式启动的, 对应的程序文件和进程名称分别为 /system/bin/servicemanager
与 servicemanager
.
所对应的源文件就是当前文件上级目录下的 service_manager.c
找到这个类中的入口函数 main
函数.
2. servicemanager.c 中的 main 函数
源码路径: /frameworks/native/cmds/servicemanager/service_manager.c
int main(int argc, char **argv)
{
struct binder_state *bs;
bs = binder_open(128*1024);
...
if (binder_become_context_manager(bs)) {
ALOGE("cannot become context manager (%s)\n", strerror(errno));
return -1;
}
...
...
binder_loop(bs, svcmgr_handler);
return 0;
}
通过上面代码大概可看出, SM 的启动过程由 3 个步骤组成,
- 调用函数
binder_open
打开设备文件/dev/binder
以及将它映射到本进程的地址空间. - 调用函数
binder_become_context_manager
将自己注册为所有服务的大管家. - 调用函数
binder_loop
来循环等待和处理 Client 进程的通信请求.
下面先看在 binder_open
中都做了什么事情.
3. binder_open
源码路径: /frameworks/native/cmds/servicemanager/binder.c
struct binder_state *binder_open(size_t mapsize)
{
struct binder_state *bs;
struct binder_version vers;
...
bs->fd = open("/dev/binder", O_RDWR);
...
...
bs->mapsize = mapsize;
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
...
return bs;
...
}
在 binder_open
与 servicemanager.main
函数中都出现了一个结构体 binder_state
那么它究竟是什么样呢.
struct binder_state
{
int fd;
void *mapped;
size_t mapsize;
};
SM 打开了设备文件 /dev/binder
之后, 就会将得到的文件描述符保存在一个 binder_state
结构体的成员变量 fb
中. 以便后面可以通过它来和 Binder 驱动程序交互.
那么一个进程如果要和 Binder 驱动程序交互, 除了要打开设备文件外, 还需要将该设备文件映射到进程的地址空间, 以便 Binder 驱动程序可以为它分配内核缓冲区来保存进程间通信的数据. 因此 SM 将映射后得到的地址空间大小和气质地址保存到 binder_state
结构体的成员变量 mapsize
与 mapped
中.
那么接着回到 binder_open
函数中. 看到调用函数open
打开 /dev/binder
设备文件. 得到文件描述符.保存到 binder_state
结构体的成员变量 fd
中.
当使用进程函数
open
打开设备文件/dev/binder
时, Binder 驱动中的函数binder_open
就会被调用, 会为当前进程创建一个binder_proc
结构体, 用来描述当前进程的 Binder 进程间通信状态.
从传入的参数 mapsize
得知大小为 128K. 接着调用函数 mmap
将设备文件 /dev/binder
映射到进程的地址空间, 请求映射的地址空间大小为 128K, 即请求 Binder 驱动程序为进程分配 128K 大小的内核缓冲区. 映射后得到的地址空间的起始地址和大小分别保存在一个 binder_state
结构体 bs 的成员变量 mapsize
与 mapped
中. 最后将这个结构体返回给调用者, 即 main 函数.
4. binder_become_context_manager 注册为所有服务的大管家
调用 binder_become_context_manager()
注册为所有服务的大管家, 也可以理解为 SM 要成为 Binder 进程间通信机制的上下文管理者, 就必须通过 IO 控制命令 BINDER_SET_CONTEXT_MGR
将自己注册到 Binder 驱动程序中. 进入 binder_become_context_manager()
函数.
源码路径: /frameworks/native/cmds/servicemanager/binder.c
int binder_become_context_manager(struct binder_state *bs)
{
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
看到调用 ioctl
函数, 就知道又要进入到驱动层的 binder_ioctl()
函数. 也就是在这个函数内处理这个 IO 控制命令的.
由于与 SM 对应的 Binder 本地对象是一个虚拟的对象, 并且它的地址等于 0, 因此, 函数
binder_become_context_manager
就将 IO 控制命令BINDER_SET_CONTEXT_MGR
的参数设置为 0. 表示 SM 对应的句柄为 0.
5. binder_ioctl 处理 IO 控制命令 BINDER_SET_CONTEXT_MGR
源码路径: kernel 3.18下的 /drivers/staging/android/binder.c
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
...
switch (cmd) {
...
case BINDER_SET_CONTEXT_MGR:
ret = binder_ioctl_set_ctx_mgr(filp);
if (ret)
goto err;
break;
...
}
...
}
在 switch
中如果命中了这个 IO 控制命令, 又调用了 binder_ioctl_set_ctx_mgr
函数.
6. binder_ioctl_set_ctx_mgr 具体的处理 IO 控制命令 BINDER_SET_CONTEXT_MGR
源码路径: kernel 3.18下的 /drivers/staging/android/binder.c
static int binder_ioctl_set_ctx_mgr(struct file *filp)
{
int ret = 0;
struct binder_proc *proc = filp->private_data;
...
if (binder_context_mgr_node != NULL) {
pr_err("BINDER_SET_CONTEXT_MGR already set\n");
ret = -EBUSY;
goto out;
}
if (uid_valid(binder_context_mgr_uid)) {
if (!uid_eq(binder_context_mgr_uid, curr_euid)) {
pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n", from_kuid(&init_user_ns, curr_euid),from_kuid(&init_user_ns,binder_context_mgr_uid));
ret = -EPERM;
goto out;
}
} else {
binder_context_mgr_uid = curr_euid;
}
binder_context_mgr_node = binder_new_node(proc, 0, 0);
if (binder_context_mgr_node == NULL) {
ret = -ENOMEM;
goto out;
}
...
return ret;
}
先获得前面 Binder 驱动程序为 SM 进程创建的一个 binder_proc
结构体并保存在变量 proc
中.
binder_context_mgr_node
是一个全局变量. 声明为 static struct binder_node *binder_context_mgr_node;
用来描述 Binder 进程间通信机制的上下文管理者对应的一个 Binder 实体对象. 如果它不等于 null 成立, 说明前面已经有组件将自己注册为上下文管理者了, 由于 Binder 驱动程序不允许重复注册上下文管理者, 所以直接就返回了错误.
接着判断用户 ID 是否有效, 当前这里是无效的. 直接执行 else
中内容. 将当前进程的有效用户 ID 赋值给全局变量 binder_context_mgr_uid
.
全局变量
binder_context_mgr_uid
用来描述注册了 Binder 进程间通信机制的上下文管理者进程的有效用户 ID. 如果值不为-1, 即判断 if 内的判断成立, 也说明前面已经有注册过上下文管理者了, 接着需要进一步检查当前进程的有效用户 ID 是否全局变量binder_context_mgr_uid
的值. 如果不等于, 就直接返回错误.
注意: Binder 驱动程序允许同一个进程重复使用 IO 控制命令BINDER_SET_CONTEXT_MGR
来注册上下文管理者.
通过前面的合法性检查后, 调用函数 binder_new_node
为 SM 创建一个 Binder 实体对象并保存在全局变量 binder_context_mgr_node
中. 最后再对 binder_context_mgr_node
进行非空校验. 现在进入到 binder_new_node
函数中. 看在创建 SM 的 Binder 实体对象的时候都做了一些什么.
7. binder_new_node 创建 SM 的 Binder 实体对象
源码路径: kernel 3.18下的 /drivers/staging/android/binder.c
static struct binder_node *binder_new_node(struct binder_proc *proc, binder_uintptr_t ptr, binder_uintptr_t cookie)
{
struct rb_node **p = &proc->nodes.rb_node;
struct rb_node *parent = NULL;
struct binder_node *node;
...
...
node = kzalloc(sizeof(*node), GFP_KERNEL);
if (node == NULL)
return NULL;
binder_stats_created(BINDER_STAT_NODE);
rb_link_node(&node->rb_node, parent, p);
rb_insert_color(&node->rb_node, &proc->nodes);
node->debug_id = ++binder_last_id;
node->proc = proc;
node->ptr = ptr;
node->cookie = cookie;
node->work.type = BINDER_WORK_NODE;
INIT_LIST_HEAD(&node->work.entry);
INIT_LIST_HEAD(&node->async_todo);
...
return node;
}
调用 kzalloc
函数创建一个新的 Binder 实体对象, 同时将其加入到宿主进程的成员变量 nodes
所描述的一个红黑树中. 最后初始化这个新创建的 Binder 实体对象. 至此, SM 就成功的将自己注册为 Binder进程间通信机制的上下文管理者了. 接着返回到用户空间, 也就是第2步 main
函数中, 调用了 binder_loop
函数来循环等待和处理 Client 进程的通信请求, 即等待和处理 Service 组件的注册请求, 以及其他代理对象的获取请求.
8. binder_loop 循环等待处理请求
源码路径: /frameworks/native/cmds/servicemanager/binder.c
01 void binder_loop(struct binder_state *bs, binder_handler func)
02 {
03 int res;
04 struct binder_write_read bwr;
05 uint32_t readbuf[32];
06 bwr.write_size = 0;
07 bwr.write_consumed = 0;
08 bwr.write_buffer = 0;
09 readbuf[0] = BC_ENTER_LOOPER;
10 binder_write(bs, readbuf, sizeof(uint32_t));
11 for (;;) {
12 bwr.read_size = sizeof(readbuf);
13 bwr.read_consumed = 0;
14 bwr.read_buffer = (uintptr_t) readbuf;
15 res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
...
16 res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
...
}
}
}
由于 SM 需要在系统运行期间为 Service 组件和 Client 组件提供服务, 因此它就需要通过一个无线循环来等待和处理 Service 和 Client 组件的进程间通信请求.
函数的第一个参数 *bs
指向前面在第 5 步 binder_open
中创建的一个 binder_state
结构体.
函数的第二个参数 func
指向 SM 中的函数 svcmgr_handler
, 它是用来处理 Service 组件和 Client 组件进程间通信请求的.
一个线程要通过协议
BC_ENTER_LOOPER
或者BC_REGISTER_LOOPER
将自己注册为 Binder 线程, 以便 Binder 驱动程序可以将进程间通信请求分发给它处理. 由于 SM 进程的主线程是主动成为一个 Binder 线程的, 因此,它就需要使用BC_ENTER_LOOPER
协议代码将自己注册到 Binder 驱动程序中.
在第 9行处首先将 BC_ENTER_LOOPER
协议代码写入到缓冲区 readbuf
中. 接着第 10 行调用函数 binder_write
将它发送到 Binder 驱动程序中.
9. binder_write 准备注册到 Binder 驱动程序.
源码路径: /frameworks/native/cmds/servicemanager/binder.c
01 int binder_write(struct binder_state *bs, void *data, size_t len)
02 {
03 struct binder_write_read bwr;
04 int res;
05 bwr.write_size = len;
06 bwr.write_consumed = 0;
07 bwr.write_buffer = (uintptr_t) data;
08 bwr.read_size = 0;
09 bwr.read_consumed = 0;
10 bwr.read_buffer = 0;
11 res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
12 if (res < 0) {
13 fprintf(stderr,"binder_write: ioctl failed (%s)\n",strerror(errno));
14 }
15 return res;
}
由于 BC_ENTER_LOOPER
协议是通过 IO 控制命令 BINDER_WRITE_READ
发送到 Binder 驱动程序中的, 所以第3行先定义一个 binder_write_read
的结构体 bwr
,
第 7 行将参数 data
所指向的一块缓冲区作为它的输入缓冲区. 接着 8,9,10 行将结构体 bwr
的输出缓冲区设置为空. 这样的话当线程将自己注册到 Binder 驱动程序之后, 就会马上返回到用户空间. 而不会在 Binder 驱动程序中等待 Client 进程的通信请求.
由于参数 ata
所指向的一块缓冲区的内容以及被设置为 BC_ENTER_LOOPER
协议代码, 因此接下来在 11 行就直接调用函数 ioctl
将当前线程注册到 Binder 驱动程序中了.
10. binder_ioctl, 在 Binder 驱动层处理 IO 控制命令 BINDER_WRITE_READ
源码路径: kernel 3.18下的 /drivers/staging/android/binder.c
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
...
switch (cmd) {
...
case BINDER_WRITE_READ:
ret = binder_ioctl_write_read(filp, cmd, arg, thread);
...
break;
}
...
return ret;
}
因为在上一步发送来的 IO 控制命令为 BINDER_WRITE_READ
, 所以直接进入到这个 case
中. 紧接着又调用了 binder_ioctl_write_read
函数. 继续进入
11. binder_ioctl_write_read
源码路径: kernel 3.18下的 /drivers/staging/android/binder.c
static int binder_ioctl_write_read(struct file *filp, unsigned int cmd, unsigned long arg, struct binder_thread *thread)
{
...
...
if (bwr.write_size > 0) {
ret = binder_thread_write(proc, thread, bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
...
}
if (bwr.read_size > 0) {
ret = binder_thread_read(proc, thread, bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
...
}
...
return ret;
}
第一个判断 bwr.write_size > 0
条件是成立的. 命中 if
后又调用了 binder_thread_write
函数.
第二个判断这时候是不成立的, 所以 binder_thread_read
函数. 这个后面会被执行. 先看binder_thread_write
12. binder_thread_write 处理 BC_ENTER_LOOPER 协议
源码路径: kernel 3.18下的 /drivers/staging/android/binder.c
static int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, binder_uintptr_t binder_buffer, size_t size, binder_size_t *consumed)
{
...
while (ptr < end && thread->return_error == BR_OK) {
...
switch (cmd) {
...
case BC_ENTER_LOOPER:
...
thread->looper |= BINDER_LOOPER_STATE_ENTERED;
break;
...
}
...
}
这个函数太长了 ,所以只截取了一部分. 在 case
内主要就是将目标线程 thread
的状态设置为 BINDER_LOOPER_STATE_ENTERED
, 表明该线程是一个 Binder 线程, 可以处理进程间通信请求.
函数 binder_thread_write
处理完 BC_ENTER_LOOPER
协议之后, 就返回到函数 binder_ioctl
中, 后者接着又返回到用户空间中. 即 SM 进程的函数 binder_write
中. 也就是第9步. 最后返回到第8步 binder_loop
函数中.
为了方便查看, 在这里贴上第 8 步的代码.
01 void binder_loop(struct binder_state *bs, binder_handler func)
02 {
03 int res;
04 struct binder_write_read bwr;
05 uint32_t readbuf[32];
06 bwr.write_size = 0;
07 bwr.write_consumed = 0;
08 bwr.write_buffer = 0;
09 readbuf[0] = BC_ENTER_LOOPER;
10 binder_write(bs, readbuf, sizeof(uint32_t));
11 for (;;) {
12 bwr.read_size = sizeof(readbuf);
13 bwr.read_consumed = 0;
14 bwr.read_buffer = (uintptr_t) readbuf;
15 res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
...
16 res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
...
}
}
进入 11 行的死循环了, 在循环中, 不断的使用 IO 控制命令 BINDER_WRITE_READ
来检查 Binder 驱动程序是否有新的进程间通信请求需要它来处理. 如果有, 就交给函数 binder_parse
来处理. 否则当前线程就会在 Binder 驱动程序中休眠等待, 直到有新的进程间通信请求到来为止. 所以接着又会进入到第 10 步, 接着又进入到第 11 步.
在 for 循环中, 每一次通过 IO 控制命令
BINDER_WRITE_READ
进入到 Binder 驱动程序时, 所传递的binder_write_read
结构体中的输入缓冲区长度均为 0, 而输出缓冲区, 也就是read_size
的长度等于缓冲区readbuf
的大小, 即 128 个字节. 因此, 在第 11 步时, 会进入到第二个 if 判断内.执行binder_thread_read
函数来检查 SM 进程是否有新的进程间通信请求需要处理, 休眠等待也是在此.
由于 15 行, 调用 ioct
函数会后会再此走到第 10 步, 接着进入到第 11 步执行 binder_ioctl_write_read
函数中的第二个 if 执行 binder_thread_read
, 所以直接进入到 binder_thread_read
去查看, 省去了跳到 10,11 步.
13. binder_thread_read 检查是否有请求需要处理
源码路径: kernel 3.18下的 /drivers/staging/android/binder.c
static int binder_thread_read(struct binder_proc *proc, struct binder_thread *thread, binder_uintptr_t binder_buffer, size_t size, binder_size_t *consumed, int non_block)
{
...
int wait_for_proc_work;
...
retry:
//分析 1
wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo);
...
//分析 2
thread->looper |= BINDER_LOOPER_STATE_WAITING;
if (wait_for_proc_work)
proc->ready_threads++;
...
//分析 3
if (wait_for_proc_work) {
...
if (non_block) {
...
} else
ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
} else {
...
}
...
//分析 4
if (wait_for_proc_work)
proc->ready_threads--;
thread->looper &= ~BINDER_LOOPER_STATE_WAITING;
...
...
return 0;
}
如果一个线程的的事务堆栈 transaction_stack
不等于 NULL, 表示它正在等待其他线程完成另外一个事务.
如果一个线程的 todo
队列不等于 NULL, 表示该线程有未处理的工作项.
一个线程只有在其事务堆栈 transaction_stack
为 NULL, 并且 todo
队列为 NULL 时, 才可以去处理其所属进程todo
队列中的待处理工作项. 否则就要处理其事务堆栈 transaction_stack
中的事物或者 todo
队列中的待处理工作项.
在分析 1 处检查这两个是否都为 NULL, 如果两个都成立则 wait_for_proc_work
的值为 1. 表示接下来要检查它所属进程的todo
队列中是否有未处理的工作项, 否则为 0, 则表示接下来要优先处理自己的事务或者工作项了.. 本流程此次 wait_for_proc_work
的值为 1.
分析 2 处将当前线程的状态设置为 BINDER_LOOPER_STATE_WAITING
表示该线程正处于空闲状态. 然后判断 wait_for_proc_work
值是否为 1, 如果是说明当前线程所属的进程又多了一个空闲的 Binder 线程. 就将该进程的空闲 Binder 线程数 ready_threads
加 1. 接下来如果 Binder 驱动程序发现当前线程有新的工作项要处理时, 在分析 4 处会将状态位 BINDER_LOOPER_STATE_WAITING
清空. 并且根据变量 wait_for_proc_work
的值是否为 1 来决定是否要减少它所属进程的空闲 Binder 线程数.
分析 3 因为本次流程 wait_for_proc_work
值为 1. 所以直接命中 if. non_block
表示当前线程是否是以非阻塞模式打开设备文件 /dev/binder
. 本次流程为阻塞, 所以这里直接进入 else
, 执行 wait_event_freezable_exclusive
函数来睡眠等待直到其所属的进程有新的未处理工作项为止.
binder_has_proc_work
就是检查是否有未处理工作项的函数
到这里 SM 的启动就分析完了, 接下来就是 SM 的获取. 假设 SM 中没有待处理的工作项, 因此它就睡眠在 Binder 驱动函数 binder_thread_read
中. 也就是上面的分析 3 处. 等待其他进程的 Service组件或者 Client 组件向它发出进程间通信请求.