Android Binder学习(二)之ServiceManager进程-我想成为管理员

  ServiceMange在binder通信中充当一个管理员的角色。当系统启动时,各种server进程将自己的service对象和service名字注册到ServiceMange中。后面client进程想使用相应的服务时,就可以根据对应服务的名字,在sm中查找到对应服务,找到之后就会在kernel中创建一个binder引用对象,并将引用对象的描述符返回给client,以让它创建一个服务代理对象。这样有了代理对象之后,客户端就像访问自己的资源一样,使用服务了。

一、ServiceMange成为管理员的心路历程

在接下来的分析过程中,我就着代码调用的顺序,来记录一下ServiceMange的坎坷之路

1. serviceManage入口函数main
代码路径:frameworks/native/cmds/servicemanager/service_manager.c
int main(int argc, char **argv)
{
    struct binder_state *bs;

    bs = binder_open(128*1024);
    if (!bs) {
        ALOGE("failed to open binder driver\n");
        return -1;
    }

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

   /*这中间夹杂了一些和selinux相关的代码
    *这里我们就先不管它了*/

    svcmgr_handle = BINDER_SERVICE_MANAGER;
    binder_loop(bs, svcmgr_handler);

    return 0;
}

  上面main函数除掉一些和selinux相关的代码之后,整个代码看起来短小精悍。但是整个链路走下来还是有很多东西的。上面代码首先打开binder设备,然后下发一个”它想成为管理员的命令”,如果成为管理后,它就进入消息循环状态,不断的处理来自客户端的请求。

2.binder_open()
struct binder_state *binder_open(size_t mapsize)
{
    struct binder_state *bs;
    struct binder_version vers;

    bs = malloc(sizeof(*bs));
    if (!bs) {
        errno = ENOMEM;
        return NULL;
    }

    bs->fd = open("/dev/binder", O_RDWR);
    if (bs->fd < 0) {
        fprintf(stderr,"binder: cannot open device (%s)\n",
                strerror(errno));
        goto fail_open;
    }
    /*下面会通过ioctl系统调用,从kernel获取当前binder驱动的版本,如果
     *binder驱动上层和kernel使用的不是同一个版本,那么自然binder设备打开
     *就失败了,所以在移植android时要注意这个地方*/
    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
        (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
        fprintf(stderr, "binder: driver version differs from user space\n");
        goto fail_open;
    }

    bs->mapsize = mapsize;
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    if (bs->mapped == MAP_FAILED) {
        fprintf(stderr,"binder: cannot map device (%s)\n",
                strerror(errno));
        goto fail_map;
    }

    return bs;

fail_map:
    close(bs->fd);
fail_open:
    free(bs);
    return NULL;
}
/*binder_state状态结构体*/
struct binder_state
{
    int fd;           /*binder驱动的文件描述符*/
    void *mapped;     /*map后内存的首地址*/
    size_t mapsize;   /*想map映射的地址大小*/
};

  首先能看到为binder_state对象分配内存。然后就open binder内核驱动文件。下面我们就来看一下kernel在打开binder驱动时都做了些什么。open函数是标准的系统调用,在每一个kernel driver中都有实现这个接口.

/*源码路径:kernel/drivers/staging/android/binder.c*/

