在回答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(...)的分析,知道怎麼回事,但內心有不少個「爲何」。並且把前面的學習筆記串聯起來,隱約以爲能感應到一些曙光了,本節的篇幅太長了,這些曙光留待下一節一塊兒領略吧。