Android笔记 - Binder之守护进程servicemanager

Posted demonyan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android笔记 - Binder之守护进程servicemanager相关的知识,希望对你有一定的参考价值。

servicemanager 是 Binder 进程间通信机制的核心,它扮演了 Binder 通信机制上下文管理者(context manager)的角色,既负责系统中 Service 组件的注册,又负责向 Client 组件提供获取 Service 组件代理对象的服务。

1. servicemanager 何时启动?
servicemanager 运行在一个独立进程当中,从Android启动之Android Framework启动可知,servicemanager 由 init 进程负责启动的,所以它和 Zygote 等系统核心服务是兄弟关系。以下是其启动脚本:

service servicemanager /system/bin/servicemanager                           [1]
    class core                                                              [2]
    user system                                                             [3]
    group system                                                            [4]
    critical                                                                [5]
    onrestart restart healthd
    onrestart restart zygote
    onrestart restart media
    onrestart restart surfaceflinger
    onrestart restart drm

[1] service 关键字表示这是一个系统服务,后面跟着两个参数: servicemanager 是服务名称; /system/bin/servicemanager 是程序执行路径。
[2] class core 表示启动类别。
[3] user system 表示 servicemanager 属于 system 用户。
[4] group system 表示 servicemanager 属于 system 组。
[5] critical 关键字表示 servicemanager 是一个关键服务,异常退出后,该服务需要被重启。重启时,onrestart 描述的5个服务也会被重新启动。

2. servicemanager 如何成为 Binder 通信机制的上下文管理者?
servicemanager 对应的程序源码位于 frameworks/native/cmds/servicemanager 中,入口函数 main 的实现在文件 service_manager.c 中,如下所示:

代码路径:frameworks/native/cmds/servicemanager/service_manager.c
int main(int argc, char **argv)

    struct binder_state *bs;
    void *svcmgr = BINDER_SERVICE_MANAGER;

    bs = binder_open(128*1024);

    if (binder_become_context_manager(bs)) 
        ALOGE("cannot become context manager (%s)\\n", strerror(errno));
        return -1;
    

    svcmgr_handle = svcmgr;
    binder_loop(bs, svcmgr_handler);
    return 0;

以上是 main 函数的所有内容,简直短小精悍。main 函数主要完成三件事情:第一,初始化 Binder 通信环境,打开 Binder 设备文件 /device/binder 并将其映射到 servicemanager 进程的用户地址空间。第二,调用 binder_become_context_manager 函数将自身注册为上下文管理者;第三,进入循环等待,接收并处理 Client 进程发送过来的通信请求。下面看看 binder_open 函数和 binder_become_context_manager 函数的具体实现。

2.1 打开和映射 Binder 设备文件
通过调用 binder_open 函数来初始化 Binder 通信环境,打开和映射 Binder 设备文件 /dev/binder 。代码如下所示:

代码路径:frameworks/native/cmds/servicemanager/binder.c
struct binder_state *binder_open(unsigned mapsize)

    struct binder_state *bs;

    bs = malloc(sizeof(*bs));                                               [1]
    if (!bs) 
        errno = ENOMEM;
        return 0;
    

    bs->fd = open("/dev/binder", O_RDWR);                                   [2]
    if (bs->fd < 0) 
        fprintf(stderr,"binder: cannot open device (%s)\\n",
                strerror(errno));
        goto fail_open;
    

    bs->mapsize = mapsize;
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);    [3]
    ......

binder_open 函数由三部分组成:
[1] 创建 binder_state 结构体 bs,其成员变量 fd 用于保存 Binder 设备文件的文件描述符,mapped 和 mapsize 分别保存调用 mmap 映射设备文件后,返回的用户地址空间的起始地址和大小。

struct binder_state 

    int fd;
    void *mapped;
    unsigned mapsize;
;

[2] 通过 open 系统调用打开 Binder 设备文件。 当 open 系统函数被调用时,最终会调用到 Binder 驱动中的 binder_open 函数。binder_open 创建了一个描述通信进程信息的 binder_proc 结构体 proc,然后对它的一些成员变量进行初始化。
[3] 通过 mmap 系统调用将 Binder 设备文件映射到 servicemanager 进程的用户地址空间。 当 mmap 系统函数被调用时,Binder 驱动中的 binder_mmap 函数就会被调用。binder_mmap 函数实现比较复杂,核心功能是为 servicemanager 进程分配内核缓存区,并映射到其用户地址空间。函数定义如下所示:

代码路径:drivers/staging/android/binder.c
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)

    ......
    area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);            [3.1]
    if (area == NULL) 
        ret = -ENOMEM;
        failure_string = "get_vm_area";
        goto err_get_vm_area_failed;
    
    proc->buffer = area->addr;                                              [3.2]
    proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;     [3.3]
    mutex_unlock(&binder_mmap_lock);

    ......
    proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);[3.4]
    if (proc->pages == NULL) 
        ret = -ENOMEM;
        failure_string = "alloc page array";
        goto err_alloc_pages_failed;
    
    proc->buffer_size = vma->vm_end - vma->vm_start;                        [3.5]

    vma->vm_ops = &binder_vm_ops;
    vma->vm_private_data = proc;

    if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma))    [3.6]
        ret = -ENOMEM;
        failure_string = "alloc small buf";
        goto err_alloc_small_buf_failed;
    
    buffer = proc->buffer;
    INIT_LIST_HEAD(&proc->buffers);
    list_add(&buffer->entry, &proc->buffers);                           
    buffer->free = 1;
    binder_insert_free_buffer(proc, buffer);                                [3.7]

    ......

[3.1] 调用函数 get_vm_area 在内核空间中分配一段长度为 vma->vm_end - vma->vm_start 的地址空间。
[3.2] 将内核空间起始地址保存到 proc->buffer 中。
[3.3] 将用户空间起始地址和内核空间起始地址的差值保存到 proc->user_buffer_offset 中,因此,只要知道其中一个地址,就可以计算出另外一个地址,如下图所示:

由于将 Binder 设备文件映射到 Server 进程的用户地址空间,因此使用 Binder 进程间通信机制时,只需要将 Client 用户地址空间的通信数据拷贝到内核空间, 拷贝一次 Server 进程就可以访问到通信数据。对比其他 IPC 机制(Socket,命名管道,消息队列等),需要先将 Client 用户地址空间的通信数据拷贝到内核空间,然后再从内核空间将通信数据拷贝到 Server 的用户地址空间,一共需要拷贝二次。

[3.4] 创建保存物理页面指针的数组。
[3.5] 将内核空间大小保存在 proc->buffer_size 中。
[3.6] 调用 binder_update_page_range 函数为 proc->buffer 指向的内核空间分配大小为 PAGE_SIZE 的物理页面。
[3.7] 调用 binder_insert_free_buffer 函数,将刚分配的物理页面添加结构体 proc 的成员变量 free_buffers 所指向的红黑树中,该红黑树保存了所有空闲的内核缓存区。

2.2 注册 servicemanager 为上下文管理者
binder_become_context_manager 函数通过 BINDER_SET_CONTEXT_MGR 命令将自己注册为上下文管理者,代码如下所示:

int binder_become_context_manager(struct binder_state *bs)

    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);

最终由 Binder 驱动中的 binder_ioctl 函数来处理 BINDER_SET_CONTEXT_MGR 命令,如下所示:

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)

    int ret;
    struct binder_proc *proc = filp->private_data;
    ......

    switch (cmd) 
    ......
    case BINDER_SET_CONTEXT_MGR:
        if (binder_context_mgr_node != NULL) 
            printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\\n");
            ret = -EBUSY;
            goto err;
        
        if (binder_context_mgr_uid != -1) 
            if (binder_context_mgr_uid != current->cred->euid) 
                printk(KERN_ERR "binder: BINDER_SET_"
                       "CONTEXT_MGR bad uid %d != %d\\n",
                       current->cred->euid,
                       binder_context_mgr_uid);
                ret = -EPERM;
                goto err;
            
         else
            binder_context_mgr_uid = current->cred->euid;
        binder_context_mgr_node = binder_new_node(proc, NULL, NULL);
        if (binder_context_mgr_node == NULL) 
            ret = -ENOMEM;
            goto err;
        
        binder_context_mgr_node->local_weak_refs++;
        binder_context_mgr_node->local_strong_refs++;
        binder_context_mgr_node->has_strong_ref = 1;
        binder_context_mgr_node->has_weak_ref = 1;
        break;
    ......