static int binder_open(struct inode *nodp, struct file *filp)
{
    struct binder_proc *proc;

    binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
             current->group_leader->pid, current->pid);

    proc = kzalloc(sizeof(*proc), GFP_KERNEL); 
    if (proc == NULL)
        return -ENOMEM;
    get_task_struct(current); /*获取当前进程的任务控制块*/
    proc->tsk = current; /*记录当前任务控制块*/
    INIT_LIST_HEAD(&proc->todo); /*初始化事务链表*/
    init_waitqueue_head(&proc->wait);
    proc->default_priority = task_nice(current); /*获取当前进程的优先级*/

    binder_lock(__func__);

    binder_stats_created(BINDER_STAT_PROC); /*记录统计信息*/
    hlist_add_head(&proc->proc_node, &binder_procs); /*将当前的binder_proc对象加入到全局的binder_proc链表中*/
    proc->pid = current->group_leader->pid; /*记录当前进程的进程ID*/
    INIT_LIST_HEAD(&proc->delivered_death); /*初始化死亡通知链表*/
    filp->private_data = proc; /*记录私有数据,便于在其它函数中访问该binder_proc对象*/

    binder_unlock(__func__);

    if (binder_debugfs_dir_entry_proc) { /*为了方便debug,这里会在/proc目录下创建一些属性文件*/
        char strbuf[11];
        snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
        proc->debugfs_entry = debugfs_create_file(strbuf, S_IRUGO,
            binder_debugfs_dir_entry_proc, proc, &binder_proc_fops);
    }

    return 0;
}

  函数刚进来,就会新创建一个binder_proc对象,初始化结构当中相应的变量,例如记录任务控制块结构、保存进程PID、生成统计信息。最后最重要的是该binder_proc对象会保存到全局的binder_procs链表对象当中。它的定义如下所示:
static HLIST_HEAD(binder_procs);
当前binder_procs全局链表下面只有一个Service_Manager进程的binder_proc对象,他们的连接关系如下图所示:

3.mmap()内存映射
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
    int ret;
    struct vm_struct *area;
    struct binder_proc *proc = filp->private_data;
    const char *failure_string;
    struct binder_buffer *buffer;

    if (proc->tsk != current)
        return -EINVAL;


         /*从这里可以看到用户空间一次申请的大小不能大于4M,之前我们知道serviemanager申请了128K*/
    if ((vma->vm_end - vma->vm_start) > SZ_4M) 
        vma->vm_end = vma->vm_start + SZ_4M;

    binder_debug(BINDER_DEBUG_OPEN_CLOSE,
             "binder_mmap: %d %lx-%lx (%ld K) vma %lx pagep %lx\n",
             proc->pid, vma->vm_start, vma->vm_end,
             (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
             (unsigned long)pgprot_val(vma->vm_page_prot));

    if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) {
        ret = -EPERM;
        failure_string = "bad vm_flags";
        goto err_bad_arg;
    }
    vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;

    mutex_lock(&binder_mmap_lock);
    if (proc->buffer) {
        ret = -EBUSY;
        failure_string = "already mapped";
        goto err_already_mapped;
    }
        /*vma是用户空间的传过来的虚拟地址,这是是申请kernel的虚拟地址空间*/
    area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
    if (area == NULL) {
        ret = -ENOMEM;
        failure_string = "get_vm_area";
        goto err_get_vm_area_failed;
    }
    proc->buffer = area->addr;/*因为binder_proc就在kernel地址空间,所以它使用kernel虚拟地址,这里保留首地址*/
        /*下面保留内核虚拟地址和用户空间虚拟地址的差值,应为它们相差一个固定的值,知道其中一个,另外一个就知道了*/
        proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer; 
    mutex_unlock(&binder_mmap_lock);

#ifdef CONFIG_CPU_CACHE_VIPT
    if (cache_is_vipt_aliasing()) {
        while (CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) {
            pr_info("binder_mmap: %d %lx-%lx maps %p bad alignment\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer);
            vma->vm_start += PAGE_SIZE;
        }
    }
#endif  /*下面申请存放物理页面指针用于指向,真正的物理页面*/
    proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
    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; /*末尾-开始,就是buffer大小,这里是128K*/

    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)) {
        ret = -ENOMEM;
        failure_string = "alloc small buf";
        goto err_alloc_small_buf_failed;
    }
    buffer = proc->buffer; /*这个时候buffer指向了一个大小为128K的kernel虚拟地址空间*/
    INIT_LIST_HEAD(&proc->buffers); /*初始化binder_proc的binder_buffer链表*/

        /*下面将当前binder_proc拥有的binder_buffer链接到binder_proc当中,注意这个时候还只有一个binder_buffer*/
        list_add(&buffer->entry, &proc->buffers); 
    buffer->free = 1; /*表明当前buffer是空闲buffer*/
    binder_insert_free_buffer(proc, buffer); /*将当前的binder_buffer插入到binder_proc空闲链表中*/
    proc->free_async_space = proc->buffer_size / 2;
    barrier();
    proc->files = get_files_struct(current);
    proc->vma = vma; /*保留这128K在用户空间地址空间*/
    proc->vma_vm_mm = vma->vm_mm; /*保留这128在kernel的地址空间*/

    /*pr_info("binder_mmap: %d %lx-%lx maps %p\n",
         proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/
    return 0;
        /*为了代码简短,这里删掉后面的错误内存回收处理,具体大家可以动手看看源码*/
 }

