Binder是Android使用的进程间通讯工具。Android自己是一个复杂的操做系统,运行着众多的应用和服务,它们之间的通讯就显得尤其重要。应用程序在使用Binder进行进程间通讯时,实现方法很是简单,只须要编写AIDL文件,系统在编译时就会生成IPC通讯代码。这种简单的方式也使得应用开发者能够不用了解Binder底层的传输机制。但对于系统开发人员,熟知Binder的原理是必要的,在分析解决问题时多数状况都会和Binder纠缠在一块儿。本文讲解使用Binder的第一步,打开和初始化一个Binder设备。node
Binder驱动在初始化时注册为一个字符设备“/dev/binder”,并实现了poll、ioctl、mmap等接口。应用层对binder的控制都是经过ioctl完成的,而IPC通讯的内容就是经过ioctl的命令“BINDER_WRITE_READ”中传输的。android
Android中大多数的Binder通讯都是经过ProcessState来初始化Binder设备的。并发
frameworks/native/libs/binder/ProcessState.cpp #define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2)) ...... ProcessState::ProcessState() : mDriverFD(open_driver()) //打开binder设备 , mVMStart(MAP_FAILED) , mManagesContexts(false) , mBinderContextCheckFunc(NULL) , mBinderContextUserData(NULL) , mThreadPoolStarted(false) , mThreadPoolSeq(1) { if (mDriverFD >= 0) { // XXX Ideally, there should be a specific define for whether we // have mmap (or whether we could possibly have the kernel module // availabla). #if !defined(HAVE_WIN32_IPC) // mmap the binder, providing a chunk of virtual address space to receive transactions. // 映射binder地址空间 mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); if (mVMStart == MAP_FAILED) { // *sigh* ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n"); close(mDriverFD); mDriverFD = -1; } #else mDriverFD = -1; #endif } LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened. Terminating."); }
能够看到,ProcessState构造过程当中先打开Binder设备得到设备描述符,而后经过mmap映射了(1M - 8K)大小的地址空间用于数据传输。先看一下open_driver()。异步
frameworks/native/libs/binder/ProcessState.cpp static int open_driver() { int fd = open("/dev/binder", O_RDWR); if (fd >= 0) { fcntl(fd, F_SETFD, FD_CLOEXEC); int vers = 0; status_t result = ioctl(fd, BINDER_VERSION, &vers); if (result == -1) { ALOGE("Binder ioctl to obtain version failed: %s", strerror(errno)); close(fd); fd = -1; } if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) { ALOGE("Binder driver protocol does not match user space protocol!"); close(fd); fd = -1; } size_t maxThreads = 15; result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads); if (result == -1) { ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno)); } } else { ALOGW("Opening '/dev/binder' failed: %s\n", strerror(errno)); } return fd; }
这里打开设备“/dev/binder”,而后验证版本信息。以后经过BINDER_SET_MAX_THREADS设置一个进程中最大的Binder线程数,在驱动中设置给proc->max_threads。代码中设置的值为15,但驱动中的计数是从0开始的,因此实际上Binder最大线程数是16。接下来看下驱动的open函数,比较简单,就是建立Binder进程结构体并初始化。async
drivers/staging/android/binder.c static int binder_open(struct inode *nodp, struct file *filp) { struct binder_proc *proc; // Binder进程结构体 ...... 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); // 记录binder状态 hlist_add_head(&proc->proc_node, &binder_procs); proc->pid = current->group_leader->pid; INIT_LIST_HEAD(&proc->delivered_death); filp->private_data = proc; // 设备私有数据设置为binder进程结构体 binder_unlock(__func__); ...... }
Bind_proc用于管理Binder进程,一个进程只会有一个binder_proc,记录了Binder传输的全部信息。下面看一下binder_proc的定义。函数
drivers/staging/android/binder.c struct binder_proc { struct hlist_node proc_node; // 该binder进程的节点, 挂载到binder_procs链表中 struct rb_root threads; // binder线程ID红黑树 struct rb_root nodes; // binder实体对象红黑树 struct rb_root refs_by_desc; // binder引用对象红黑树,以handle为key struct rb_root refs_by_node; // binder引用对象红黑树,以node为key int pid; // 进程ID struct vm_area_struct *vma; // 该进程的虚拟地址空间指针 struct mm_struct *vma_vm_mm; // 该进程的内存结构体 struct task_struct *tsk; // 该进程的task结构体 struct files_struct *files; // 该进程的file结构体 struct hlist_node deferred_work_node; // 延迟工做队列节点,挂载到binder_deferred_list链表中 int deferred_work; // 延迟工做的类型 void *buffer; // 映射的内核空间地址 ptrdiff_t user_buffer_offset; // 映射的内核空间与用户空间的偏移 struct list_head buffers; // 所有buffer链表 struct rb_root free_buffers; // 空闲buffer红黑树 struct rb_root allocated_buffers; // 已分配的buffer红黑树 size_t free_async_space; // 剩余异步传输空间大小 struct page **pages; // 物理内存页 size_t buffer_size; // 映射的buffer大小 uint32_t buffer_free; // 剩余可用buffer大小 struct list_head todo; // 进程工做链表 wait_queue_head_t wait; // 等待队列 struct binder_stats stats; // binder统计信息 struct list_head delivered_death; // 已经发布的死亡通知链表 int max_threads; // 最大线程数 int requested_threads; // 请求的线程数 int requested_threads_started; // 请求的线程已经启动的数量 int ready_threads; // 已经准备好的线程数 long default_priority; // 默认优先级 struct dentry *debugfs_entry; // debugf入口指针 };
建立binder_proc,完成初始化后,应用层会调用mmap()来映射地址空间。工具
drivers/staging/android/binder.c 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 if ((vma->vm_end - vma->vm_start) > SZ_4M) vma->vm_end = vma->vm_start + SZ_4M; ...... // fork的子进程没法复制映射空间,而且不容许修改写属性 vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE; ...... // 获取内核虚拟地址空间 area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP); ...... proc->buffer = area->addr; proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer; mutex_unlock(&binder_mmap_lock); ...... // 建立物理页结构体 proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL); ...... proc->buffer_size = vma->vm_end - vma->vm_start; 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)) { ...... // 建立buffers链表,插入第一个free buffer 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; proc->vma_vm_mm = vma->vm_mm; ...... }
Binder_mmap()主要完成了内核虚拟地址空间分配,地址空间映射,binder_proc中内存相关参数的初始化等工做。真正的物理页分配时经过binder_update_page_range()进行的,这里只分配了一个物理页放入free_buffers树中。以后的Binder传输过程当中会根据须要分配和回收buffer。oop
drivers/staging/android/binder.c 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; ...... // 只有mmap时vma不为NULL,其余状况都会根据proc来获取内存相关数据 if (vma) mm = NULL; else // 获取内存描述符,并增长用户计数,防止mm_struct被释放 mm = get_task_mm(proc->tsk); if (mm) { down_write(&mm->mmap_sem); vma = proc->vma; if (vma && mm != proc->vma_vm_mm) { pr_err("%d: vma mm and task mm mismatch\n", proc->pid); vma = NULL; } } // 回收内存时allocate为0 if (allocate == 0) goto free_range; ...... // 循环分配物理页,每次分配一页 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); // 分配一个物理页,保存地址到proc中 *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO); ...... 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); ...... user_page_addr = (uintptr_t)page_addr + proc->user_buffer_offset; // 插入物理页到用户虚拟地址空间 ret = vm_insert_page(vma, user_page_addr, page[0]); ...... } 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: ; } ........ }
ProcessState构造函数执行完,也就完成了对Binder设备的初始化和内存空间映射。应用层得到了Binder设备的描述符mDriverFD,以后经过ioctl操做就能够进行IPC传输。ui
以前分析到,一个Binder进程最多能够有16个Binder线程,每一个Binder线程均可以独立进行Binder传输。第一个Binder线程通常在建立ProcessState对象后,经过ProcessState::startThreadPool()来建立。this
frameworks/native/libs/binder/ProcessState.cpp class PoolThread : public Thread { public: PoolThread(bool isMain) : mIsMain(isMain) { } protected: virtual bool threadLoop() { // 经过startThreadPool建立时,mIsMain是true,为主线程 IPCThreadState::self()->joinThreadPool(mIsMain); return false; } const bool mIsMain; }; ...... void ProcessState::startThreadPool() { AutoMutex _l(mLock); // 第一个线程必须经过startThreadPool来建立 if (!mThreadPoolStarted) { mThreadPoolStarted = true; spawnPooledThread(true); } } ...... void ProcessState::spawnPooledThread(bool isMain) { if (mThreadPoolStarted) { // 构造binder名字:Binder_XX String8 name = makeBinderThreadName(); ALOGV("Spawning new pooled thread, name=%s\n", name.string()); sp<Thread> t = new PoolThread(isMain); t->run(name.string()); } }
最终真正的Binder线程建立是经过IPCThreadState::joinThreadPool()来实现的。
frameworks/native/libs/binder/IPCThreadState.cpp void IPCThreadState::joinThreadPool(bool isMain) { ...... // 主线程使用命令BC_ENTER_LOOPER,之后再建立线程都经过BC_REGISTER_LOOPER mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER); ...... // 设置线程为前台group,保证Binder传输初始化 set_sched_policy(mMyThreadId, SP_FOREGROUND); status_t result; do { // 清理命令队列 processPendingDerefs(); // now get the next command to be processed, waiting if necessary // 获取一个命令并执行 result = getAndExecuteCommand(); ...... // 非主线程时,若是线程不在须要将退出。主线程将一直存活 if(result == TIMED_OUT && !isMain) { break; } } while (result != -ECONNREFUSED && result != -EBADF); ...... // 线程退出命令 mOut.writeInt32(BC_EXIT_LOOPER); talkWithDriver(false); }
joinThreadPool()发送命令BC_ENTER_LOOPER建立第一个Binder线程。以后进入循环,经过getAndExecuteCommand()获取命令并执行。简单看下是如何与驱动交互的。
frameworks/native/libs/binder/IPCThreadState.cpp status_t IPCThreadState::getAndExecuteCommand() { status_t result; int32_t cmd; // 与Binder驱动进行交互 result = talkWithDriver(); if (result >= NO_ERROR) { size_t IN = mIn.dataAvail(); ...... // 执行命令 result = executeCommand(cmd); ...... // 执行命令时可能将线程设置为后台group,再次设置为前台group set_sched_policy(mMyThreadId, SP_FOREGROUND); } return result; } ...... status_t IPCThreadState::talkWithDriver(bool doReceive) { ...... // 写数据为mOut bwr.write_size = outAvail; bwr.write_buffer = (uintptr_t)mOut.data(); // This is what we'll read. // 读数据为mIn if (doReceive && needRead) { bwr.read_size = mIn.dataCapacity(); bwr.read_buffer = (uintptr_t)mIn.data(); } else { bwr.read_size = 0; bwr.read_buffer = 0; } ...... // 使用BINDER_WRITE_READ对驱动进行读写 if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) ...... }
应用对Binder驱动的读写是经过ioctl的BINDER_WRITE_READ命令完成的,读写数据为mIn和mOut。mIn和mOut为Parcel实例,Binder的数据都须要通过Parcel将数据序列化后才传输的,应用接收的数据再通过反序列化读出。咱们先关心线程的建立,看一下驱动中对BC_ENTER_LOOPER的处理。
drivers/staging/android/binder.c static int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, binder_uintptr_t binder_buffer, size_t size, binder_size_t *consumed) { ...... case BC_ENTER_LOOPER: ...... // 设置thread looper状态 thread->looper |= BINDER_LOOPER_STATE_ENTERED; break; ...... } ...... 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; // 在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; } // 若是threads树上没有找到线程则建立一个线程,并插入到树中 if (*p == NULL) { 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; } ...... static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { ...... // 在binder进程中获取当前线程的binder线程 thread = binder_get_thread(proc); ...... switch (cmd) { case BINDER_WRITE_READ: { if (bwr.write_size > 0) { // 写数据 ret = binder_thread_write(proc, thread, bwr.write_buffer, bwr.write_size, &bwr.write_consumed); ...... if (bwr.read_size > 0) { // 读数据 ret = binder_thread_read(proc, thread, bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK); ...... if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { ret = -EFAULT; goto err; } break;BC_ENTER_LOOPER } ...... }
整理一下第一个Binder线程建立的流程,
进程中的第一个Binder线程建立后,再建立Binder线程有两种方式。
大部分后续Binder线程的建立都是经过BR_SPAWN_LOOPER命令来完成的。这个命令是由Binder驱动发出的。当Binder驱动检查到Binder线程不足时,就会发送BR_SPAWN_LOOPER给应用层,用来建立Binder线程。
drivers/staging/android/binder.c 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) { ...... // 是否从proc中获取工做项 wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo); ...... // 设置状态为Binder线程正在等待处理 thread->looper |= BINDER_LOOPER_STATE_WAITING; // 若是处理proc工做,则让ready_threads加1 if (wait_for_proc_work) proc->ready_threads++; binder_unlock(__func__); ...... // 线程进入休眠,等待数据到来 if (wait_for_proc_work) { ...... ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread)); } else { ...... ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread)); } binder_lock(__func__); // 若是处理proc工做,则让ready_threads减1,表面Binder线程开始处理数据 if (wait_for_proc_work) proc->ready_threads--; // 移除Binder线程等待处理状态 thread->looper &= ~BINDER_LOOPER_STATE_WAITING; ...... // 若是没有剩余的可用Binder线程,而且没有达到最大线程数,则发送BR_SPAWN_LOOPER 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++; binder_debug(BINDER_DEBUG_THREADS, "%d:%d BR_SPAWN_LOOPER\n", proc->pid, thread->pid); if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer)) return -EFAULT; binder_stat_br(proc, thread, BR_SPAWN_LOOPER); } return 0; }
当进程中没有剩余可用的Binder线程时,驱动就会发送BR_SPAWN_LOOPER命令,应用层接收到该命令则执行spawnPooledThread(false)。这个函数在上面分析过,会向驱动发送BC_REGISTER_LOOPER命令。
drivers/staging/android/binder.c static int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, binder_uintptr_t binder_buffer, size_t size, binder_size_t *consumed) { ...... case BC_REGISTER_LOOPER: ...... } else { // 更新proc中线程状态计数 proc->requested_threads--; proc->requested_threads_started++; } // 设置thread looper状态 thread->looper |= BINDER_LOOPER_STATE_REGISTERED; break; ...... }
能够看到,proc中线程相关的计数有三个,
根据上面分析,能够知道Binder线程有两种建立方式,
上图是对Binder线程建立流程的简单整理。能够看到若是Binder主线程接收到消息就会再建立一个Binder线程,以后若是接收消息时没有空余线程才会建立。因此在一些Native Service中,会在初始化时就调用joinThreadPool()建立一个副线程,这样Service在开始运行时就会有两个Binder线程,由于它们很明确必定会使用跨进程通讯。