在源码分析(三),数据最终由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怎么接收和处理事务。