mmap函数主要做了下面几件事情

  • 1.申请128K的kernel虚拟地址空间,并保存在binder_proc->buffer中
  • 2.计算这128K内存,用户的虚拟地址空间和kernel虚拟地址空间的偏移量
  • 3.申请物理页面记录指针
  • 4.为这128K申请实际的物理页面,并将128K映射到kernel和用户空间的128K地址空间中。这里是128k大小,一个页是4K,要分陪32个物理页面。

下面我们仔细看看物理页面如何分配以及虚拟地址如何映射到对应的物理地址的,下面来欣赏一下binder_update_page_range()函数的经典部分.

static int binder_update_page_range(struct binder_proc *proc, int allocate,
                    void *start, void *end,
                    struct vm_area_struct *vma)
{
    void *page_addr;
    unsigned long user_page_addr;
    struct vm_struct tmp_area;
    struct page **page;
    struct mm_struct *mm;
        /*这中间大都是一些保护,正常情况下是走不到的,我们就直接步入正题*/
        .........
        for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
        int ret;
        struct page **page_array_ptr;
        page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];

        BUG_ON(*page);
        *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO); /*真正的分配物理页面,并记录在binder_proc->page中*/
        if (*page == NULL) {
            pr_err("%d: binder_alloc_buf failed for page at %p\n",
                proc->pid, page_addr);
            goto err_alloc_page_failed;
        }
        tmp_area.addr = page_addr; /*128K 在kernel虚拟地址的首地址*/
        tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */; /*不是只有一个物理页面吗,怎么有2个页大小,google注释是保护内存*/
        page_array_ptr = page; /*实际物理页面*/
        ret = map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr); /*kernel一页虚拟地址映射实际的物理空间*/
        if (ret) {
            pr_err("%d: binder_alloc_buf failed to map page at %p in kernel\n",
                   proc->pid, page_addr);
            goto err_map_kernel_failed;
        }
        user_page_addr =
            (uintptr_t)page_addr + proc->user_buffer_offset;/*还记得之前保存的偏移量吗,这里就可以直接得到用户空间的地址了*/
        ret = vm_insert_page(vma, user_page_addr, page[0]); /*将该页也映射到用户地址空间*/
        if (ret) {
            pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n",
                   proc->pid, user_page_addr);
            goto err_vm_insert_page_failed;
        }
        /* vm_insert_page does not seem to increment the refcount */
    } ............
}

看到这里我们大概就知道了内存的映射过程了,其实上面主要做了下面几件事情

  • 1.申请物理页面,并将该物理页面映射到kernel虚拟地址空间。
  • 2.将该物理页面映射到用户空间
  • 3.上面的操作循环32次,128K的的内存就申请完毕。其中申请的32个物理页面应该是离散的。

最终ServiceManager申请的128K内存映射结果大概就如同下图这样。用户可以直接使用用户空间的地址,来使用这块内存。

4.binder_become_context_manager()
int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
//只有一个系统调用ioctl,调到kernel中,我们重点关注一下kernel中做了什么。
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
 /*......*/
