Service Manager是整个Binder机制的守护进程,用来管理开发者创建的各种Server,并且向Client提供查询Server远程接口的功能。Service Manager作为本地服务由Init进程启动,在Android Init进程源码分析中详细分析了Init进程是如何启动本地服务的,在Init.rc配置文件中有这么一段配置:

service servicemanager /system/bin/servicemanagerclass coreuser systemgroup systemcriticalonrestart restart zygoteonrestart restart mediaonrestart restart surfaceflingeronrestart restart drm

Android Init进程源码分析详细介绍了rc脚本文件的语法及Init进程的解析过程,上面这段配置是表示Init进程将启动servicemanager服务进程,该服务进程是系统关键进程,当该服务被杀重启时,必须重启zygote,media,surfaceflinger,drm进程,对于这段配置的解析请查看 Android Init进程源码分析一文。

Service Manager在用户空间的源代码位于frameworks/base/cmds/servicemanager目录下,主要是由binder.h、binder.c和service_manager.c三个文件组成。Service Manager的入口位于service_manager.c文件中的main函数:

int main(int argc, char **argv)
{struct binder_state *bs;//#define BINDER_SERVICE_MANAGER ((void*) 0)void *svcmgr = BINDER_SERVICE_MANAGER;//打开binder设备驱动文件,并将Binder设备文件映射到servicemanger进程的地址空间中bs = binder_open(128*1024);//在binder驱动层设置服务管理者角色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;
}

servicemanager进程的任务分为以下三个步骤:

1)打开binder设备文件并映射到servicemanger进程地址空间中来;

2)在binder驱动层将servicemanger设置为Binder上下午管理者;

3)睡眠等待客户端的请求;

打开binder设备文件并映射到进程地址空间

binder驱动初始化时会在/dev目录下创建一个binder设备文件,所有使用Binder通信机制的进程都是通过该设备文件来访问Binder驱动的,进程之间通信的IPC数据就是通过Binder驱动在内核空间进行交互的,首先来分析一下binder设备文件的打开过程:

struct binder_state *binder_open(unsigned mapsize)
{//打开binder设备驱动文件,并返回一个binder_state结构体变量struct binder_state *bs;bs = malloc(sizeof(*bs));if (!bs) {errno = ENOMEM;return 0;}//打开/dev/binder设备文件,并将设备文件句柄保存在binder_state的成员变量fd中bs->fd = open("/dev/binder", O_RDWR);if (bs->fd < 0) {fprintf(stderr,"binder: cannot open device (%s)\n",strerror(errno));goto fail_open;}//将Binder设备文件映射到servicemanger进程的地址空间中,映射空间大小为128k//将映射地址空间大小保存在binder_state的成员变量mapsize中bs->mapsize = mapsize;//将映射地址空间的起始地址保存在binder_state的成员变量mapped中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 0;
}

该函数首先使用open系统调用函数打开/dev/binder设备文件,调用open函数打开binder设备文件时,在Linux驱动设计框架下,系统会自动调用binder驱动层的binder_open()函数,原因为何已经这两个函数之间有什么关联,在 Android IPC数据在内核空间中的发送过程分析 中简要的介绍了,如需详细了解Linux驱动设计框架,请参阅Linux设备驱动相关的书籍。mmap()函数分配IPC数据的Buffer,servicemanger使用大小为128K的Buffer来接收IPC数据。binder_state结构体保存了打开的binder设备文件句柄及内核缓冲区映射到的ServiceManager进程地址空间的起始地址及大小等信息。

1.打开binder设备文件

