Binder系统:servicemanager

本文转载自:http://blog.csdn.net/yanshazi/article/details/50447631

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

[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;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

以上是 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]
    ......
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

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

struct binder_state 
{
    int fd;
    void *mapped;
    unsigned mapsize;
};
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

[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]

    ......
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

[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);
}
 
 
  • 1
  • 2
  • 3
  • 4

最终由 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;
    ......
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

首先,进行一些合法性检查,确保之前没有组件注册成为上下文管理者。然后,调用 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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

[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);
 
 
  • 1
  • 2
  • 3

[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);
 
 
  • 1
  • 2

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]
    }
    ......
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

[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;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

解析过程与封装过程相对应,因此所使用的数据结构也与封装使用的数据结构(请参考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;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

[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 进程间通信系统

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值