case BINDER_SET_CONTEXT_MGR:
        if (binder_context_mgr_node != NULL) {
            pr_err("BINDER_SET_CONTEXT_MGR already set\n");
            ret = -EBUSY;
            goto err;
        }
        ret = security_binder_set_context_mgr(proc->tsk); /*该函数5.1目前还没有实现,直接返回0*/
        if (ret < 0)
            goto err;
        if (uid_valid(binder_context_mgr_uid)) {
            if (!uid_eq(binder_context_mgr_uid, current->cred->euid)) {
                pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n",
                       from_kuid(&init_user_ns, current->cred->euid),
                       from_kuid(&init_user_ns, binder_context_mgr_uid));
                ret = -EPERM;
                goto err;
            }
        } else
            binder_context_mgr_uid = current->cred->euid; /*保留service_manager进程的*/
        binder_context_mgr_node = binder_new_node(proc, 0, 0); /*这里根据sm进程的binder_proc创建binder_node实体*/
        if (binder_context_mgr_node == NULL) {
            ret = -ENOMEM;
            goto err;
        }
        binder_context_mgr_node->local_weak_refs++; /*弱引用+1*/
        binder_context_mgr_node->local_strong_refs++; /*强引用+1*/
        binder_context_mgr_node->has_strong_ref = 1;
        binder_context_mgr_node->has_weak_ref = 1;
        break;
/*.......*/
}

  这个函数主要作用就是针对service_manager进程创建一个全局的kernel binder实体,并保留在binder_context_mgr_node全局变量中,其中最主要的就是inder_context_mgr_node = binder_new_node(proc, 0, 0);

5.binder_loop()
void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;
    uint32_t readbuf[32];

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

    readbuf[0] = BC_ENTER_LOOPER; /*这里只写入一个进入LOOP的命令*/
    binder_write(bs, readbuf, sizeof(uint32_t)); /*所以这里后面的命令大小是4个字节*/
    for{
          /*这里面的东西很多,到后面看*/
    }

}

int binder_write(struct binder_state *bs, void *data, size_t len)
{
    struct binder_write_read bwr;
    int res;

    bwr.write_size = len; /*binder_write_read这个结构,前面数据结构那篇博文已经介绍过*/
    bwr.write_consumed = 0;
    bwr.write_buffer = (uintptr_t) data; /*就是上面的BC_ENTER_LOOP命令*/
    bwr.read_size = 0;
    bwr.read_consumed = 0; 
    bwr.read_buffer = 0;
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); /*请看下面kernel代码*/
    if (res < 0) {
        fprintf(stderr,"binder_write: ioctl failed (%s)\n",
                strerror(errno));
    }
    return res;
} 

在进入for消息循环之前,会有一个写binder操作,现在我们只分析写操作(binder_write),for循环我们在下面单独分析。写操作主要做了下面几件事情。

  • 1.向read_buf[0]写入BC_ENTER_LOOPER命令,并通过binder_write()传递给kernel。通过这个命令将service_manager主线程设置为ENTER_LOOP状态,即后面的for循环处理都是有由主线程来处理(线程PID和进程PID一样的)。
  • 2.不断的由主线程读取kernel中的binder消息(注意上面的代码中可以看到read_buf大小只有128个字节),
  • 3.调用binder_parse来解析命令,并使用svcmgr_handler处理一些事务方面的消息。具体在后面请求服务时,我们在仔细分析一下

