ServiceManager 进程启动源码分析

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

service servicemanager /system/bin/servicemanager
    class core
    user system
    group system
    critical
    onrestart restart zygote
    onrestart restart media
    onrestart restart surfaceflinger
    onrestart 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;
	/× ========内核虚拟地址空间的分配========= ×/
    //判断内核缓存区在内核虚拟地址空间的起始值是否等于NULL
	if (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);
	//判断描述物理页面的数组起始地址是否等于NULL
	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;
    //注册进程虚拟地址空间的操作函数
	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;
	else
		mm = 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_SIZE
		page = &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;
		else
			break;
	}
	//如果没有查找到
	if (*p == NULL) {
		//创建一个线程描述符binder_thread
		thread = 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;
		else
			return 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为true
	wait_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_priority
		binder_set_nice(proc->default_priority);
		//文件打开模式为非阻塞模式,函数就直接返回-EAGAIN,要求用户重新执行ioctl
		if (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;
		} else
			ret = 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进程,代码才继续往下执行!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值