首先,进行一些合法性检查,确保之前没有组件注册成为上下文管理者。然后,调用 binder_new_node 函数为 servicemanager 创建一个对应的 Binder 实体对象,并将其保存在全局变量 binder_context_mgr_node 中。最后,初始化 binder_context_mgr_node 内部的引用计数。
至此,servicemanager 已经成功将自己注册为 Binder 通信机制的上下文管理者。

3. servicemanager 如何提供服务?
servicemanager 在系统运行时为 Service 组件提供注册服务,为 Client 组件提供获取 Service 组件代理对象的服务,这些都是通过调用函数 binder_loop 来实现的。binder_loop 函数定义如下:

void binder_loop(struct binder_state *bs, binder_handler func)

    int res;
    struct binder_write_read bwr;
    unsigned readbuf[32];

    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;

    readbuf[0] = BC_ENTER_LOOPER;
    binder_write(bs, readbuf, sizeof(unsigned));                            [1]

    for (;;) 
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (unsigned) readbuf;

        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);                       [2]

        if (res < 0) 
            ALOGE("binder_loop: ioctl failed (%s)\\n", strerror(errno));
            break;
        

        res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);        [3]
        if (res == 0) 
            ALOGE("binder_loop: unexpected reply?!\\n");
            break;
        
        if (res < 0) 
            ALOGE("binder_loop: io error %d %s\\n", res, strerror(errno));
            break;
        
    

[1] 调用 binder_write 函数将 BC_ENTER_LOOPER 命令协议写入 Binder 驱动,从而通知 Binder 驱动 servicemanager 已经准备就绪,可以接收处理进程间通信请求。调用过程如下所示:

-> binder_write(bs, readbuf, sizeof(unsigned));
-> ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
-> binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);

[2] 通过 ioctl 系统调用检查 Binder 驱动是否有进程间通信请求需要 servicemanager 处理。如果没有,那么 servicemanager 线程就会在 Binder 驱动中睡眠等待,直到有新的进程间通信请求。调用过程如下所示:

-> ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
-> binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);

binder_thread_read 函数会检查 Binder 驱动是否有新的进程间通信请求需要它来处理,并且可能会在 Binder 驱动中睡眠等待。如下所示:

static int binder_thread_read(struct binder_proc *proc,
                  struct binder_thread *thread,
                  void  __user *buffer, int size,
                  signed long *consumed, int non_block)

    ....
    wait_for_proc_work = thread->transaction_stack == NULL &&
                list_empty(&thread->todo)                                                        [2.1]
    if (wait_for_proc_work) 
        if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
                    BINDER_LOOPER_STATE_ENTERED))) 
            binder_user_error("binder: %d:%d ERROR: Thread waiting "
                "for process work before calling BC_REGISTER_"
                "LOOPER or BC_ENTER_LOOPER (state %x)\\n",
                proc->pid, thread->pid, thread->looper);
            wait_event_interruptible(binder_user_error_wait,
                         binder_stop_on_user_error < 2);
        
        binder_set_nice(proc->default_priority);
        if (non_block) 
            if (!binder_has_proc_work(proc, thread))
                ret = -EAGAIN;
         else
            ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));[2.2]
     else 
        if (non_block) 
            if (!binder_has_thread_work(thread))
                ret = -EAGAIN;
         else
            ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));            [2.3]
    
    ......

[2.1] 判断当前线程的 todo 队列中是否有待处理工作项,如果有的话则将 wait_for_proc_work 的值赋值为0,当前线程会优先处理 todo 队列中的待处理工作项。否则 wait_for_proc_work 值为1,当前线程可以处理所属进程的 todo 队列中的待处理工作项。
[2.2] 如果 wait_for_proc_work 值为1,并且当 non_block 值为0时,当前线程调用 wait_event_freezable_exclusive 函数,睡眠等待在所属进程的 wait 等待队列中,直到所属进程的 todo 队列有新的待处理工作项。
[2.3] 如果 wait_for_proc_work 值为0,并且当 non_block 值为0时,当前线程调用 wait_event_freezable 函数,睡眠等待在其 wait 等待队列中,直到当前线程的 todo 队列有新的待处理工作项。