我们来看看在kernel中如何处理这条命令的。

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    int ret;
    struct binder_proc *proc = filp->private_data;/*拿到sm的binder_proc对象*/
    struct binder_thread *thread;
    unsigned int size = _IOC_SIZE(cmd);
    void __user *ubuf = (void __user *)arg;
        /*省略部分与分析无关代码*/
    binder_lock(__func__);
    thread = binder_get_thread(proc); /*查找当前线程的主线程的binder_thread,没有的话就重新创建一个*/
    if (thread == NULL) {
        ret = -ENOMEM;
        goto err;
    }
    switch (cmd) {
    case BINDER_WRITE_READ: {
        struct binder_write_read bwr;
        if (size != sizeof(struct binder_write_read)) { /*传下来的就是binder_write_read结构,这里只是做了个保护。*/
            ret = -EINVAL;
            goto err;
        }
        if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
            ret = -EFAULT;
            goto err;
        }
        if (bwr.write_size > 0) {/*下面的binder_thread_write进去看看*/
            ret = binder_thread_write(proc, thread, bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
            trace_binder_write_done(ret);
        /*省略一些返回值错误检查机制*/
        }
        if (bwr.read_size > 0) { /*read_size = 0 这里不走*/
            ret = binder_thread_read(proc, thread, bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
            trace_binder_read_done(ret);
            if (!list_empty(&proc->todo))
                wake_up_interruptible(&proc->wait);
                /*省略一些返回值错误检查机制*/
        }
                /*省略一些debug信息*/
        if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { /*处理之后的binder_write_read结构题数据返回给user层*/
            ret = -EFAULT;
            goto err;
        }
        break;
    }
        .......
}

int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
            binder_uintptr_t binder_buffer, size_t size,
            binder_size_t *consumed)
{
    uint32_t cmd;
    void __user *buffer = (void __user *)(uintptr_t)binder_buffer; /*首先拿到用户的buffer*/
    void __user *ptr = buffer + *consumed; /**consumed = 0,sonsumed意思就是kernel已经读了或写了多少数据,这里得到buffer开始的4个字节的数据*/
    void __user *end = buffer + size;
        .......

        /*这里省略了很多case项,针对与service_manage的BC_ENTER_LOOPER命令,它的操作相对就简单了*/

        case BC_ENTER_LOOPER:
            binder_debug(BINDER_DEBUG_THREADS,
                     "%d:%d BC_ENTER_LOOPER\n",
                     proc->pid, thread->pid);
             /*下面由于之前service_manager主线程还没有创建,而且在后面创建时looper=BINDER_LOOPER_STATE_NEED_RETURN,应次下面为假*/
            if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {
                thread->looper |= BINDER_LOOPER_STATE_INVALID;
                binder_user_error("%d:%d ERROR: BC_ENTER_LOOPER called after BC_REGISTER_LOOPER\n",
                    proc->pid, thread->pid);
            }
            thread->looper |= BINDER_LOOPER_STATE_ENTERED; /*这里直接将主线程的binder_thread loop标志设置为已经进入LOOP循环了,*/
            break;
        *consumed = ptr - buffer; /*kernel已经读取多少数据,返回给user层*/
       return 0;
}

  针对BC_ENTER_LOOP命令,kernel做的事情很简单,就是创建主线程binder_thread,并将主线程的loop标志设置为BINDER_LOOPER_STATE_ENTERED。这里我们要强调的有一个结构体struct binder_write_read.

  • 1.当该结构体当中的write_size != 0时且buffer有效时,kernel会调用binder_thread_write()函数,去写相应的数据。
  • 2.当结构题成员read_size != 0时且buffer有效时,kernel会调用binder_thread_read(),读取消息队列中是否有要处理的数据。