binder_open()函数将为打开binder驱动的进程生成并初始化binder_proc结构体,该结构体用于管理Binder IPC所需要的各种信息,如打开binder驱动的进程信息、接收IPC数据的Buffer信息、IPC状态信息。并且初始化待机队列,用来将进程切换到待机状态。

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);//创建一个binder_proc结构体用于记录打开binder驱动的进程信息proc = kzalloc(sizeof(*proc), GFP_KERNEL);if (proc == NULL)return -ENOMEM;//获取当前进程的task信息,在Linux中current就是指的当前进程get_task_struct(current);//将当前进程task信息保存在binder_proc的tsk成员中proc->tsk = current;//初始化todo队列,binder驱动在接收到IPC数据后,会将要执行的任务保存到todo队列中INIT_LIST_HEAD(&proc->todo);//初始化待机队列以便将打开binder驱动的进程切换到待机状态init_waitqueue_head(&proc->wait);//将当前进程的优先级保存到binder_proc的default_priority成员中proc->default_priority = task_nice(current);mutex_lock(&binder_lock);binder_stats_created(BINDER_STAT_PROC);//将当前创建的binder_proc挂载到全局链表binder_procs中,这样就可以通过binder_procs查看所有打开binder驱动的进程hlist_add_head(&proc->proc_node, &binder_procs);//设置binder_proc的pid信息proc->pid = current->group_leader->pid;//初始化delivered_death链表INIT_LIST_HEAD(&proc->delivered_death);//将当前创建的binder_proc注册到file结构体的private_data成员变量中,以便在binder驱动的其他操作中可以直接取出binder_proc结构体filp->private_data = proc;mutex_unlock(&binder_lock);//在/proc/binder/proc目录下生成文件显示Binder IPC相关信息if (binder_debugfs_dir_entry_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;
}

2.分配内核缓存区并建立地址空间映射关系

在Android IPC数据在内核空间中的发送过程分析中介绍了Linux进程的用户空间是独立的,但所有进程共享内核空间,因此进程间通信可以通过内核空间来交换数据。为了在内核空间交换IPC数据,因此采用Binder机制通信的进程必须在内核空间中开辟一块共享空间。前面介绍了打开binder设备文件节点,并返回设备文件的句柄,/dev/binder是用户空间访问内核空间的桥梁,当把binder设备文件映射到内核的某一段地址空间时,用户程序就可用通过操作/dev/binder设备来直接访问内核空间。进程调用mmap函数在内核空间中开辟一块用于接收IPC数据的buffer,并且将该内核缓存区映射到ServiceManager进程的虚拟地址空间中,当客户进程将IPC数据写入到该内核缓存区时,ServiceManager进程在用户空间也可以读取发送过来的IPC数据,节省了内核空间到用户空间的一次拷贝过程,提高了通信效率。

static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{//struct vm_area_struct表示用户空间进程虚拟地址,地址空间范围是0~3G//struct vm_struct表示内核空间虚拟地址,地址空间范围是(3G + 896M + 8M) ~ 4G//它们对应的物理页面都可以是不连续的。int ret;//定义描述即将分配的内核缓存区的变量struct vm_struct *area;//通过filp->private_data得到在打开设备文件/dev/binder时创建的struct binder_proc结构struct binder_proc *proc = filp->private_data;const char *failure_string;struct binder_buffer *buffer;/× ========进程虚拟地址空间的设置=========== ×///判断进程虚拟地址大小是否大于4M,如果大于则设置为4M大小if ((vma->vm_end - vma->vm_start) > SZ_4M)vma->vm_end = vma->vm_start + SZ_4M;//判断进程虚拟地址空间是否被设置为禁止映射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;/× ========内核虚拟地址空间的分配========= ×///判断内核缓存区在内核虚拟地址空间的起始值是否等于NULLif (proc->buffer) {ret = -EBUSY;failure_string = "already mapped";goto err_already_mapped;}//在内核虚拟地址空间中申请分配指定大小的内存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;//计算进程虚拟地址起始值与内核虚拟地址的起始值之间的偏移proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;//分配描述所有物理页面的数组,一个物理页面用page来表示,缓存区所需物理页面个数为((vma->vm_end - vma->vm_start) / PAGE_SIZE),所以//描述所有物理页面信息需要sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE)大小的存储空间proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);//判断描述物理页面的数组起始地址是否等于NULLif (proc->pages == NULL) {ret = -ENOMEM;failure_string = "alloc page array";goto err_alloc_pages_failed;}//计算分配的缓存区大小proc->buffer_size = vma->vm_end - vma->vm_start;//注册进程虚拟地址空间的操作函数vma->vm_ops = &binder_vm_ops;//将当前进程的binder_proc注册到虚拟地址空间描述符的vm_private_data中vma->vm_private_data = proc;/× =====分配实际的物理页面并同时映射到进行虚拟地址空间和内核虚拟地址空间中====== ×///为虚拟地址空间proc->buffer ~ proc->buffer + PAGE_SIZE分配一个空闲的物理页面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;}//这段地址空间使用一个binder_buffer来描述,分别插入到proc->buffers链表和proc->free_buffers红黑树中去buffer = proc->buffer;INIT_LIST_HEAD(&proc->buffers);list_add(&buffer->entry, &proc->buffers);buffer->free = 1;binder_insert_free_buffer(proc, buffer);proc->free_async_space = proc->buffer_size / 2;barrier();proc->files = get_files_struct(current);proc->vma = vma;return 0;err_alloc_small_buf_failed:kfree(proc->pages);proc->pages = NULL;
err_alloc_pages_failed:vfree(proc->buffer);proc->buffer = NULL;
err_get_vm_area_failed:
err_already_mapped:
err_bad_arg:return ret;
}

