前面几章我们讨论了Binder驱动的核心实现,包括数据结构和跨进程操作方法。今天我们就在Android系统中看看整个实现的流程是什么样子,我们就从service_manager.c开始!
可能有人要问了,为什么是service_manager.c而不是其他的,这里我需要说明一下,service_manager.c是整个系统管理Binder的代码,也是一个守护进程,并且自己就是一个binder。所以我们就从它下手!
framework/native/cnds/servicemanager.cpp
int main(int argc, char **argv) { struct binder_state *bs;//[1] bs = binder_open(128*1024);//[2] if (binder_become_context_manager(bs)) {//[3] ... } ... binder_loop(bs, svcmgr_handler);//[4] return 0; }打开设备节点,创建binder_proc进程结构体
static int binder_open(struct inode *nodp, struct file *filp) { struct binder_proc *proc;//[2.1]binder进程 proc = kzalloc(sizeof(*proc), GFP_KERNEL);//给进程分配空间 get_task_struct(current); proc->tsk = current; INIT_LIST_HEAD(&proc->todo);//初始化todo链表 init_waitqueue_head(&proc->wait);//初始化wait队列 proc->default_priority = task_nice(current);//优先级相关 binder_lock(__func__); binder_stats_created(BINDER_STAT_PROC); hlist_add_head(&proc->proc_node, &binder_procs); proc->pid = current->group_leader->pid; INIT_LIST_HEAD(&proc->delivered_death); filp->private_data = proc; //file文件指针的private_data变量指向binder_proc数据 binder_unlock(__func__); return 0; }作用是在内存中分配内存,并且交给proc中 - proc->buffer = area->addr - proc->user_buffer_offset 进行管理
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;//在binder_open时候赋值的进程 const char *failure_string; struct binder_buffer *buffer; if ((vma->vm_end - vma->vm_start) > SZ_4M) vma->vm_end = vma->vm_start + SZ_4M;//虚拟内存不能超过4M ... mutex_lock(&binder_mmap_lock); area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);//分配内存 proc->buffer = area->addr;//将内核的空间首地址分配proc->buffer 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); ... return ret; }这里整个函数是在内核分配空间,分配好之后将物理内存与虚拟内存中内核空间接收到的Buffer与用户控件接收到的Buffer映射起来。
给内核发送的消息是BINDER_SET_CONTEXT_MGR,然后内核创建一个binder_node *binder_context_mgr_node,这个binder_node是一个binder实体对象。
int binder_become_context_manager(struct binder_state *bs){ return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);//[3.1] }这里要注意的是,拿到数据是看todo链表中有没有数据,写数据也是写入todo链表中。其中数据是binder_transaction结构体,当binder_transaction()写入数据给线程或者进程的todo链表之后,然后进行唤醒操作,之后就可以通过binder_thread_read()方法进行读取数据到用户态的bwr中。
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;//bwr.read_buffer void __user *end = buffer + size; //当一开始读的时候,马上写入一个BR_NOOP,对于所有读操作数据头部都是BR_NOOP //给bwr.read_buffer内写的数据格式是:BR_NOOP+cmd+数据+cmd+数据... //如果保存返回结果的缓冲区中还没有数据先写入BR_NOOP消息 if (*consumed == 0) { if (put_user(BR_NOOP, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); } ret = wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread));//如果没有数据的话,就停留在休眠状态 while (1) { if (!list_empty(&thread->todo))//如果线程里面的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); switch (w->type) { case BINDER_WORK_TRANSACTION: {//这个type是binder_thread_write最后添加链表的时候写的 t = container_of(w, struct binder_transaction, work);//根据work得到binder_transaction } break; //调整优先级 if (t->buffer->target_node) { struct binder_node *target_node = t->buffer->target_node; tr.target.ptr = target_node->ptr; tr.cookie = target_node->cookie; t->saved_priority = task_nice(current); if (t->priority < target_node->min_priority && !(t->flags & TF_ONE_WAY)) binder_set_nice(t->priority); else if (!(t->flags & TF_ONE_WAY) || t->saved_priority > target_node->min_priority) binder_set_nice(target_node->min_priority); cmd = BR_TRANSACTION;//由于是从驱动返回用户空间把命令改成BR_TRANSACTION } //这里进行构造binder_transaction_data,然后返回到service_manager.c中,这里ioctl就执行完毕 tr.data_size = t->buffer->data_size; tr.offsets_size = t->buffer->offsets_size; tr.data.ptr.buffer = (void *)t->buffer->data + proc->user_buffer_offset; tr.data.ptr.offsets = tr.data.ptr.buffer + ALIGN(t->buffer->data_size, sizeof(void *)); //把tr数据复制到用户空间 if (put_user(cmd, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); if (copy_to_user(ptr, &tr, sizeof(tr))) return -EFAULT; ptr += sizeof(tr); break; } return 0; }同时我们知道找到服务返回的是handle值,这个值就在内核中引用的Binder对象。
uint32_t do_find_service(struct binder_state *bs, const uint16_t *s, size_t len, uid_t uid, pid_t spid) { struct svcinfo *si = find_svc(s, len);[4.3.1.1.1] if (!si || !si->handle) { return 0; } if (!si->allow_isolated) { uid_t appid = uid % AID_USER; if (appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END) { return 0; } } if (!svc_can_find(s, len, spid)) { return 0; } return si->handle; }从svclist服务列表中,根据服务名遍历查找是否已经注册。当服务已存在svclist,则返回相应的服务名,否则返回NULL。
struct svcinfo *find_svc(const uint16_t *s16, size_t len) { struct svcinfo *si; for (si = svclist; si; si = si->next) { if ((len == si->len) && !memcmp(s16, si->name, len * sizeof(uint16_t))) { return si; } } return NULL; }这里我们知道有svclist,但是我们并不知道从哪里来的这个数据结构,看下面
int do_add_service(struct binder_state *bs, const uint16_t *s, size_t len, uint32_t handle, uid_t uid, int allow_isolated, pid_t spid) { struct svcinfo *si; if (!handle || (len == 0) || (len > 127)) return -1; if (!svc_can_register(s, len, spid)) { ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n", str8(s, len), handle, uid); return -1; } si = find_svc(s, len); if (si) { if (si->handle) { svcinfo_death(bs, si); } si->handle = handle; } else { si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t)); if (!si) { return -1; } si->handle = handle; si->len = len; memcpy(si->name, s, (len + 1) * sizeof(uint16_t)); si->name[len] = '\0'; si->death.func = (void*) svcinfo_death; si->death.ptr = si; si->allow_isolated = allow_isolated; si->next = svclist; svclist = si;//不断向链表中添加 } binder_acquire(bs, handle); binder_link_to_death(bs, handle, &si->death); return 0; }首先我们知道了ServiceManager做的事情就是管理Binder。那么我们也知道到ServiceManager的流程如下:
1.打开open()驱动文件节点,并且通过mmap()分配虚拟空间,对应到真实的物理空间中去。2.然后通过binder_become_context_manager()创建一个Binder实体在内核3.进入循环状态,等待Client端的请求:binder_loop()。