6.真正的loop
    for (;;) {
        bwr.read_size = sizeof(readbuf); /*这里需要读取数据大小是128字节,而write_size = 0*/
        bwr.read_consumed = 0; /*这里首次读取的数据位置在0偏移处,下一次for循环过来,就不一定是0了*/
        bwr.read_buffer = (uintptr_t) readbuf; /*直接传递是一个user的指针值类型*/

        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); /*这个在后面分析*/
        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;
        }
    }

  这里的for循环加上kernel相应读操作,有很多细节需要我们分析,所以需要拆解分析,首先我们先来分析kernel读binder消息的实现。

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)
{
    void __user *buffer = (void __user *)(uintptr_t)binder_buffer; /*别忘了上层传下来的buffer有128字节*/
    void __user *ptr = buffer + *consumed;
    void __user *end = buffer + size;

    int ret = 0;
    int wait_for_proc_work;

    if (*consumed == 0) {
        if (put_user(BR_NOOP, (uint32_t __user *)ptr)) /*将BR_NOOP命令写入ptr指向buffer的前4个字节*/
            return -EFAULT;
        ptr += sizeof(uint32_t); /*位移指针+4*/
    }

retry:  /*下面如果事务栈为空,而且待处理事务链表也为空的话,主线程就真的没事做了*/
    wait_for_proc_work = thread->transaction_stack == NULL &&
                list_empty(&thread->todo);
         /*这里省略一些错误检查代码*/
    thread->looper |= BINDER_LOOPER_STATE_WAITING; /*将主线程的标志设置为等待状态*/
    if (wait_for_proc_work)
        proc->ready_threads++; /*空闲线程数目也+1了*/

    binder_unlock(__func__);

    trace_binder_wait_for_work(wait_for_proc_work,
                   !!thread->transaction_stack,
                   !list_empty(&thread->todo));
    if (wait_for_proc_work) { /*当前主线程是空闲的,表面个当前进程就在空闲着,没其它事可做*/

                 /*上面已经将looper追加了BINDER_LOOPER_STATE_WAITING,但别忘了一开始的状态是BINDER_LOOPER_STATE_ENTERED,所以这里为假*/

            if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
                    BINDER_LOOPER_STATE_ENTERED))) {
                binder_user_error("%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 /*下面当前整个线程都是空闲的,所以这里就睡眠servicemanager进程,程序不在执行下去*/
                ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
        } else {
        if (non_block) {
            if (!binder_has_thread_work(thread)) /*这里是判断线程是否有没有处理的事务*/
                ret = -EAGAIN;
        } else
            ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread)); /*线程休眠,停止到这里,不执行下去了*/
    }
 ......
    while (1) {
        uint32_t cmd;
        struct binder_transaction_data tr;
        struct binder_work *w;
        struct binder_transaction *t = NULL;
        ......
        /*下面case介绍了各种工作项,情况较复杂,后面我在逐一来分析,这里就先分析到这里*/
        switch (w->type) {
        case BINDER_WORK_TRANSACTION: break;
        case BINDER_WORK_TRANSACTION_COMPLETE: break;
        case BINDER_WORK_NODE: break;
        case BINDER_WORK_DEAD_BINDER: break;
        case BINDER_WORK_DEAD_BINDER_AND_CLEAR: break;
        case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: break;
        ......
 }

  如果你看过kernel binder驱动你就知道上面包含了很多的处理逻辑。等后面我们继续分析各个工作项的时候,在逐一分析。这里由于servicemange进程刚起来,还没有任何服务注册。所以servicemanage进程就休眠了,直到有其它server进程请求服务时,才会唤醒servicemange进程。下面我们来小结一下上面的binder_thread_read所做的工作。

  • 1.取出从用户空间传下来的命令buffer,由于底层还没还是往buffer里面写东西,所以会将BR_NOOP写入buffer缓冲区,并移动记录指针ptr。
  • 2.判断线程的事务堆栈和线程的工作项队列是否都为空,都为空就表示该线程真的有点闲。就会将binder_proc->ready_threads++;
  • 3.程序判断主线程是否真的很闲,如果为真,就去判断进程是阻塞式等待还是非阻塞式等待,这里是阻塞式等待,进程就在下面这行代码的位置暂时睡去了。
    wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
    到这里servicemange升官之旅,我们就先探索到这里。

二、总结

  来到这里servicemanage的启动过程,就先告一段落了。下面在简单总结一下servicemanage启动过程。

  • 1.servicemange进程是由init进程启动,通过解析init.rc文件找到了servicemanage可执行文件。如下的代码可见,servicemanage是在system/bin目录下。
android/system/core/rootdir/init.rc 
514 service servicemanager /system/bin/servicemanager
515     class core
516     user system
517     group system
518     critical
519     onrestart restart healthd
520     onrestart restart zygote
521     onrestart restart media
522     onrestart restart surfaceflinger
523     onrestart restart drm
  • 2.servicemange可执行程序执行之后,会去打开binder设备,紧接着创建serviceManage进程的对应的binder_proc对象。然后申请128K的buffer,并将该128K的buffer映射到用户空间。
  • 3.servicemange进程注册自己为服务山下文管理员,并自己的binder_proc对象链接到保存在kernel中全局的binder_procs变量开始处,而且生成自己的一个binder_node对象,并将该对象的指针赋给全局的binder_node对象binder_context_mgr_node,与此同时我们需要注意该binder_node的ptr和cookie都是为空的,这在后面查找servicemange binder_node时可以用到。
  • 4.servicemange进入消息循环状态,如果是第一次启动servicemange进程,那么servicemanage进程会在binder kernel驱动中睡眠,直到有client进程请求服务时才被唤醒。

下图是serviceManage简单的启动流程。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值