在源碼分析(三),數據最終由ioctl調用交由驅動處理。android源碼不包括驅動代碼,驅動代碼需要另外下載。網上有很多下載驅動代碼的方法。如果嫌麻煩可以直接在github隨便找一個手機品牌的驅動代碼下載。
其實驅動層是獨立的一層,不應該歸於native層,但爲了讓對Binder底層實現的整個分析過程更爲連續,這裏就不換標題了。
在分析驅動層的數據傳輸過程之前,有必要了解一下Binder驅動的打開和映射操作。這兩個操作爲Binder通信做了數據結構和內存空間上的準備,所以至少要知道里面一些重要的數據結構和爲Binder通信分配的內核緩衝區的概念。
Binder驅動的註冊與打開(可跳過)
Binder被android系統註冊成misc device類型的驅動,這種類型的驅動主設備號統一爲10,次設備號爲每種設備獨有,且只需調用misc_register()就可完成註冊:
//\drivers\android\binder.c
static int __init init_binder_device(const char *name)
{
int ret;
struct binder_device *binder_device;
//...
binder_device->miscdev.fops = &binder_fops;//文件操作
binder_device->miscdev.minor = MISC_DYNAMIC_MINOR;
binder_device->miscdev.name = name;
//...
ret = misc_register(&binder_device->miscdev);//註冊
/...
}
fops即file operations,指該驅動支持的文件操作,如下:
static const struct file_operations binder_fops = {
.owner = THIS_MODULE,
.poll = binder_poll,
.unlocked_ioctl = binder_ioctl,
.compat_ioctl = binder_ioctl,
.mmap = binder_mmap,
.open = binder_open,
.flush = binder_flush,
.release = binder_release,
};
其中最常用的就是ioctl,mmap和open。
binder_open用於打開驅動,進程在訪問Binder驅動時,首先得先打開/dev/binder節點:
static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc;
struct binder_device *binder_dev;
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
if (proc == NULL)
return -ENOMEM;
spin_lock_init(&proc->inner_lock);
spin_lock_init(&proc->outer_lock);
get_task_struct(current->group_leader);
proc->tsk = current->group_leader;
mutex_init(&proc->files_lock);
INIT_LIST_HEAD(&proc->todo);//todo鏈表,裏面存放待處理的Binder通信工作項
if (binder_supported_policy(current->policy)) {
proc->default_priority.sched_policy = current->policy;
proc->default_priority.prio = current->normal_prio;
} else {
proc->default_priority.sched_policy = SCHED_NORMAL;
proc->default_priority.prio = NICE_TO_PRIO(0);
}
binder_dev = container_of(filp->private_data, struct binder_device,
miscdev);
proc->context = &binder_dev->context;
binder_alloc_init(&proc->alloc);
binder_stats_created(BINDER_STAT_PROC);
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->delivered_death);
INIT_LIST_HEAD(&proc->waiting_threads);//等待鏈表
filp->private_data = proc;//放到filp中
mutex_lock(&binder_procs_lock);//上鎖
hlist_add_head(&proc->proc_node, &binder_procs);//放到binder_proc隊列頭部
mutex_unlock(&binder_procs_lock);
//...後面是debug代碼,略去
return 0;
}
上面代碼主要完成的工作就是創建並初始化binder_proc proc,它代表了一個正在使用Binder的進程,存儲了Binder分配給該進程的內核空間等重要信息。
binder_mmap
mmap即Memory Map,可以將某個設備或者文件映射到進程的內存空間中,方便直接對文件/設備進行修改。
binder_mmap是在內核空間爲本進程分配緩衝區,同時在用戶空間中映射該緩衝區。之後,其它進程就可以在內核態可以訪問該緩衝區,進行數據的拷貝。
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
//vm_area_struct vma描述一塊應用程序使用的虛擬內存(應用空間)
int ret;
struct binder_proc *proc = filp->private_data;//取得binder_proc實體
const char *failure_string;
if (proc->tsk != current->group_leader)
return -EINVAL;
if ((vma->vm_end - vma->vm_start) > SZ_4M)
vma->vm_end = vma->vm_start + SZ_4M;//最大分配4M
//...
ret = binder_alloc_mmap_handler(&proc->alloc, vma);
if (ret)
return ret;
mutex_lock(&proc->files_lock);
proc->files = get_files_struct(current);
mutex_unlock(&proc->files_lock);
return 0;
//...
}
vma中記錄的一塊用戶虛擬空間,它被傳遞給了binder_alloc_mmap_handler,該函數的作用是分配一塊內核虛擬空間,該內核虛擬空間和用戶虛擬空間對應同一塊物理空間。
//\drivers\android\binder_alloc.c
int binder_alloc_mmap_handler(struct binder_alloc *alloc,
struct vm_area_struct *vma)
{
int ret;
struct vm_struct *area;//內核中對虛擬內存的描述(內核空間)
const char *failure_string;
struct binder_buffer *buffer;
mutex_lock(&binder_alloc_mmap_lock);
if (alloc->buffer) {//如果已經映射
ret = -EBUSY;
failure_string = "already mapped";
goto err_already_mapped;
}
//爲Binder獲取可用的虛擬空間(此時還未分配物理空間??)
//注意vma是應用程序的虛擬空間,area則是內核虛擬空間
area = get_vm_area(vma->vm_end - vma->vm_start, VM_ALLOC);
if (area == NULL) {
ret = -ENOMEM;
failure_string = "get_vm_area";
goto err_get_vm_area_failed;
}
alloc->buffer = area->addr;//buffer指針指向該內核虛擬空間
alloc->user_buffer_offset =
vma->vm_start - (uintptr_t)alloc->buffer;
mutex_unlock(&binder_alloc_mmap_lock);
//...
//pages指向物理頁面
alloc->pages = kzalloc(sizeof(alloc->pages[0]) *
((vma->vm_end - vma->vm_start) / PAGE_SIZE),
GFP_KERNEL);
if (alloc->pages == NULL) {
ret = -ENOMEM;
failure_string = "alloc page array";
goto err_alloc_pages_failed;
}
alloc->buffer_size = vma->vm_end - vma->vm_start;
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
if (!buffer) {
ret = -ENOMEM;
failure_string = "alloc buffer struct";
goto err_alloc_buf_struct_failed;
}
buffer->data = alloc->buffer;//buffer指向一整塊內核緩衝區
list_add(&buffer->entry, &alloc->buffers);//將內存塊放到buffers鏈表中
buffer->free = 1;//此內存可用
binder_insert_free_buffer(alloc, buffer);//將該buffer插入到alloc的空閒紅黑樹中。
alloc->free_async_space = alloc->buffer_size / 2;
binder_alloc_set_vma(alloc, vma);
mmgrab(alloc->vma_vm_mm);
return 0;
//...
}
上面的代碼後半部分有好幾個buffer(s),要讀懂這部分代碼幹了什麼首先得理清代碼中幾個buffer(s)的含義。先看binder_alloc結構體中的buffer:
binder_alloc與linux中特殊的鏈表寫法
struct binder_alloc {
struct mutex mutex;
struct vm_area_struct *vma;//用戶地址空間
struct mm_struct *vma_vm_mm;
void *buffer;//內核緩衝區的地址
ptrdiff_t user_buffer_offset;
//將buffer指向的地址塊分成若干小塊,每一小塊用binder_buffer結構體記錄,
struct list_head buffers;
//free_buffers和allocated_buffers是空閒小塊和已分配小塊的紅黑樹。
struct rb_root free_buffers;
struct rb_root allocated_buffers;
size_t free_async_space;
struct binder_lru_page *pages;
size_t buffer_size;
uint32_t buffer_free;//空閒緩衝區大小
int pid;
size_t pages_high;
};
需要注意的是,儘管buffers的類型爲list_head,free_buffers和allocated_buffers的類型爲rb_root,但它們都可以理解爲以binder_buffer爲元素的數據結構。這是linux代碼中一種特殊的鏈表寫法。我們看完binder_buffer的定義就清楚是怎麼一回事了:
struct binder_buffer {
struct list_head entry; /* free and allocated entries by address */
struct rb_node rb_node; /* free entry by size or allocated entry */
/* by address */
unsigned free:1;
unsigned allow_user_free:1;
unsigned async_transaction:1;
unsigned debug_id:29;
struct binder_transaction *transaction;//正在使用該buffer的事務
struct binder_node *target_node;//正在使用該buffer的Binder實體對象
size_t data_size;//該buffer的大小
size_t offsets_size;
size_t extra_buffers_size;
void *data;
};
可以看到,entry的類型是list_head,和buffers的類型一樣。實際上,buffers中的元素是binder_buffer.entry,但由於entry是binder_buffer的成員變量,它們的地址偏移是固定的,一旦得到了entry的地址,也就可以算出binder_buffer的地址。所以爲了便於理解,我們邏輯上可以把buffers當成以binder_buffer爲元素的鏈表。free_buffers和allocated_buffers也同理,可以把它們當作元素爲binder_buffer的紅黑樹。
再回到binder_alloc_mmap_handler(),其實該函數就是在內核分配了一塊緩衝空間,令alloc->buffer指向該空間的地址,再創建了一個binder_buffer結構體,然後令這個結構體描述這整塊內核空間(初始情況下沒有事務使用緩衝,因此整塊空間都是空閒的),並且插入到alloc的buffers和free_buffers中去。
binder_mmap總結
總結一下binder_mmap乾的事情,binder_mmap分別在內核虛擬空間中開闢了一塊空間,並且該內核空間和傳遞進來的用戶空間對應一塊物理空間,如圖:
binder_proc->alloc->buffer中放的是內核地址,mmap返回的是用戶空間地址。兩者經過地址轉換後指向同一個物理地址。
由於內核空間共享,如果在另一進程的內核態中取得了本進程的binder_proc,就可以得到本進程的binder_mmap分配的地址空間,這就實現了空間的共享,然後就可以向該空間複製數據,只需一次複製就可以實現數據的傳遞。如下圖(圖來自深入理解android內核設計思想)
binder_ioctl
在talkWithDriver中以如下方式調用了ioctl
ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)
我們看ioctl具體是做了什麼(分段閱讀):
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
struct binder_proc *proc = filp->private_data;//取出binder_proc
struct binder_thread *thread;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;//即bwr
//...
trace_binder_ioctl(cmd, arg);
//掛起進程,等待喚醒
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret)
goto err_unlocked;
thread = binder_get_thread(proc);//從proc中找到(新建)一個線程
if (thread == NULL) {
ret = -ENOMEM;
goto err;
}
其中wait_event_interruptible函數是進程調度中掛起進程的實現方法。
在wait_event_interruptible中,會判斷condition是否滿足,若不滿足,則進入死循環,在循環中首先將自己設爲可中斷掛起狀態,然後進行schedule()調度(我以前一直搞不清楚進程切換的代碼是否屬於該進程,其實代碼就在內核空間中,而內核空間被所有進程共享)。當該進程被喚醒後,會再進行condition判斷,若依舊不滿足,則繼續中斷掛起。
後面就開始處理命令了,這裏只看talkWithDriver中使用的命令BINDER_WRITE_READ
switch (cmd) {
case BINDER_WRITE_READ:
ret = binder_ioctl_write_read(filp, cmd, arg, thread);
if (ret)
goto err;
break;
交給binder_ioctl_write_read處理。
binder_ioctl_write_read
static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread)
{
int ret = 0;
struct binder_proc *proc = filp->private_data;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
struct binder_write_read bwr;
if (size != sizeof(struct binder_write_read)) {
ret = -EINVAL;
goto out;
}
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
copy_from_user表示從用戶空間中複製bwr。talkWithDriver構建的bwr位於用戶空間,而ioctl位於內核空間,所以首先得先從將bwr複製到內核空間。
函數剩餘部分如下:
if (bwr.write_size > 0) {
//binder_thread_write完成寫
ret = binder_thread_write(proc, thread,
bwr.write_buffer,
bwr.write_size,
&bwr.write_consumed);
trace_binder_write_done(ret);
if (ret < 0) {//發生錯誤
bwr.read_consumed = 0;
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
if (bwr.read_size > 0) {
//binder_thread_read完成讀
ret = binder_thread_read(proc, thread, bwr.read_buffer,
bwr.read_size,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
trace_binder_read_done(ret);
binder_inner_proc_lock(proc);
if (!binder_worklist_empty_ilocked(&proc->todo))
binder_wakeup_proc_ilocked(proc);
binder_inner_proc_unlock(proc);
if (ret < 0) {
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
//...
}
根據bwr的讀寫大小判斷是否需要讀寫。讀寫由binder_thread_write/read完成。注意這裏它將bwr.*_consumed的地址傳進了函數。在源碼分析三中,我們知道bwr記錄的是mIn和mOut的信息,而mOut中放在我們要傳輸的數據,故write_size必不爲0,所以會進入binder_thread_write函數。
binder_thread_write
我們只看該函數是怎麼處理BC_TRANSACTION協議的。
/*binder_thread_write(proc, thread,
bwr.write_buffer,//mOut.data
bwr.write_size,
&bwr.write_consumed);*/
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)
{
uint32_t cmd;
struct binder_context *context = proc->context;
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
while (ptr < end && thread->return_error.cmd == BR_OK) {
int ret;
if (get_user(cmd, (uint32_t __user *)ptr))//讀出協議,即BC_TRANSACTION
return -EFAULT;
ptr += sizeof(uint32_t);//更新讀寫指針
trace_binder_command(cmd);
if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
atomic_inc(&binder_stats.bc[_IOC_NR(cmd)]);
atomic_inc(&proc->stats.bc[_IOC_NR(cmd)]);
atomic_inc(&thread->stats.bc[_IOC_NR(cmd)]);
}
switch (cmd) {
case BC_TRANSACTION:
case BC_REPLY: {
struct binder_transaction_data tr;
if (copy_from_user(&tr, ptr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
binder_transaction(proc, thread, &tr,
cmd == BC_REPLY, 0);
//...
Binder數據的構成如下,這裏即根據bwr的指針信息從mOut中讀出了tr,然後交給了binder_transaction()處理。
binder_transaction分段1
繼續看binder_transaction函數,這個函數非常長,處理了BC_REPLY和BC_TRANSACTION。在源碼分析(二)中我們知道,傳輸的協議爲BC_TRANSACTION,handle值爲0,據此截取部分代碼如下(分段閱讀)。
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply,
binder_size_t extra_buffers_size)
{
//...
if (reply) {//...}//協議爲BC_REPLY的分支
else {//協議爲BC_TRANSACTION的分支,進入該分支
if (tr->target.handle) {//...}//當handle值不爲0的分支
else {//當handle==0,即目標進程是service_manage時進入這個分支
mutex_lock(&context->context_mgr_node_lock);
target_node = context->binder_context_mgr_node;//直接從全局變量中獲取sm進程的binder_node
if (target_node)
//從binder_node中得到目標進程的binder_proc
target_node = binder_get_node_refs_for_txn(
target_node, &target_proc,
&return_error);
else
return_error = BR_DEAD_REPLY;
mutex_unlock(&context->context_mgr_node_lock);
//...
}
上面的代碼從全局環境中取得service manager的binder_node和binder_proc。繼續往下看:
t = kzalloc(sizeof(*t), GFP_KERNEL);//生成一個binder_transaction對象
tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);//生成biner_work對象
//...
if (!reply && !(tr->flags & TF_ONE_WAY))
t->from = thread;//from等於本線程
else
t->from = NULL;
t->sender_euid = task_euid(proc->tsk);
t->to_proc = target_proc;//sm的binder_proc
t->to_thread = target_thread;//to等於目標線程,爲NULL
t->code = tr->code;//記錄code(請求碼)
t->flags = tr->flags;
//...
//從目標進程的通信空間中分配一塊空間用於本次通信。關於binder_buffer類型見mmap
t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size,
tr->offsets_size, extra_buffers_size,
!reply && (t->flags & TF_ONE_WAY));
//...
t->buffer->debug_id = t->debug_id;
t->buffer->transaction = t;
t->buffer->target_node = target_node;//目標結點。
trace_binder_transaction_alloc_buf(t->buffer);
這裏創建了一個binder_transaction對象指針t,從類型名來看這個對象應該就是傳輸的事務(消息)。然後創建了binder_work對象指針tcomplete,用於通知t傳輸完畢。
從後面的代碼來看,binder_transaction裏也有一個binder_work類型的成員,這個binder_work的指針會被放在目標線程的todo鏈表,而tcomplete會被放在本線程的todo鏈表。也就是說,binder_work代表一個Binder工作內容,線程會從鏈表從取出該變量並根據其類型來完成工作。
t->work的類型爲BINDER_WORK_TRANSACTION,binder_thread_read在遇到這種類型時會根據binder_work在binder_transaction中的偏移得到bnder_transaction的地址,即得到t,這樣就完成了事務的傳輸。
後面的代碼就是對填寫事務t,注意其中的buffer就是前面mmap中的那塊buffer。繼續往下看:
if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
tr->data.ptr.buffer, tr->data_size)) {
//...
}
//複製object,由Parcel數據結構知offset中存儲的是offset。
if (copy_from_user(offp, (const void __user *)(uintptr_t)
tr->data.ptr.offsets, tr->offsets_size)) {
//...
}
申請完內存後,將數據和object複製。由於buffer所指向的內存空間是共享的,因此只需要一次複製,如圖(圖來自深入理解android內核設計思想):
在上圖中,binder_proc指的都是進程A的binder_proc,由於內核空間共享,所以在進程B的內核態可以取得進程A的binder_binder_proc,也就可以直接向進程A的空間複製數據。
之後一個很長的for循環是對object進行處理,在源碼分析(二)對Parcel的分析中我們知道,傳輸的object的類型是BINDER_TYPE_BINDER,我們看驅動是怎麼處理該類型的object的:
for (; offp < off_end; offp++) {
struct binder_object_header *hdr;
size_t object_size = binder_validate_object(t->buffer, *offp);
//...
/* struct binder_object_header {
__u32 type;
};*/ //其實就是指flat_binder_object的第一個成員type
hdr = (struct binder_object_header *)(t->buffer->data + *offp);
off_min = *offp + object_size;
switch (hdr->type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
struct flat_binder_object *fp;
fp = to_flat_binder_object(hdr);//從數據中恢復flat_binder_object
ret = binder_translate_binder(fp, t, thread);
if (ret < 0) {
return_error = BR_FAILED_REPLY;
return_error_param = ret;
return_error_line = __LINE__;
goto err_translate_failed;
}
} break;
//...
取得flat_binder_object後,傳給了binder_translate_binder處理。
binder_translate_binder分段1:創建(找到)obj對應binder_node
static int binder_translate_binder(struct flat_binder_object *fp,
struct binder_transaction *t,
struct binder_thread *thread)
{
struct binder_node *node;
struct binder_proc *proc = thread->proc;
struct binder_proc *target_proc = t->to_proc;//即sm的binder_proc
struct binder_ref_data rdata;
int ret = 0;
//從sm的binder_proc的紅黑樹中找binder_node
//由於MediaPlayerService是新建的,所以在binder_proc中找不到該binder_node
node = binder_get_node(proc, fp->binder);
if (!node) {
//創建一個新binder_node,並插入到本進程的binder_proc的紅黑樹中
node = binder_new_node(proc, fp);
if (!node)
return -ENOMEM;
}
if (fp->cookie != node->cookie) {
binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n",
proc->pid, thread->pid, (u64)fp->binder,
node->debug_id, (u64)fp->cookie,
(u64)node->cookie);
ret = -EINVAL;
goto done;
}
if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
ret = -EPERM;
goto done;
}
根據傳遞的flat_binder_obj創建了一個新的binder_node,我們看是怎麼創建的。
binder_get_node和binder_new_node
binder_get_node中調用了binder_get_node_ilocked
//ptr是flat_binder_obj->binder,即MediaPlayerService的weakref_type
//proc是指本進程的binder_proc,也就是服務進程的binder_proc。
static struct binder_node *binder_get_node_ilocked(struct binder_proc *proc,
binder_uintptr_t ptr)
{
struct rb_node *n = proc->nodes.rb_node;
struct binder_node *node;
assert_spin_locked(&proc->inner_lock);
while (n) {
node = rb_entry(n, struct binder_node, rb_node);
//binder_proc是以服務類的弱引用指針作爲鍵值來建樹的
if (ptr < node->ptr)
n = n->rb_left;
else if (ptr > node->ptr)
n = n->rb_right;
else {
/*
* take an implicit weak reference
* to ensure node stays alive until
* call to binder_put_node()
*/
binder_inc_node_tmpref_ilocked(node);
return node;
}
}
return NULL;
}
在源碼分析二中分析writeStrongBinder時我們知道,flat_binder_obj的binder成員存儲的是MediaPlayerService的弱引用指針。上面的代碼就是將該弱引用指針作爲鍵值來在紅黑樹中進行查找。由於是第一次查找,所以肯定找不到,返回NULL。
binder_new_node中創建了一個binder_node類型交給binder_init_node_ilocked進行初始化。
static struct binder_node *binder_init_node_ilocked(
struct binder_proc *proc,
struct binder_node *new_node,
struct flat_binder_object *fp)
{
struct rb_node **p = &proc->nodes.rb_node;
struct rb_node *parent = NULL;
struct binder_node *node;
//binder即MediaPlayerService的弱引用類型
binder_uintptr_t ptr = fp ? fp->binder : 0;
//cookie即MediaPlayerService的指針
binder_uintptr_t cookie = fp ? fp->cookie : 0;
__u32 flags = fp ? fp->flags : 0;
s8 priority;
assert_spin_locked(&proc->inner_lock);
//從proc的紅黑樹中查找是否以及存在該binder_node,若存在則直接返回
//前面已經看過查找的代碼了,所以跳過這部分
while (*p) {
//...
}
//proc的紅黑樹並不存在該node,此時對new_node進行初始化。
node = new_node;
binder_stats_created(BINDER_STAT_NODE);
node->tmp_refs++;
//插入到proc的紅黑樹中
rb_link_node(&node->rb_node, parent, p);
rb_insert_color(&node->rb_node, &proc->nodes);
node->debug_id = atomic_inc_return(&binder_last_id);
node->proc = proc;//等於本binder_proc
node->ptr = ptr;//弱引用指針
node->cookie = cookie;//服務類
node->work.type = BINDER_WORK_NODE;
priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
node->sched_policy = (flags & FLAT_BINDER_FLAG_SCHED_POLICY_MASK) >>
FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT;
node->min_priority = to_kernel_prio(node->sched_policy, priority);
node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
node->inherit_rt = !!(flags & FLAT_BINDER_FLAG_INHERIT_RT);
spin_lock_init(&node->lock);
INIT_LIST_HEAD(&node->work.entry);
INIT_LIST_HEAD(&node->async_todo);
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
"%d:%d node %d u%016llx c%016llx created\n",
proc->pid, current->pid, node->debug_id,
(u64)node->ptr, (u64)node->cookie);
return node;
}
對new_node進行了初始化,其中將flat_binder_obj的binder和cookie(即服務類的弱引用指針和服務類指針)賦給了binder_node的ptr和cookie,該node會被插入到本進程(服務進程)的binder_proc
binder_translate_binder分段2:修改obj
創建完binder_node之後並插入到本進程的binder_proc中後,我們看 binder_translate_binder還幹了什麼
//rdata的地址被傳進了該函數,該函數會對rdata進行賦值
//注意傳入的是sm的binder_proc!!!
ret = binder_inc_ref_for_node(target_proc, node,
fp->hdr.type == BINDER_TYPE_BINDER,
&thread->todo, &rdata);
if (ret)
goto done;
if (fp->hdr.type == BINDER_TYPE_BINDER)
fp->hdr.type = BINDER_TYPE_HANDLE;//注意這裏,type被換成了HANDLE
else
fp->hdr.type = BINDER_TYPE_WEAK_HANDLE;
fp->binder = 0;
fp->handle = rdata.desc;
fp->cookie = 0;
trace_binder_transaction_node_to_ref(t, node, &rdata);
binder_debug(BINDER_DEBUG_TRANSACTION,
" node %d u%016llx -> ref %d desc %d\n",
node->debug_id, (u64)node->ptr,
rdata.debug_id, rdata.desc);
done:
binder_put_node(node);
return ret;
}
總結一下,當傳遞的是BINDER_TYPE_BINDER類型的object時,驅動會將該object的類型換成BINDER_TYPE_HANDLE,並令其handle成員值等於rdata.desc。
而rdata在函數binder_inc_ref_for_node中被賦值,我們看其究竟是什麼。
binder_inc_ref_for_node:增加binder_node引用計數
注意傳入的是sm的binder_proc。
/*binder_inc_ref_for_node(target_proc, node,
fp->hdr.type == BINDER_TYPE_BINDER,
&thread->todo, &rdata);*/
static int binder_inc_ref_for_node(struct binder_proc *proc,
struct binder_node *node,
bool strong,
struct list_head *target_list,
struct binder_ref_data *rdata)
{
struct binder_ref *ref;
struct binder_ref *new_ref = NULL;
int ret = 0;
binder_proc_lock(proc);
//根據binder_node在sm的binder_proc中找binder_ref
//由於MediaPlayerService時新建的,所以sm的binder_proc中找不到該binder_ref
ref = binder_get_ref_for_node_olocked(proc, node, NULL);
if (!ref) {//若找不到,則新建一個binder_ref並插入binder_proc
binder_proc_unlock(proc);
new_ref = kzalloc(sizeof(*ref), GFP_KERNEL);
if (!new_ref)
return -ENOMEM;
binder_proc_lock(proc);
//將創建的binder_ref插入到sm的binder_proc中並返回。
ref = binder_get_ref_for_node_olocked(proc, node, new_ref);
}
//見下
ret = binder_inc_ref_olocked(ref, strong, target_list);
*rdata = ref->data;//rdata等於binder_ref的data
binder_proc_unlock(proc);
if (new_ref && ref != new_ref)
/*
* Another thread created the ref first so
* free the one we allocated
*/
kfree(new_ref);
return ret;
}
rdata其實就是binder_ref的data成員變量,而binder_ref在binder_get_ref_for_node_olocked中被初始化。在創建完binder_ref後,會增加binder_ref的引用值。我們先看binder_ref的初始化過程。
binder_get_ref_for_node_olocked爲binder_node創建引用
static struct binder_ref *binder_get_ref_for_node_olocked(
struct binder_proc *proc,
struct binder_node *node,
struct binder_ref *new_ref)
{
struct binder_context *context = proc->context;
struct rb_node **p = &proc->refs_by_node.rb_node;
struct rb_node *parent = NULL;
struct binder_ref *ref;
struct rb_node *n;
//在binder_proc中找binder_ref
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;
}
if (!new_ref)
return NULL;
//若找不到,則初始化new_ref並插入binder_proc的refs_by_node紅黑樹中
binder_stats_created(BINDER_STAT_REF);
new_ref->data.debug_id = atomic_inc_return(&binder_last_id);
//賦值node和proc
new_ref->proc = proc;
new_ref->node = node;
//將新的binder_ref插入到binder_proc的refs_by_node紅黑樹中
rb_link_node(&new_ref->rb_node_node, parent, p);
rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node);
//從這裏可以看出service manager的desc(handle)爲0
new_ref->data.desc = (node == context->binder_context_mgr_node) ? 0 : 1;
//我們傳遞的MediaPlayerService非service manager,所以這裏找一個空閒的值作爲
//其desc(handle),而前面代碼中賦給flat_binder_object的handle值就是該desc。
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->data.desc > new_ref->data.desc)
break;
new_ref->data.desc = ref->data.desc + 1;
}
//從binder_proc的refs_by_desc紅黑樹中找到一個合適的位置將新binder_ref插入
p = &proc->refs_by_desc.rb_node;
while (*p) {
parent = *p;
ref = rb_entry(parent, struct binder_ref, rb_node_desc);
if (new_ref->data.desc < ref->data.desc)
p = &(*p)->rb_left;
else if (new_ref->data.desc > ref->data.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);
binder_node_lock(node);
//插入到binder_node的紅黑樹中
hlist_add_head(&new_ref->node_entry, &node->refs);
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
"%d new ref %d desc %d for node %d\n",
proc->pid, new_ref->data.debug_id, new_ref->data.desc,
node->debug_id);
binder_node_unlock(node);
return new_ref;
}
也就是說,驅動爲binder_node創建了一個binder_ref引用,並插入到service_manager的binder_proc的兩顆紅黑樹中,並且賦予該binder_ref一個非重複的空閒值作爲desc(binder_ref.data.desc)。該值作爲binder_ref在binder_proc紅黑樹中的鍵值。前面賦給flat_binder_obj的handle值就是指該desc。
以後,就可以通過該鍵值,在sm的binder_proc中找到相應的binder_ref,進而找到相應的binder_node。
binder_inc_ref_olocked增加引用
static int binder_inc_ref_olocked(struct binder_ref *ref, int strong,
struct list_head *target_list)
{
int ret;
if (strong) {//進入該分支
if (ref->data.strong == 0) {//進入該分支
//相當於node->internal_strong_refs++
ret = binder_inc_node(ref->node, 1, 1, target_list);
if (ret)
return ret;
}
ref->data.strong++;//ref中data的強引用也加一
} else {
if (ref->data.weak == 0) {
ret = binder_inc_node(ref->node, 0, 1, target_list);
if (ret)
return ret;
}
ref->data.weak++;
}
return 0;
}
同時增加了binder_node和binder_ref的引用成員。
binder_proc,binder_node,binder_ref與handle總結
前面分析代碼的過程中大量涉及到這三個數據結構,這裏對它們做一個介紹。
首先是binder_proc,在Binder驅動的註冊與打開中我們知道每個打開binder驅動的進程都會有一個binder_proc結構體。其中binder_proc的幾顆紅黑樹描述了客戶端和服務端之間的關係。
struct binder_proc {
struct hlist_node proc_node;//該binder_proc在全局hash表中的結點。
struct rb_root threads;//處理Binder通信的線程池。
struct rb_root nodes;//以binder_node爲結點的紅黑樹
struct rb_root refs_by_desc;//以binder_ref的desc值爲鍵值的紅黑樹
struct rb_root refs_by_node;//以binder_ref的node爲鍵值的紅黑樹。
struct list_head waiting_threads;
int pid;
struct task_struct *tsk;// 指向進程任務控制塊
struct files_struct *files;// 指向進程打開文件結構體數組
struct mutex files_lock;
struct hlist_node deferred_work_node;
int deferred_work;
bool is_dead;
struct list_head todo; // 工作項,每個工作項代表一個通信請求
struct binder_stats stats;
struct list_head delivered_death;
int max_threads;
int requested_threads;
int requested_threads_started;
int tmp_ref;
struct binder_priority default_priority;
struct dentry *debugfs_entry;
struct binder_alloc alloc;//用來管理內核緩衝
struct binder_context *context;
spinlock_t inner_lock;
spinlock_t outer_lock;
};
binder_node描述一個Binder實體對象,每一個Service組件在驅動層都對應一個binder_node。
struct binder_node {
int debug_id;
spinlock_t lock;
struct binder_work work;
union {
struct rb_node rb_node; //該binder_node在binder_proc的紅黑樹中的結點
struct hlist_node dead_node;
};
struct binder_proc *proc;//指向宿主進程的binder_proc
struct hlist_head refs;//元素爲binder_ref的紅黑樹。
//引用計數
int internal_strong_refs;
int local_weak_refs;
int local_strong_refs;
int tmp_refs;
binder_uintptr_t ptr;// 描述用戶空間中的Service組件,指向Service的影子對象???
binder_uintptr_t cookie;// 描述用戶空間中的Service組件的地址,指向Service的地址
struct {
/*
* bitfield elements protected by
* proc inner_lock
*/
u8 has_strong_ref:1;
u8 pending_strong_ref:1;
u8 has_weak_ref:1;
u8 pending_weak_ref:1;
};
struct {
/*
* invariant after initialization
*/
u8 sched_policy:2;
u8 inherit_rt:1;
u8 accept_fds:1;
u8 min_priority;
};
bool has_async_transaction;
struct list_head async_todo;
};
一個Binder實體對象可能會同時被多個Client組件引用,Binder驅動使用結構體binder_ref來描述這些引用關係。
struct binder_ref {
/* Lookups needed: */
/* node + proc => ref (transaction) */
/* desc + proc => ref (transaction, inc/dec ref) */
/* node => refs + procs (proc exit) */
struct binder_ref_data data;//記錄了desc和引用數等信息
//以下兩個成員是binder_proc中的紅黑樹結點
struct rb_node rb_node_desc;//以desc爲關鍵字構造的紅黑樹結點
struct rb_node rb_node_node;//以binder_node爲關鍵字構造的紅黑樹結點。
struct hlist_node node_entry;
struct binder_proc *proc;
struct binder_node *node;//所引用的binder_node
struct binder_ref_death *death;
};
從前面的代碼分析可知它們的關係如下:
注意binder_ref和binder_node是以紅黑樹結點的形式放於binder_proc中,上圖是爲了便於理解才這樣畫的。而handle值的作用在於從binder_proc的紅黑樹中找到該binder_ref,進而得到binder_node。
binder_transaction分段2
弄清楚binder_proc,binder_node和binder_ref的關係後,我們繼續看binder_transaction的代碼
//t->work和tcomplete一樣也是binder_transaction,記住下面它們的類型,待會分析binder_thread_read時會用到。
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
t->work.type = BINDER_WORK_TRANSACTION;
if (reply) {...}
else if (!(t->flags & TF_ONE_WAY)) {
BUG_ON(t->buffer->async_transaction != 0);
binder_inner_proc_lock(proc);
//把tcomplete加入本線程的todo隊列中
binder_enqueue_deferred_thread_work_ilocked(thread, tcomplete);
t->need_reply = 1;
t->from_parent = thread->transaction_stack;
thread->transaction_stack = t;//本線程的transaction_stack等於t
binder_inner_proc_unlock(proc);
//binder_proc_transaction把t(t->work)放到對方線程的todo隊列中。
if (!binder_proc_transaction(t, target_proc, target_thread)) {
binder_inner_proc_lock(proc);
binder_pop_transaction_ilocked(thread, t);
binder_inner_proc_unlock(proc);
goto err_dead_proc_or_thread;
}
} else {...}
//...
最後就是設置t->work和tcomplete的類型,並將其加入對方線程和自己線程的todo隊列。
到這裏binder_thread_write完成了對BC_TRANSACTION協議的處理,已經將事務(消息)放到了sm的todo隊列中。下一篇博文將介紹service_manager怎麼接收和處理事務。