进程虚拟地址空间和内核虚拟地址空间映射图:

通过将分配的物理页面同时映射到内核虚拟地址空间及SeviceManager进程虚拟地址空间,客户进程将IPC数据发送给ServiceManager进程时,只需要将IPC数据写入到为ServiceManager进程开辟的内核缓存区,ServiceManager进程就可以在用户空间访问发送过来的IPC数据了。通常进程间通信是将进程A的数据复制到内核缓存区,然后从内核缓存区复制到进程B,使用地址空间映射方式的Binder通信机制只需要进程A复制IPC数据到内核缓存区,进程B就可以直接访问到该数据,整个过程节省了一次数据复制过程,如下图所示:

pages成员变量是一个struct page*类型的数组,struct page是用来描述物理页面的数据结构;Binder驱动程序将buffer ~ (buffer + buffer_size)这段地址空间划分为一段一段来管理,每一段是结构体struct binder_buffer来描述。每一个binder_buffer通过其成员entry按从低址到高地址连入到struct binder_proc中的buffers表示的链表中去,同时,每一个binder_buffer又分为正在使用的和空闲的,通过free成员变量来区分,空闲的binder_buffer通过成员变量rb_node连入到struct binder_proc中的free_buffers表示的红黑树中去,正在使用的binder_buffer通过成员变量rb_node连入到struct binder_proc中的allocated_buffers表示的红黑树中去。binder_mmap首先检查要映射的内存大小不能超过SIZE_4M,即4M,然后调用get_vm_area函数获得一个空闲的内核vm_struct区间,并初始化proc结构体的buffer、user_buffer_offset、pages和buffer_size和成员变量,接着调用binder_update_page_range来为虚拟地址空间proc->buffer ~ proc->buffer + PAGE_SIZE分配一个空闲的物理页面,同时这段地址空间使用一个binder_buffer来描述,分别插入到proc->buffers链表和proc->free_buffers红黑树中去。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)
{//参数allocate用于区分是分配物理页面还是释放物理页面,1为分配物理页面,0为释放物理页面void *page_addr;unsigned long user_page_addr;struct vm_struct tmp_area;struct page **page;struct mm_struct *mm;if (end <= start)return 0;if (vma)mm = NULL;elsemm = get_task_mm(proc->tsk);if (mm) {down_write(&mm->mmap_sem);vma = proc->vma;}//如果allocate等于0,释放物理页面if (allocate == 0)goto free_range;if (vma == NULL) {printk(KERN_ERR "binder: %d: binder_alloc_buf failed to ""map pages in userspace, no vma\n", proc->pid);goto err_no_vma;}//循环分配PAGE_SIZE大小的物理页面for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {int ret;struct page **page_array_ptr;//计算当前分配的物理页在物理页面数组pages中的索引号(page_addr - proc->buffer) / PAGE_SIZEpage = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];BUG_ON(*page);//分配物理页面*page = alloc_page(GFP_KERNEL | __GFP_ZERO);if (*page == NULL) {printk(KERN_ERR "binder: %d: binder_alloc_buf failed ""for page at %p\n", proc->pid, page_addr);goto err_alloc_page_failed;}//映射物理页面到内核虚拟地址空间中tmp_area.addr = page_addr;tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */;page_array_ptr = page;ret = map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr);if (ret) {printk(KERN_ERR "binder: %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) {printk(KERN_ERR "binder: %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 */}if (mm) {up_write(&mm->mmap_sem);mmput(mm);}return 0;free_range:for (page_addr = end - PAGE_SIZE; page_addr >= start;page_addr -= PAGE_SIZE) {page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];if (vma)zap_page_range(vma, (uintptr_t)page_addr + proc->user_buffer_offset, PAGE_SIZE, NULL);
err_vm_insert_page_failed:unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
err_map_kernel_failed:__free_page(*page);*page = NULL;
err_alloc_page_failed:;}
err_no_vma:if (mm) {up_write(&mm->mmap_sem);mmput(mm);}return -ENOMEM;
}