non_block 值为1时,表示当前线程使用非阻塞模式。如果当前线程或所属进程中没有待处理工作项,那么则不在 Binder 驱动中睡眠等待,而是马上返回用户空间。

[3] ioctl 系统调用返回,说明有新的 Binder 通信请求需要处理,通过调用 binder_parse 函数对 Binder 通信请求内容进行解析。binder_parse 函数定义如下:

int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uint32_t *ptr, uint32_t size, binder_handler func)

    int r = 1;
    uint32_t *end = ptr + (size / 4);

    while (ptr < end) 
        uint32_t cmd = *ptr++;
        switch(cmd) 
        ......
        case BR_TRANSACTION: 
            struct binder_txn *txn = (void *) ptr;
            if ((end - ptr) * sizeof(uint32_t) < sizeof(struct binder_txn)) 
                ALOGE("parse: txn too small!\\n");
                return -1;
            
            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);
                bio_init_from_txn(&msg, txn);
                res = func(bs, txn, &msg, &reply);
                binder_send_reply(bs, &reply, txn->data, res);
            
            ptr += sizeof(*txn) / sizeof(uint32_t);
            break;
        
        ......
    

    return r;

解析过程与封装过程相对应,因此所使用的数据结构也与封装使用的数据结构(请参考Binder之数据结构(二))对应。结构体 binder_txn 对应于 binder_transaction_data;结构体 binder_io 对应于 Parcel 类;结构体 binder_object 对应于 flat_binder_object,如下图所示:

如果 Binder 驱动通知 servicemanager 处理 Client 进程的服务请求,那么使用的协议为 BR_TRANSACTION,于是调用 func 为 Client 进程提供服务。func 是一个函数指针,指向的实现函数为 svcmgr_handler,如下所示:

int svcmgr_handler(struct binder_state *bs,
                   struct binder_txn *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)

    struct svcinfo *si;
    uint16_t *s;
    unsigned len;
    void *ptr;
    uint32_t strict_policy;
    int allow_isolated;

    ......

    switch(txn->code) 
    case SVC_MGR_GET_SERVICE:
    case SVC_MGR_CHECK_SERVICE:
        s = bio_get_string16(msg, &len);
        ptr = do_find_service(bs, s, len, txn->sender_euid);                    [3.1]
        if (!ptr)
            break;
        bio_put_ref(reply, ptr);
        return 0;

    case SVC_MGR_ADD_SERVICE:
        s = bio_get_string16(msg, &len);
        ptr = bio_get_ref(msg);
        allow_isolated = bio_get_uint32(msg) ? 1 : 0;
        if (do_add_service(bs, s, len, ptr, txn->sender_euid, allow_isolated)) [3.2]
            return -1;
        break;

    case SVC_MGR_LIST_SERVICES: 
        unsigned n = bio_get_uint32(msg);

        si = svclist;
        while ((n-- > 0) && si)
            si = si->next;
        if (si) 
            bio_put_string16(reply, si->name);
            return 0;
        
        return -1;
    
    default:
        ALOGE("unknown code %d\\n", txn->code);
        return -1;
    

    bio_put_uint32(reply, 0);
    return 0;

[3.1] 如果 Client 端调用 getService 函数,则命令协议为 SVC_MGR_GET_SERVICE 或者 SVC_MGR_CHECK_SERVICE,使用 do_find_service 函数为 Client 组件提供获取 Service 组件代理对象的服务。
[3.2] 如果 Client 端调用 addService 函数,则命令协议为 SVC_MGR_ADD_SERVICE,使用 do_add_service 函数为 Service 组件提供 Service 组件注册服务。

最后,一图胜千言,servicemanager 启动流程图如下所示:

参考资料:
1. Android 系统源代码情景分析的第5章 - Binder 进程间通信系统

以上是关于Android笔记 - Binder之守护进程servicemanager的主要内容,如果未能解决你的问题,请参考以下文章

Android 进阶——Framework 核心之Binder对象管理者 Service Manager 守护进程及其自身代理对象详解

Android:安卓学习笔记之Binder 机制的简单理解和使用

Android:安卓学习笔记之Binder 机制的简单理解和使用

Android笔记 - Binder之基本概念

Android笔记 - Binder之数据结构

AndroidFramework 之启动 ServiceManager