Android Service Manager 的启动流程

本章学习内容:学习 Service Manager 的启动流程

前言

Service Manager 是 Binder 进程间通信机制的核心组件之一, 它扮演者 Binder 进程间通信机制上下文管理者的角色, 同时负责管理系统中的 Service 组件, 并且向 Client 组件提供获取 Service 代理对象的服务.

Service Manager (下文都称 SM) 运行在一个独立的进程中, 因此, Service 组件和 Client 组件也需要通过进程间通信机制来和它进行交互, 而采用的进程间通信机制整好也是 Binder 机制. 这样看来 SM 除了是 Binder 进程间通信机制的上下文管理者外, 还是一个特殊的 Service 组件.

为了阅读方便, 将会去掉一些无关紧要的代码, 只保留关键代码. 不懂 C/C++ 代码也没关系, 这里只看流程. 不做具体的分析, 需要了解具体分析的朋友可以去看老罗的的 Android 系统源码情景分析 一书. 本文也是参考这本书来学习的. 只不过省去了一些步骤.

一. Service Manager 的启动

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/servicemanagerservicemanager.
所对应的源文件就是当前文件上级目录下的 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_openservicemanager.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 结构体的成员变量 mapsizemapped 中.

那么接着回到 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 的成员变量 mapsizemapped 中. 最后将这个结构体返回给调用者, 即 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 步.

12. 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 组件向它发出进程间通信请求.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章