函数调用alloc_page分配一块实际物理页面,然后分别调用map_vm_area函数映射到内核虚拟地址空间中,调用vm_insert_page函数映射到进程虚拟地址空间中。

设置ServiceManager为服务大管家

前面介绍了Binder设备文件的打开,并开辟内核缓存区同时映射到内核虚拟地址空间及进程虚拟地址空间中,这些工作仅仅是为进程间通信作准备。接下来需要告诉Binder驱动程序ServiceManager是系统服务的大管家,通过ioctl命令控制函数进入到binder驱动程序中,并为ServiceManager进程创建binder实体对象。在Linux中ioctl函数是设备命令控制函数,在Binder驱动中依据Binder通信协议也实现了该函数,整个Binder通信过程就是在这个函数中完成的。下表是Binder驱动程序的控制命令。

ioctl命令

说明

BINDER_WRITE_READ

Binder数据读写

BINDER_SET_IDLE_TIMEOUT

设置空闲超时时间

BINDER_SET_MAX_THREADS

设置线程池的线程个数

BINDER_SET_IDLE_PRIORITY

设置线程优先级

BINDER_THREAD_EXIT

设置线程退出

BINDER_VERSION

设置版本号

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{int ret;//通过filp->private_data得到在打开设备文件/dev/binder时创建的struct binder_proc结构struct binder_proc *proc = filp->private_data;struct binder_thread *thread;unsigned int size = _IOC_SIZE(cmd);void __user *ubuf = (void __user *)arg;ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);if (ret)return ret;mutex_lock(&binder_lock);//从binder_proc中取出binder_thread线程描述符thread = binder_get_thread(proc);if (thread == NULL) {ret = -ENOMEM;goto err;}//处理不同的binder命令switch (cmd) {case BINDER_WRITE_READ: break;case BINDER_SET_MAX_THREADS:break;case BINDER_SET_CONTEXT_MGR:break;case BINDER_THREAD_EXIT:break;case BINDER_VERSION:break;default:ret = -EINVAL;goto err;}ret = 0;
err:if (thread)//设置binder线程状态thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;mutex_unlock(&binder_lock);wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);if (ret && ret != -ERESTARTSYS)printk(KERN_INFO "binder: %d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);return ret;
}

函数首先从filp->private_data中取出当前ServiceManager进程的binder_proc,然后调用binder_get_thread函数从当前进程描述符中查找是否已经存在了线程描述符binder_thread,处理不同Binder命令后设置Binder线程的运行状态。binder线程查询过程如下:

