在回答binder_transaction(...)以前,还有一些基础设施要去探究,好比binder_open(...),binder_mmap(...),这些调用是在打开设备文件/dev/binder以后必须完成的程式化操做,而在它们内部须要作一些数据结构的准备。首先来看binder_open(...)
kernel/drivers/staging/android/binder.c:2979node
static int binder_open(struct inode *nodp, struct file *filp) { struct binder_proc *proc; ...... proc = kzalloc(sizeof(*proc), GFP_KERNEL); // 建立binder_proc结构体 ...... get_task_struct(current); proc->tsk = current; INIT_LIST_HEAD(&proc->todo); // 初始化链表头 init_waitqueue_head(&proc->wait); proc->default_priority = task_nice(current); ...... // 将proc_node串入全局链表binder_procs中 hlist_add_head(&proc->proc_node, &binder_procs); proc->pid = current->group_leader->pid; INIT_LIST_HEAD(&proc->delivered_death); filp->private_data = proc; ...... return 0; }
binder_open(...)生成并初始化binder_proc结构体以下:
linux
struct binder_proc描述一个“正在使用Binder进程间通讯机制”的进程,它的定义参见kernel/goldfish/drivers/staging/android/binder.c:286android
struct binder_proc { // 进程打开设备文件/dev/binder时,Binder驱动会为它建立一个binder_proc结构体,并将它 // 保存在全局hash列表中,proc_node是该hash列表的节点。 struct hlist_node proc_node; // 每一个使用了Binder机制的进程都有一个Binder线程池,用来处理进程间通讯请求。threads以 // 线程ID做为key来组织进程的Binder线程池。进程能够调用ioctl将线程注册到Binder驱动中 // 当没有足够的空闲线程处理进程间通讯请求时,驱动能够要求进程注册更多的线程到Binder线程 // 池中 struct rb_root threads; struct rb_root nodes; // 组织Binder实体对象,它以成员ptr做为key struct rb_root refs_by_desc; // 组织Binder引用对象,它以成员desc做为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; // 指向进程任务控制块 struct files_struct *files; // 指向进程打开文件结构体数组 // 一个hash表,保存进程能够延迟执行的工做项,这些延迟工做有三种类型 // BINDER_DEFERRED_PUT_FILES、BINDER_DEFERRED_FLUSH、BINDER_DEFERRED_RELEASE // 驱动为进程分配内核缓冲区时,会为该缓冲区建立一个文件描述符,进程能够经过该描述符将该内 // 核缓冲区映射到本身的地址空间。当进程再也不须要使用Binder机制时,就会通知驱动关闭该文件 // 描述符并释放以前所分配的内核缓冲区。因为这不是一个立刻就要完成的操做,所以驱动会建立一 // 个BINDER_DEFERRED_PUT_FILES类型的工做来延迟执行; // Binder线程池中的空闲Binder线程是睡眠在一个等待队列中的,进程能够经过调用函数flush // 来唤醒这些线程,以便它们能够检查进程是否有新的工做项须要处理。此时驱动会建立一个 // BINDER_DEFERRED_FLUSH类型的工做项,以便延迟执行唤醒空闲Binder线程的操做; // 当进程再也不使用Binder机制时,会调用函数close关闭文件/dev/binder,此时驱动会释放之 // 前为它分配的资源,因为资源释放是个比较耗时的操做,驱动会建立一个 // BINDER_DEFERRED_RELEASE类型的事务来延迟执行 struct hlist_node deferred_work_node; int deferred_work; // 描述该延迟工做项的具体类型 void *buffer; // 内核缓冲区的内核空间地址,供驱动程序使用 ptrdiff_t user_buffer_offset; // vma和buffer之间的差值 // buffer指向一块大的内核缓冲区,驱动程序为方便管理,将它划分红若干小块,这些小块的内核缓 // 冲区用binder_buffer描述保存在列表中,按地址从小到大排列。buffers指向该列表的头部。 struct list_head buffers; struct rb_root free_buffers; // buffers中的小块有的正在使用,被保存在此红黑树 struct rb_root allocated_buffers; // buffers中的空闲小块被保存在此红黑树 size_t free_async_space; // 当前可用的保存异步事务数据的内核缓冲区的大小 struct page **pages; // buffer和vma都是虚拟地址,它们对应的物理页面保存在pages // 中,这是一个数组,每一个元素指向一个物理页面 size_t buffer_size; // 进程调用mmap将它映射到进程地址空间,其实是请求驱动为它 // 分配一块内核缓冲区,缓冲区大小保存在该成员中 uint32_t buffer_free; // 空闲内核缓冲区的大小 struct list_head todo; // 当进程接收到一个进程间通讯请求时,Binder驱动就将该请求封 // 装成一个工做项,而且加入到进程的待处理工做向队列中,该队列 // 使用成员变量todo来描述。 wait_queue_head_t wait; // 线程池中空闲Binder线程会睡眠在由该成员所描述的等待队列中 // 当宿主进程的待处理工做项队列增长新工做项后,驱动会唤醒这 // 些线程,以便处理新的工做项 struct binder_stats stats; // 用来统计进程数据 // 当进程所引用的Service组件死亡时,驱动会向该进程发送一个死亡通知。这个正在发出的通知被 // 封装成一个类型为BINDER_WORK_DEAD_BINDER或BINDER_WORK_DEAD_BINDER_AND_CLEAR // 的工做项,并保存在由该成员描述的队列中删除 struct list_head delivered_death; int max_threads; // 驱动程序最多能够主动请求进程注册的线程数 int requested_threads; int requested_threads_started; int ready_threads; // 进程当前的空闲Binder线程数 long default_priority; // 进程的优先级,当线程处理一个工做项时,线程优先级可能被 // 设置为宿主进程的优先级 struct dentry *debugfs_entry; };
在binder_proc内部有若干个list_head类型的字段,用来把binder_proc串到不一样的链表中去。通常写链表的作法是在链表节点结构体中追加业务逻辑字段,顺着链表的prev、next指针到达指定节点,而后再访问业务逻辑字段:
在Linux代码中则经常反过来,先定义业务逻辑的结构体,在其内部嵌入链表字段list_head,顺着该字段遍历链表,在每一个节点上根据该字段与所在结构体的偏移量找到所在结构体,访问业务逻辑字段:
这样作的好处是让业务逻辑和链表逻辑分离,Linux还定义了宏用于操做链表,以及根据链表字段找到所在结构体。如binder_proc结构体内部盛放多个list_head,表示把该结构体串入了不一样的链表。
具体技巧可参见《Linux内核设计与实现》第6章。git
回到binder_open(...),除了直接字段赋值,须要解释的是几个链表字段的处理。
INIT_LIST_HEAD(&proc->todo)
用于将todo的next、prev指针指向本身,该宏的定义在kernel/goldfish/include/linux/lish.t:24github
static inline void INIT_LIST_HEAD(struct list_head *list) { list->next = list; list->prev = list; }
init_waitqueue_head(&proc->wait)
这个宏定义在kernel/goldfish/include/linux/wait.h:81数组
#define init_waitqueue_head(q) \ do { \ static struct lock_class_key __key; \ \ __init_waitqueue_head((q), #q, &__key); \ } while (0)
__init_waitqueue_head(...)
定义在kernel/goldfish/kernel/wait.c:13,主要完成了对task_list字段的初始化:安全
void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *key) // q=(&proc->todo) { spin_lock_init(&q->lock); lockdep_set_class_and_name(&q->lock, key, name); INIT_LIST_HEAD(&q->task_list); // 为何使用符号->来提领task_list呢? }
说到底仍是初始化proc->wait->task_list字段。不过有点奇怪task_list是wait内的结构体,而不是结构体指针,为何对task_list的提领使用符号->
呢?cookie
struct binder_proc { ...... wait_queue_head_t wait; ...... };
kernel/goldfish/include/linux/wait.h:53数据结构
struct __wait_queue_head { spinlock_t lock; struct list_head task_list; }; typedef struct __wait_queue_head wait_queue_head_t;
hlist_add_head(&proc->proc_node, &binder_procs)
将proc->proc_node节点串到全局链表binder_procs的头部,其定义在kernel/goldfish/include/linux/list.h:610app
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) { struct hlist_node *first = h->first; n->next = first; if (first) first->pprev = &n->next; h->first = n; n->pprev = &h->first; }
kernel/goldfish/include/linux/types.h:233
struct hlist_head { struct hlist_node *first; }; struct hlist_node { struct hlist_node *next, **pprev; };
综上所述,binder_open(...)组织的数据结构proc为:
接下来就是binder_mmap(...),当进程打开/dev/binder以后,必须调用mmap(...)函数把该文件映射到进程的地址空间。
kernel/goldfish/drivers/staging/android/binder.c:2883
static int binder_mmap(struct file *filp, struct vm_area_struct *vma) { int ret; struct vm_struct *area; // area描述内核地址空间;vma描述用户地址空间 struct binder_proc *proc = filp->private_data; const char *failure_string; struct binder_buffer *buffer; ...... 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)) { ret = -ENOMEM; failure_string = "alloc small buf"; goto err_alloc_small_buf_failed; } buffer = proc->buffer; INIT_LIST_HEAD(&proc->buffers); list_add(&buffer->entry, &proc->buffers); // 把entry串到buffers链表中 buffer->free = 1; binder_insert_free_buffer(proc, buffer); proc->free_async_space = proc->buffer_size / 2; barrier(); proc->files = get_files_struct(proc->tsk); proc->vma = vma; proc->vma_vm_mm = vma->vm_mm; /*printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/ return 0; err_alloc_small_buf_failed: kfree(proc->pages); proc->pages = NULL; err_alloc_pages_failed: mutex_lock(&binder_mmap_lock); vfree(proc->buffer); proc->buffer = NULL; err_get_vm_area_failed: err_already_mapped: mutex_unlock(&binder_mmap_lock); err_bad_arg: printk(KERN_ERR "binder_mmap: %d %lx-%lx %s failed %d\n", proc->pid, vma->vm_start, vma->vm_end, failure_string, ret); return ret; }
到第28行调用binder_update_page_range(...)以前,binder_mmap(...)在内核地址空间申请了struct vm_struct area
,并完成部分红员的初始化,以下:
kernel/goldfish/drivers/staging/android/binder.c:627
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; ... ... if (vma) mm = NULL; else mm = get_task_mm(proc->tsk); if (mm) { down_write(&mm->mmap_sem); vma = proc->vma; ... ... } 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); *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: ; } err_no_vma: if (mm) { up_write(&mm->mmap_sem); mmput(mm); } return -ENOMEM; }
以后在binder_mmap(...)第34行,buffer的类型是struct binder_buffer*
,该结构体用来描述一个内核缓冲区,该缓冲区用于在进程间传输数据。
kernel/goldfish/drivers/staging/android/binder.c:263
struct binder_buffer { // 每个使用Binder机制的进程在Binder驱动中都有一个内核缓冲区列表,用来保存Binder驱动 // 程序为它分配的内核缓冲区,entry是该列表的一个节点 struct list_head entry; /* free and allocated entries by address */ // 进程使用两个红黑树分别保存使用中以及空闲的内核缓冲区。若是空闲,free=1, //rb_node就是空闲内核缓冲区红黑树中的节点,不然是使用中内核缓冲区红黑树中的节点 struct rb_node rb_node; /* free entry by size or allocated entry */ /* by address */ unsigned free:1; // Service处理完成该事务后,若发现allow_user_free为1,会请求驱动程序释放该内核缓冲区 unsigned allow_user_free:1; unsigned async_transaction:1; // 与该内核缓冲区关联的是一个异步事务 unsigned debug_id:29; struct binder_transaction *transaction; // 内核缓冲区正交给哪一个事务使用 struct binder_node *target_node; // 内核缓冲区正交给哪一个Binder实体对象使用 size_t data_size; size_t offsets_size; // 保存通讯数据,分两种类型:普通数据、Binder对象。驱动程序不关心普通数据,但必须知道里面 // 的Binder对象,由于要根据它们来维护内核中Binder实体对象和Binder引用对象的生命周期。 uint8_t data[0]; };
初始化完proc->buffers以后,第36行执行一个list_add(...),该函数定义见kernel/goldfish/include/linux/list.h:37~60
static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } ... ... static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); }
运算过程以下图:
因而到binder_mmap(...)
第37行为止,binder_mmap(...)构造的数据结构以下:
kernel/goldfish/drivers/statging/android/binder.c:545
static void binder_insert_free_buffer(struct binder_proc *proc, struct binder_buffer *new_buffer) { // new_buffer就是以前分配的buffer,被转型成了binder_buffer struct rb_node **p = &proc->free_buffers.rb_node; struct rb_node *parent = NULL; struct binder_buffer *buffer; size_t buffer_size; size_t new_buffer_size; ... ... // 计算binder_buffer中data部分的大小 new_buffer_size = binder_buffer_size(proc, new_buffer); ... ... // 根据new_buffer的大小,找到在proc->free_buffers红黑树中的正确位置,并插入 while (*p) { parent = *p; buffer = rb_entry(parent, struct binder_buffer, rb_node); BUG_ON(!buffer->free); buffer_size = binder_buffer_size(proc, buffer); if (new_buffer_size < buffer_size) p = &parent->rb_left; else p = &parent->rb_right; } rb_link_node(&new_buffer->rb_node, parent, p); rb_insert_color(&new_buffer->rb_node, &proc->free_buffers); }
因而到binder_mmap(...)结束,这个binder_proc结构体就被作成了这样:
binder_transaction(...)
从native层的调用过程参见binder学习笔记(十)—— 穿越到驱动层。咱们以addService为例深刻到binder_transaction(...)内部,
传入的binder_transaction_data
输入参数为:
kernel/goldfish/drivers/staging/android/binder.c:1402
static void binder_transaction(struct binder_proc *proc, struct binder_thread *thread, struct binder_transaction_data *tr, int reply) { // reply=(cmd==BC_REPLY)即false,flags=TF_ACCEPT_FDS // proc和thread表示当前进程对应的数据结构 struct binder_transaction *t; struct binder_work *tcomplete; size_t *offp, *off_end; struct binder_proc *target_proc; struct binder_thread *target_thread = NULL; struct binder_node *target_node = NULL; struct list_head *target_list; wait_queue_head_t *target_wait; struct binder_transaction *in_reply_to = NULL; struct binder_transaction_log_entry *e; uint32_t return_error; ...... if (reply) { ...... } else { if (tr->target.handle) { // tr->target.handle!=0 ...... } else { // target_node表示binder请求要发送到的节点,此处即 // service manager对应的节点 target_node = binder_context_mgr_node; ...... } ...... target_proc = target_node->proc; // 获得目标进程的binder_proc ...... // 获得目标线程tr->flags=TF_ACCEPT_FDS // thread未被操做过,故transaction_stack为0 if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) { struct binder_transaction *tmp; tmp = thread->transaction_stack; ... ... while (tmp) { if (tmp->from && tmp->from->proc == target_proc) target_thread = tmp->from; tmp = tmp->from_parent; } } } if (target_thread) { e->to_thread = target_thread->pid; target_list = &target_thread->todo; target_wait = &target_thread->wait; } else { // 走这里 target_list = &target_proc->todo; target_wait = &target_proc->wait; } ...... t = kzalloc(sizeof(*t), GFP_KERNEL); // 建立binder_transaction节点 ...... tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);//建立一个binder_work节点 ...... // 这里岂不是为真?thread来自binder_ioctl(...)中的binder_get_thread(proc) // 返回proc的当前线程 if (!reply && !(tr->flags & TF_ONE_WAY)) t->from = thread; else t->from = NULL; t->sender_euid = proc->tsk->cred->euid; // 源线程用户id t->to_proc = target_proc; // 负责处理该事务的进程,sm t->to_thread = target_thread; // 负责处理该事务的线程 t->code = tr->code; // ADD_SERVICE_TRANSACTION t->flags = tr->flags; // TF_ACCEPT_FDS t->priority = task_nice(current); // 源线程优先级 ... ... t->buffer = binder_alloc_buf(target_proc, tr->data_size, tr->offsets_size, !reply && (t->flags & TF_ONE_WAY)); ...... t->buffer->allow_user_free = 0;// Service处理完该事务后,驱动不会释放该内核缓冲区 t->buffer->debug_id = t->debug_id; t->buffer->transaction = t; // 缓冲区正交给哪一个事务使用 t->buffer->target_node = target_node; // 缓冲区正交给哪一个Binder实体对象使用 ...... if (target_node) binder_inc_node(target_node, 1, 0, NULL); // 分析所传数据中的全部binder对象,若是是binder实体,在红黑树中添加相应的节点。 // 首先,从用户态获取所传输的数据,以及数据里的binder对象偏移信息。 offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *))); // 将服务端传来的Parcel的数据部分拷贝到内核空间 if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) { ...... } // 将服务端传来的Parcel的偏移数组拷贝到内核空间 if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) { ...... } ...... off_end = (void *)offp + tr->offsets_size; // 遍历每一个flat_binder_object信息,建立必要的红黑树节点 for (; offp < off_end; offp++) { struct flat_binder_object *fp; ...... fp = (struct flat_binder_object *)(t->buffer->data + *offp); switch (fp->type) { case BINDER_TYPE_BINDER: case BINDER_TYPE_WEAK_BINDER: { // 若是是binder实体 struct binder_ref *ref; // fp->binder是BnTestService::getWeakRefs(),BnTestService的影子对象 // binder_get_node(...)在proc->nodes.rb_node中找fp->binder,若是没有 // 找到,则在该红黑树中为fp->binder建立节点 struct binder_node *node = binder_get_node(proc, fp->binder); if (node == NULL) { node = binder_new_node(proc, fp->binder, fp->cookie); ...... node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK; node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); } ...... // 必要时,会在目标进程的binder_proc中建立对应的binder_ref红黑树节点 ref = binder_get_ref_for_node(target_proc, node); ...... if (fp->type == BINDER_TYPE_BINDER) fp->type = BINDER_TYPE_HANDLE; else fp->type = BINDER_TYPE_WEAK_HANDLE; // 修改所传数据中的flat_binder_object信息,由于远端的binder实体到 // 了目标端就变为binder代理了,因此要记录下binder句柄了。 fp->handle = ref->desc; binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE, &thread->todo); ...... } break; case BINDER_TYPE_HANDLE: case BINDER_TYPE_WEAK_HANDLE: { // 对flat_binder_object作必要的修改,好比将BINDER_TYPE_HANDLE改成 // BINDER_TYPE_BINDER struct binder_ref *ref = binder_get_ref(proc, fp->handle); ...... if (ref->node->proc == target_proc) { if (fp->type == BINDER_TYPE_HANDLE) fp->type = BINDER_TYPE_BINDER; else fp->type = BINDER_TYPE_WEAK_BINDER; fp->binder = ref->node->ptr; fp->cookie = ref->node->cookie; binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL); trace_binder_transaction_ref_to_node(t, ref); ... ... } else { struct binder_ref *new_ref; new_ref = binder_get_ref_for_node(target_proc, ref->node); if (new_ref == NULL) { return_error = BR_FAILED_REPLY; goto err_binder_get_ref_for_node_failed; } fp->handle = new_ref->desc; binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL); trace_binder_transaction_ref_to_ref(t, ref, new_ref); ... ... } } break; case BINDER_TYPE_FD: { int target_fd; struct file *file; ... ... file = fget(fp->handle); ... ... target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC); ... ... task_fd_install(target_proc, target_fd, file); trace_binder_transaction_fd(t, fp->handle, target_fd); binder_debug(BINDER_DEBUG_TRANSACTION, " fd %ld -> %d\n", fp->handle, target_fd); /* TODO: fput? */ fp->handle = target_fd; } break; default: ... ... return_error = BR_FAILED_REPLY; goto err_bad_object_type; } } if (reply) { ...... } else if (!(t->flags & TF_ONE_WAY)) { ... ... t->need_reply = 1; t->from_parent = thread->transaction_stack; thread->transaction_stack = t; } else { ...... if (target_node->has_async_transaction) { target_list = &target_node->async_todo; target_wait = NULL; } else target_node->has_async_transaction = 1; } t->work.type = BINDER_WORK_TRANSACTION; // 把binder_transaction节点插入target_list(即目标todo队列) list_add_tail(&t->work.entry, target_list); tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; list_add_tail(&tcomplete->entry, &thread->todo); if (target_wait) // 传输动做完毕,如今能够唤醒系统中其它相关线程,wake up! wake_up_interruptible(target_wait); return; err_get_unused_fd_failed: err_fget_failed: err_fd_not_allowed: err_binder_get_ref_for_node_failed: err_binder_get_ref_failed: err_binder_new_node_failed: err_bad_object_type: err_bad_offset: err_copy_data_failed: trace_binder_transaction_failed_buffer_release(t->buffer); binder_transaction_buffer_release(target_proc, t->buffer, offp); t->buffer->transaction = NULL; binder_free_buf(target_proc, t->buffer); err_binder_alloc_buf_failed: kfree(tcomplete); binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE); err_alloc_tcomplete_failed: kfree(t); binder_stats_deleted(BINDER_STAT_TRANSACTION); err_alloc_t_failed: err_bad_call_stack: err_empty_call_stack: err_dead_binder: err_invalid_target_handle: err_no_context_mgr_node: binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, "binder: %d:%d transaction failed %d, size %zd-%zd\n", proc->pid, thread->pid, return_error, tr->data_size, tr->offsets_size); { struct binder_transaction_log_entry *fe; fe = binder_transaction_log_add(&binder_transaction_log_failed); *fe = *e; } BUG_ON(thread->return_error != BR_OK); if (in_reply_to) { thread->return_error = BR_TRANSACTION_COMPLETE; binder_send_failed_reply(in_reply_to, return_error); } else thread->return_error = return_error; }
在函数binder_transaction(...)第53行建立告终构体binder_transaction,该结构体用来描述进程间通讯过程,即事务。其定义在kernel/goldfish/drivers/staging/android/binder.c:346
struct binder_transaction { int debug_id; // 当驱动为目标进程或线程建立一个事务时,就会将该成员的type置为 // BINDER_WORK_TRANSACTION,并将它添加到目标进程或线程的todo队列,等待处理 struct binder_work work; struct binder_thread *from; // 发起事务的线程 // 事务所依赖的另一个事务以及目标线程下一个要处理的事务 struct binder_transaction *from_parent; struct binder_proc *to_proc; // 负责处理该事务的进程 struct binder_thread *to_thread; // 负责处理该事务的线程 struct binder_transaction *to_parent; unsigned need_reply:1; // 同步事务为1须要等待对方回复;异步为0 /* unsigned is_dead:1; */ /* not used at the moment */ // 指向驱动为该事务分配的内核缓冲区,保存了进程间通讯数据 struct binder_buffer *buffer; unsigned int code; // 直接从进程间通讯数据中拷贝过来 unsigned int flags; // 直接从进程间通讯数据中拷贝过来 long priority; // 源线程优先级 // 线程在处理事务时,驱动会修改它的优先级以知足源线程和目标Service组建的要求。在修改之 // 前,会将它原来的线程优先级保存在该成员中,以便线程处理完该事务后能够恢复原来的优先级 long saved_priority; uid_t sender_euid; // 源线程用户ID };
在binder_transaction(...)第55行建立了struct binder_work,该结构体用于描述待处理的工做项,其定义在kernel/goldfish/drivers/staging/android/binder.c:205
struct binder_work { struct list_head entry; // 用来将该结构体嵌入到一个宿主结构中 // 描述工做项的类型,根据取值,Binder驱动程序就能够判断出一个binder_work结构体嵌入到 // 什么类型的宿主结构中 enum { BINDER_WORK_TRANSACTION = 1, BINDER_WORK_TRANSACTION_COMPLETE, BINDER_WORK_NODE, BINDER_WORK_DEAD_BINDER, BINDER_WORK_DEAD_BINDER_AND_CLEAR, BINDER_WORK_CLEAR_DEATH_NOTIFICATION, } type; };
到binder_transaction(...)第92行为止,它构造的数据结构以下。此时用户控件的部分数据被拷贝到了内核空间,内核空间中binder_transaction的buffer是从proc->free_buffers中摘取下来的,为了不图片过大,此处的细节暂不展示了。摘取下的buffer的数据部分用于暂存从用户空间拷贝来的数据。
从94行开始,逐个遍历t->buffer.data中的binder objects,在for循环中,fp指向当前的binder object。若是fp->type是BINDER_TYPE_BINDER或BINDER_TYPE_WEAK_BINDER,#104先从proc->nodes.rb_node中查找有没有fp->binder,若是没有则调用binder_new_node(...)在proc->nodes.rb_node中建立此节点。接下来先看看struct binder_node
,kernel/goldfish/drivers/staging/android/binder.c:217,它用来描述一个Binder实体对象,每个Service组件在驱动层都对应一个binder_node,用来描述在内核中的状态:
struct binder_node { int debug_id; // 帮助调试用的 // 当Binder实体对象的引用计数由0变为1或由1变为0时,Binder驱动程序就会请求相应的 // Service组件增长或减小其引用计数。Binder驱动程序就会将“该引用计数修改”封装成一个类 // 型为一个类型为binder_node的工做项,即将成员work的值置为BINDER_WORK_NODE,并将 // 它添加到相应进程的todo队列中等待处理 struct binder_work work; union { struct rb_node rb_node; struct hlist_node dead_node; }; // 指向宿主进程,宿主进程使用一个红黑树来维护它内部全部Binder实体对象,而每个 // Binder实体对象的成员变量rb_node就正好是这个红黑树的一个节点。若是Binder实体对象 // 的宿主进程已经死亡,那么该Binder实体对象就会经过它的成员变量dead_node保存在一个全 // 局的hash列表中。 struct binder_proc *proc; // 一个Binder实体对象可能会同时被多个Client组件引用,所以Binder驱动使用结构体 // binder_ref来描述这些引用关系,而且将引用了同一个Binder实体对象的全部引用都保存在 // 一个hash列表中。这个hash列表经过Binder实体对象的refs成员来描述,而Binder驱动通 // 过refs就能够知道有哪些Client组件引用了同一个Binder实体对象 struct hlist_head refs; int internal_strong_refs; // 描述Bidner实体对象的强引用计数 int local_weak_refs; // 描述Binder实体对象的弱引用计数 int local_strong_refs; // 描述Bidner实体对象的强引用计数 void __user *ptr; // 描述用户空间中的Service组件,指向Service的影子对象 void __user *cookie; // 描述用户空间中的Service组件的地址,指向Service的地址 // 当Binder实体对象请求Service执行某个操做时,会增长该Service的强/弱引用计数, // has_strong_ref和has_weak_ref被置1; // 当Service完成Binder所请求的操做后,会递减该Service的强/弱引用计数,has_strong_ref和has_weak_ref被置0; // Binder实体在请求Service增/减强/弱引用计数的过程当中,会将 // pending_strong_ref或pending_weak_ref置1; // 当Service完成增/减强/弱引用计数以后,会将这两个成员变量置为0。 unsigned has_strong_ref:1; unsigned pending_strong_ref:1; unsigned has_weak_ref:1; unsigned pending_weak_ref:1; // 描述Binder对象是否正在处理一个异步事务。Binder驱动程序将一个事务保存在todo队列中 // 表示将由该线程来处理该事务。每一个事务都关联着一个Binder实体对象,要求与该Binder实 // 体对象对应的Service组件在指定线程中处理该事务。然而,当Binder驱动发现一个事务是异 // 步事务时,就会将它保存在目标Binder实体对象的一个异步事务队列中,这个异步事务队列就 // 是由成员变量async_todo来描述的。异步事务的定义是那些单向的进程间通讯请求,即不需 // 要等待应答的进程间通讯请求,与此相对的是同步事务。由于不须要等待应答,Binder驱动就 // 认为异步事务优先级低于同步事务,具体表现为在同一时刻,一个Binder实体对象的全部异步 // 事务最多只有一个会获得处理,其他的都等待在异步事务队列中,而同步事务无此限制。 unsigned has_async_transaction:1; // 描述Binder实体对象是否能够接收包含有文件描述符的进程间通讯数据。1表示能够接收,0表 // 示禁止接收。当一个进程向另外一个进程发送的数据中包含有文件描述符时,Binder驱动程序就 // 会自动在目标进程中打开一个相同的文件。基于安全性考虑,Binder程序就要经过该变量防止 // 源进程在目标进程中打开文件。 unsigned accept_fds:1; // 表示Binder实体对象在处理来自Client进程的请求时,他所要求的处理线程(即Server进程 // 中的一个线程)应具有的最小线程优先级。 unsigned min_priority:8; struct list_head async_todo; };
接下来的binder_new_node(proc, fp->binder, fp->cookie)将申请一个struct binder_node
,在初始化中,将该节点挂到proc->nodes.rb_node中,并初始化部分红员,数据结构图以下:
struct binder_ref用来描述一个Binder引用对象,当客户端使用Binder实体时,在客户端保存的就是对该实体的引用,该结构体用来描述引用对象在内核中的状态。kernel/goldfish/drivers/staging/android/binder.c:246
struct binder_ref { /* Lookups needed: */ /* node + proc => ref (transaction) */ /* desc + proc => ref (transaction, inc/dec ref) */ /* node => refs + procs (proc exit) */ int debug_id; // 宿主进程使用两个红黑树来保存它内部全部Binder引用对象,分别以句柄值和对应的Binder // 实体对象地址来做为关键字保存这些引用对象,这两个rb_node_xxxx正是红黑树中的节点 struct rb_node rb_node_desc; struct rb_node rb_node_node; // 每一个Binder实体对象都有一个hash表保存引用了它的Binder引用对象,这些引用对象的成员 // node_entry就是该hash表的节点 struct hlist_node node_entry; struct binder_proc *proc; // 宿主进程 struct binder_node *node; // 描述Binder引用对象所引用的Binder实体对象 // 描述Binder引用对象的句柄值,驱动经过该句柄找到对应的Binder引用对象,而后再根据该 // Binder引用对象的成员node找到对应的Binder实体对象,而后就能够经过该实体对象找到要 // 访问的Service组件了。一个Binder引用对象的句柄值仅在进程范围内惟一,所以在两个不一样 // 进程中,同一个句柄可能表明不一样的Service组件 uint32_t desc; int strong; // 描述Binder引用对象的强/弱引用计数 int weak; // 指向一个Service组件的死亡接收通知。当Client进程向Binder驱动程序注册一个它所引用 // 的Service组件死亡接收通知时,Binder驱动程序会建立一个binder_ref_death结构体,然 // 后保存在该成员变量death中 struct binder_ref_death *death; };
接下来看binder_get_ref_for_node(target_proc, node)。须要注意,前面建立binder_node的时候,是为proc建立的,proc是在调用binder_open(...)时建立,用来描述“使用(打开)该binder的进程”,proc就藏在binder文件的文件描述符的私有数据中;而此处(第150行)参数使用的是target_proc,它表示当前的binder请求发向的目标进程,在本上下文中就是handle为0的service manager,即binder_context_mgr_node。
kernel/goldfish/drivers/staging/android/binder.c:1107
static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc, struct binder_node *node) { struct rb_node *n; struct rb_node **p = &proc->refs_by_node.rb_node; struct rb_node *parent = NULL; struct binder_ref *ref, *new_ref; // 在target_proc中查找node,若是找不到就建立 while (*p) { parent = *p; ref = rb_entry(parent, struct binder_ref, rb_node_node); if (node < ref->node) p = &(*p)->rb_left; else if (node > ref->node) p = &(*p)->rb_right; else return ref; } new_ref = kzalloc(sizeof(*ref), GFP_KERNEL); ... ... binder_stats_created(BINDER_STAT_REF); new_ref->debug_id = ++binder_last_id; new_ref->proc = proc; new_ref->node = node; rb_link_node(&new_ref->rb_node_node, parent, p); rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node); // 遍历target_proc的binder_ref,找到最大的desc,加1后赋给new_ref->desc new_ref->desc = (node == binder_context_mgr_node) ? 0 : 1; for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) { ref = rb_entry(n, struct binder_ref, rb_node_desc); if (ref->desc > new_ref->desc) break; new_ref->desc = ref->desc + 1; } // 将new_ref插入到target_proc->refs_by_desc.rb_node中 p = &proc->refs_by_desc.rb_node; while (*p) { parent = *p; ref = rb_entry(parent, struct binder_ref, rb_node_desc); if (new_ref->desc < ref->desc) p = &(*p)->rb_left; else if (new_ref->desc > ref->desc) p = &(*p)->rb_right; else BUG(); } rb_link_node(&new_ref->rb_node_desc, parent, p); rb_insert_color(&new_ref->rb_node_desc, &proc->refs_by_desc); if (node) { hlist_add_head(&new_ref->node_entry, &node->refs); ... ... } ... ... return new_ref; }
因而,在binder_transaction(...)函数第114行完成调用binder_get_ref_for_node(target_proc, node)
以后,数据结构图为:
接下来在函数binder_transaction(...)中还有几个关键操做,见第116行,若是fp->type为BINDER_TYPE_BINDER,就改成BINDER_TYPE_HANDLE,而后把fp->handle改成ref->desc,接下来的binder_ref_ref(ref, fp->type==BINDER_TYPE_HANDLE, &thread->todo)定义在kernel/goldfish/drivers/staging/android/binder.c:1200
static int binder_inc_ref(struct binder_ref *ref, int strong, struct list_head *target_list) { // strong = (fp->type==BINDER_TYPE_HANDLE)即为1 // target_list = &thread->todo int ret; if (strong) { if (ref->strong == 0) { // ref->node->internal_strong_ref++,成功返回0 ret = binder_inc_node(ref->node, 1, 1, target_list); if (ret) return ret; } ref->strong++; } else { if (ref->weak == 0) { ret = binder_inc_node(ref->node, 0, 1, target_list); if (ret) return ret; } ref->weak++; } return 0; }
接下来跳出case后还有对t的成员need_reply、from_parent、t->work.type的处理,并将t插入到target_list即target_proc或target_thread的todo队列中,尔后返回。此时的数据结构图为:
到此为止,终于完成了binder_transaction(...)的分析,知道怎么回事,但内心有不少个“为何”。并且把前面的学习笔记串联起来,隐约以为能感应到一些曙光了,本节的篇幅太长了,这些曙光留待下一节一块儿领略吧。