static struct binder_thread *binder_get_thread(struct binder_proc *proc)
{struct binder_thread *thread = NULL;struct rb_node *parent = NULL;struct rb_node **p = &proc->threads.rb_node;//从进程描述符binder_proc的threads红黑树中查找是否有线程描述符存在while (*p) {parent = *p;thread = rb_entry(parent, struct binder_thread, rb_node);if (current->pid < thread->pid)p = &(*p)->rb_left;else if (current->pid > thread->pid)p = &(*p)->rb_right;elsebreak;}//如果没有查找到if (*p == NULL) {//创建一个线程描述符binder_threadthread = kzalloc(sizeof(*thread), GFP_KERNEL);if (thread == NULL)return NULL;//初始化该线程描述符的各个成员变量binder_stats_created(BINDER_STAT_THREAD);thread->proc = proc;thread->pid = current->pid;init_waitqueue_head(&thread->wait);INIT_LIST_HEAD(&thread->todo);rb_link_node(&thread->rb_node, parent, p);rb_insert_color(&thread->rb_node, &proc->threads);thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN;thread->return_error = BR_OK;thread->return_error2 = BR_OK;}return thread;
}

由于ServiceManager进程是第一次进来,查找结果肯定为空,因此会创建一个binder_thread,并挂载到ServiceManager进程的binder_proc的threads红黑树中。

ServiceManager进程通过binder_become_context_manager函数来告诉Binder驱动程序该进程负责管理系统所有服务

int binder_become_context_manager(struct binder_state *bs)
{return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

函数直接调用ioctl设备控制函数,并且控制命令为BINDER_SET_CONTEXT_MGR,设备句柄为上面打开的binder设备文件句柄,因此将调用Binder驱动程序的binder_ioctl函数,上面已经介绍了该函数的实现,现在只介绍binder_ioctl函数对控制命令为
BINDER_SET_CONTEXT_MGR的实现过程。

//binder_context_mgr_node是一个全局变量,首先判断该变量是否为空以验证是否已经为ServiceManager进程创建了binder实体对象
if (binder_context_mgr_node != NULL) {printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");ret = -EBUSY;goto err;
}
//binder_context_mgr_uid也是一个全局变量,判断是否与当前ServiceManager进程的uid相同
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//设置ServiceManager进程的uid到binder_context_mgr_uid全局变量中binder_context_mgr_uid = current->cred->euid;//为当前ServiceManager进程创建一个binder实体对象,并保存到binder_context_mgr_node全局变量中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;
BINDER_SET_CONTEXT_MGR

BINDER_SET_CONTEXT_MGR命令的实现很简单,就是为ServiceManager进程创建一个Binder实体对象binder_node,并保存到全局变量binder_context_mgr_node中,同时保存ServiceManager进程的uid到全局变量binder_context_mgr_uid中。现在来看看是如何为ServiceManager进程创建Binder实体对象的。

static struct binder_node *binder_new_node(struct binder_proc *proc,void __user *ptr,void __user *cookie)
{struct rb_node **p = &proc->nodes.rb_node;struct rb_node *parent = NULL;struct binder_node *node;//从进程描述符binder_proc的nodes红黑树中查找是否存在指定的binder实体对象while (*p) {parent = *p;node = rb_entry(parent, struct binder_node, rb_node);if (ptr < node->ptr)p = &(*p)->rb_left;else if (ptr > node->ptr)p = &(*p)->rb_right;elsereturn NULL;}//创建一个binder实体对象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);binder_debug(BINDER_DEBUG_INTERNAL_REFS,"binder: %d:%d node %d u%p c%p created\n",proc->pid, current->pid, node->debug_id,node->ptr, node->cookie);return node;
}

这个函数的实现和binder_get_thread函数非常类似,都是先从指定的红黑树中查找,如果没有查找到就创建并初始化binder_thread或binder_node。现在ServiceManager进程在Binder驱动中就存在了对应的binder_thread和binder_node实体对象了。

等待客户端的请求

binder_loop(bs, svcmgr_handler)

ServiceManager进程通过binder_loop函数进入循环等待客户端的请求中,当有客户端请求时,进程ServiceManager被唤醒并调用回调函数svcmgr_handler来处理客户端的请求。

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线程状态binder_write(bs, readbuf, sizeof(unsigned));for (;;) {bwr.read_size = sizeof(readbuf);bwr.read_consumed = 0;bwr.read_buffer = (unsigned) readbuf;//循环读取客户端的请求,当没有客户端请求时,进程睡眠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, 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;}}
}

1.设置binder线程状态

ServiceManager进程在进入循环之前,通过ioctl系统调用设置Binder线程的运行状态为BINDER_LOOPER_STATE_ENTERED

int binder_write(struct binder_state *bs, void *data, unsigned len)
{struct binder_write_read bwr;int res;bwr.write_size = len;bwr.write_consumed = 0;bwr.write_buffer = (unsigned) data;bwr.read_size = 0;bwr.read_consumed = 0;bwr.read_buffer = 0;res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);if (res < 0) {fprintf(stderr,"binder_write: ioctl failed (%s)\n",strerror(errno));}return res;
}

binder_write函数实现较为简单,首先是设置binder_write_read结构体变量的值,然后通过ioctl传递到Binder驱动程序中,此时控制命令为BINDER_WRITE_READ,传入的参数bwr值为:

bwr.write_size = 4;
bwr.write_consumed = 0;
//data[0] = BC_ENTER_LOOPER
bwr.write_buffer = (unsigned) data;
bwr.read_size = 0;
bwr.read_consumed = 0;
bwr.read_buffer = 0;

由于write_buffer的大小为4,大于0,而read_buffer的大小为0,因此binder_ioctl函数中对BINDER_WRITE_READ命令的处理过程为:

case BINDER_WRITE_READ: {struct binder_write_read bwr;if (size != sizeof(struct binder_write_read)) {ret = -EINVAL;goto err;}//将参数从用户空间拷贝到内核空间if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {ret = -EFAULT;goto err;}//数据写入处理if (bwr.write_size > 0) {ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);if (ret < 0) {bwr.read_consumed = 0;if (copy_to_user(ubuf, &bwr, sizeof(bwr)))ret = -EFAULT;goto err;}}//将处理结果拷贝回用户空间if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {ret = -EFAULT;goto err;}break;
}

在binder_thread_write函数中,对BC_ENTER_LOOPER Binder协议的处理如下:

case BC_ENTER_LOOPER:if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {thread->looper |= BINDER_LOOPER_STATE_INVALID;}thread->looper |= BINDER_LOOPER_STATE_ENTERED;break;

这里仅仅设置了binder_thread结构体变量中的线程运行状态looper为BINDER_LOOPER_STATE_ENTERED,表示当前binder线程进入循环状态。

2.睡眠等待客户端请求

这里依然通过ioctl函数进入binder驱动程序,使用BINDER_WRITE_READ命令进程Binder驱动数据读写。传入的参数bwr此时的值为:

bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
bwr.read_size = 32;
bwr.read_consumed = 0;
bwr.read_buffer = (unsigned) readbuf;

由于write_buffer的大小等于0,而read_buffer的大小等于32,大于0,因此binder_ioctl函数中对BINDER_WRITE_READ命令的处理过程为:

case BINDER_WRITE_READ: {struct binder_write_read bwr;if (size != sizeof(struct binder_write_read)) {ret = -EINVAL;goto err;}//将参数从用户空间拷贝到内核空间if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {ret = -EFAULT;goto err;}//数据读处理if (bwr.read_size > 0) {ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);if (!list_empty(&proc->todo))wake_up_interruptible(&proc->wait);if (ret < 0) {if (copy_to_user(ubuf, &bwr, sizeof(bwr)))ret = -EFAULT;goto err;}}//将处理结果拷贝回用户空间if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {ret = -EFAULT;goto err;}break;
}

此时调用binder_thread_read函数来读取客户端发送过来的数据,在客户端发送请求到来前,进程将睡眠等待客户端的请求。

static int binder_thread_read(struct binder_proc *proc,struct binder_thread *thread,void  __user *buffer, int size,signed long *consumed, int non_block)
{void __user *ptr = buffer + *consumed;void __user *end = buffer + size;int ret = 0;int wait_for_proc_work;// *consumed == 0 if (*consumed == 0) {//写入一个值BR_NOOP到参数ptr指向的缓冲区中去if (put_user(BR_NOOP, (uint32_t __user *)ptr))return -EFAULT;ptr += sizeof(uint32_t);}
retry://thread->transaction_stack == NULL,并且thread->todo列表也是空的,这表示当前线程没有事务需要处理,于是wait_for_proc_work为truewait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo);//由于在初始化binder_thread时return_error被设置为BR_OK,因此这里条件不成立if (thread->return_error != BR_OK && ptr < end) {if (thread->return_error2 != BR_OK) {if (put_user(thread->return_error2, (uint32_t __user *)ptr))return -EFAULT;ptr += sizeof(uint32_t);if (ptr == end)goto done;thread->return_error2 = BR_OK;}if (put_user(thread->return_error, (uint32_t __user *)ptr))return -EFAULT;ptr += sizeof(uint32_t);thread->return_error = BR_OK;goto done;}//设置binder线程为等待状态thread->looper |= BINDER_LOOPER_STATE_WAITING;//如果当前线程没有事务需要处理,则增加proc->ready_threads计数if (wait_for_proc_work)proc->ready_threads++;mutex_unlock(&binder_lock);if (wait_for_proc_work) {if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED))) {wait_event_interruptible(binder_user_error_wait,binder_stop_on_user_error < 2);}//调用binder_set_nice函数设置当前线程的优先级别为proc->default_prioritybinder_set_nice(proc->default_priority);//文件打开模式为非阻塞模式,函数就直接返回-EAGAIN,要求用户重新执行ioctlif (non_block) {if (!binder_has_proc_work(proc, thread))ret = -EAGAIN;} else//当前线程就通过wait_event_interruptible_exclusive函数进入休眠状态,等待请求到来再唤醒了。ret = wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread));} else {if (non_block) {if (!binder_has_thread_work(thread))ret = -EAGAIN;} elseret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread));}//=============== 至此ServiceManager进程中的Binder线程进入睡眠等待状态,下面的代码只有在线程被唤醒时才执行===========================mutex_lock(&binder_lock);if (wait_for_proc_work)proc->ready_threads--;thread->looper &= ~BINDER_LOOPER_STATE_WAITING;if (ret)return ret;while (1) {uint32_t cmd;struct binder_transaction_data tr;struct binder_work *w;struct binder_transaction *t = NULL;if (!list_empty(&thread->todo))w = list_first_entry(&thread->todo, struct binder_work, entry);else if (!list_empty(&proc->todo) && wait_for_proc_work)w = list_first_entry(&proc->todo, struct binder_work, entry);else {if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) /* no data added */goto retry;break;}.....
done:*consumed = ptr - buffer;if (proc->requested_threads + proc->ready_threads == 0 &&proc->requested_threads_started < proc->max_threads &&(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to *//*spawn a new thread if we leave this out */) {proc->requested_threads++;if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))return -EFAULT;}return 0;
}

在没有客户端请求时,thread->transaction_stack和thread->todo都为空,当前进程就通过wait_event_interruptible_exclusive函数进入休眠状态,等待请求到来再唤醒。至此ServiceManager进程的启动过程就讲解完成了,ServiceManager进程启动过程首先打开binder驱动并开辟内核缓存区,同时将缓存区的物理页面同时映射到内核虚拟地址空间及进程虚拟地址空间中,然后在内核中创建属于ServiceManager进程的binder_node实体节点,接着设置处理客户端请求的binder线程运行状态,由于此时没有客户端的请求,ServiceManager进程进入睡眠等待中,直到客户端请求到来时,唤醒ServiceManager进程,代码才继续往下执行!

ServiceManager 进程启动源码分析相关推荐

  1. Android 9 (P)之init进程启动源码分析指南之三

          Android 9 (P)之init进程启动源码分析指南之三 Android 9 (P)系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 An ...

  2. Android 9(P)之init进程启动源码分析指南之一

         Android 9 之init进程启动源码分析指南之一 Android 9 (P) 系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 Andro ...

  3. Android 9 (P) Zygote进程启动源码分析指南二

         Android 9 Zygote进程启动源码分析指南二 Android 9 (P) 系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 Andro ...

  4. Android之vold进程启动源码分析

    1.Vold (Volume Daemon)介绍 vold进程接收来自内核的外部设备消息,用于管理和控制Android平台外部存储设备,包括SD插拨.挂载.卸载.格式化等:当外部设备发生变化时,内核通 ...

  5. Android之rild进程启动源码分析

    Android 电话系统框架介绍 在android系统中rild运行在AP上,AP上的应用通过rild发送AT指令给BP,BP接收到信息后又通过rild传送给AP.AP与BP之间有两种通信方式: 1. ...

  6. 【Android 启动过程】Activity 启动源码分析 ( ActivityThread 流程分析 二 )

    文章目录 前言 一.ActivityManagerService.attachApplicationLocked 二.ActivityStackSupervisor.attachApplication ...

  7. 【Android 启动过程】Activity 启动源码分析 ( ActivityThread -> Activity、主线程阶段 二 )

    文章目录 前言 一.ActivityThread 类 handleLaunchActivity -> performLaunchActivity 方法 二.Instrumentation.new ...

  8. 【Android 启动过程】Activity 启动源码分析 ( ActivityThread -> Activity、主线程阶段 一 )

    文章目录 前言 一.ClientTransactionHandler.scheduleTransaction 二.ActivityThread.H 处理 EXECUTE_TRANSACTION 消息 ...

  9. 【Android 启动过程】Activity 启动源码分析 ( AMS -> ActivityThread、AMS 线程阶段 二 )

    文章目录 前言 一.热启动与冷启动选择 二.AMS 进程中执行的相关操作 三.通过 Binder 机制转到 ActivityThread 中执行的操作 总结 前言 上一篇博客 [Android 启动过 ...

最新文章

  1. android studio 开发环境搭建
  2. shell编程基础之基本文本工具集合
  3. python用表达式解密密文_基于Python解密仿射密码
  4. 可折叠Web可能会给我们带来的变化
  5. Java SSM篇2——框架的基本认识
  6. H264格式详细说明
  7. Wrong namespace. Expected 'com.example.springboot.mapper.DepartmentMapper' but found 'com.sandystar.
  8. 运放的相位补偿 ?
  9. java 支持哪些字体_java有哪些字体
  10. RPC failed; curl 56 GnuTLS recv error (-9): A TLS packet with unexpected length was received
  11. Android 多张2K,4k图片加载压缩的详解
  12. 淘宝刷信用教程曝光:“一钻”售价仅为250元
  13. 文旅夜游项目如何做好景区内容建设
  14. MIPS环境填坑指南
  15. 使用ItextPdf+Freemarker+jfreechart实现将数据+使用jfreechart生成的图表转为html,再转为pdf。
  16. 什么是云计算?运维与云计算之间存在什么样的关系?
  17. 机械硬盘的内部结构与读写访问特性
  18. java开发的成长迷茫--java架构师
  19. 团队管理27--任务执行之流程机制
  20. 深圳某程序员2021减肥计划,目标55公斤

热门文章

  1. 《Core Java 课件》Day02
  2. java四舍五入自己写_java提高篇-----详解java的四舍五入与保留位
  3. 利用计算机提出算法分析问题,应用计算机计算不可积类型的积分算法分析
  4. mysql数据类型内存_mysql 存储金额类型,用什么数据类型比较可靠,一般企业数据用什么数据类型?...
  5. 交换机短路_交换机日常怎么运行维护?一文告诉你
  6. 《数据结构》陈越课件重点总结
  7. 面试官的几句话,差点让我挂在HTTPS上
  8. 交换机tftp服务器修改ip,华为交换机tftp服务器地址
  9. 贴吧粉丝怎么全部移除_亚马逊FBA怎么发货?怎么把货发到FBA仓库?
  10. python编程字符输入连接_python字符串连接的N种方式总结