Service与Android系统设计
来源:互联网 发布:深圳开网络出租屋 编辑:程序博客网 时间:2024/06/03 05:43
转载自:http://www.tuicool.com/articles/uUNZJ3
特别声明:本系列文章LiAnLab.org著作权所有,转载请注明出处。作者系 LiAnLab.org 资深Android技术顾问吴赫老师。本系列文章交流与讨论:@ 宋宝华Barry
5.6 Binder驱动的读写接口 -- binder_thread_write()与binder_thread_read()
Binder驱动在Binder IPC过程中的作用容易明了,它只是一种中间容器,把一个进程的请求,转发给另一个进程。出于这样的设计,binder_thread_write()的实现相对来说就比较简单,只是通过解析binder_ioctl()得到的BC_*命令,然后再根据命令作出处理。
int binder_thread_write( struct binder_proc *proc, struct binder_thread *thread,
void __user *buffer, int size, signed long *consumed)
{
uint32_tcmd;
void __user *ptr =buffer + *consumed;
void __user *end =buffer + size;
while (ptr < end&& thread->return_error == BR_OK) {
if (get_user(cmd,(uint32_t __user *)ptr))
return -EFAULT;
switch (cmd) {
case BC_INCREFS:
case BC_ACQUIRE:
case BC_RELEASE:
case BC_DECREFS:
...
case BC_INCREFS_DONE:
case BC_ACQUIRE_DONE:
...
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);
break ;
}
case BC_REGISTER_LOOPER:
thread->looper|= BINDER_LOOPER_STATE_REGISTERED;
break ;
case BC_ENTER_LOOPER:
thread->looper|= BINDER_LOOPER_STATE_ENTERED;
break ;
case BC_EXIT_LOOPER:
thread->looper|= BINDER_LOOPER_STATE_EXITED;
break ;
case BC_REQUEST_DEATH_NOTIFICATION:
case BC_CLEAR_DEATH_NOTIFICATION:
...
case BC_DEAD_BINDER_DONE:
...
break ;
default :
...
*consumed= ptr - buffer;
}
return 0 ;
}
出于效率的考虑,在binder_thread_write()函数里的写操作,几乎都是同步进行的。甚至在Binder驱动里来维护用户态数据的引用计数时(由于Binder交互总处于两个进程间,所以引用计数必须由Binder驱动来维护一份计数),也不会发生内存拷贝,是直接通过get_user()函数来验证用户态内存是否有效。这种方式也许放在其他环境下会不安全,但对于Binder来说却是合理的,因为处于引用计数下内存区必然会是有效的。
对于其他的BC_系列命令,处理只是直接完成即可,但对于附带数据的BC_TRANASACTION和BC_REPLY,因为有可能涉及到复杂数据结构的解析以及数据拷贝,于是会通过binder_transaction()来进行传输。从基本操作上来看,整个binder_transaction()操作,有点类似于Mailbox,就是把需要发送的数据直接找到目的,然后再归类整理好,等待接收者在它的读取周期里取走数据。而由于Binder驱动在传输时会使用transaction,这种具有原子性的事务概念,于是只有所有验证都通过,成功把数据传送到目标进程之后(不一定是会被接收处理,但发送操作必须成功之后,才有可能进入读取周期)接收方才能访问到这一传输的结果,于是只到最后的成功点,这一操作才会被挂载到目标进程的binder_thread的todo链表,使后续的读取周期里可以读出来。
binder_transaction()代码比较长,去除掉一些验证性代码后,其基本实现如下:
static void binder_transaction( struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data*tr, int reply)
{
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_treturn_error;
if (reply) { 1
in_reply_to= thread->transaction_stack;
binder_set_nice(in_reply_to->saved_priority);
thread->transaction_stack= in_reply_to->to_parent;
target_thread= in_reply_to->from;
target_proc= target_thread->proc;
} else { 2
if (tr->target.handle) { 3
struct binder_ref*ref;
ref= binder_get_ref(proc, tr->target.handle);
target_node= ref->node;
} else {
target_node= binder_context_mgr_node;
}
e->to_node= target_node->debug_id;
target_proc= target_node->proc;
if (!(tr->flags& TF_ONE_WAY) && thread->transaction_stack) { 4
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) { 5
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); 6
tcomplete= kzalloc( sizeof (*tcomplete), GFP_KERNEL);
if (!reply &&!(tr->flags & TF_ONE_WAY)) 7
t->from= thread;
else
t->from= NULL ;
t->sender_euid= proc->tsk->cred->euid;
t->to_proc= target_proc;
t->to_thread= target_thread;
t->code= tr->code;
t->flags= tr->flags;
t->priority= task_nice(current);
t->buffer= binder_alloc_buf(target_proc, tr->data_size,
tr->offsets_size,!reply && (t->flags & TF_ONE_WAY)); 8
if (t->buffer == NULL ) {
return_error= BR_FAILED_REPLY;
goto err_binder_alloc_buf_failed;
}
t->buffer->allow_user_free= 0 ;
t->buffer->debug_id= t->debug_id;
t->buffer->transaction= t;
t->buffer->target_node= target_node;
if (target_node)
binder_inc_node(target_node, 1 , 0 , NULL );
offp= (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof ( void *)));
if (copy_from_user(t->buffer->data, tr->data.ptr.buffer,tr->data_size)) { 9
binder_user_error( "binder: %d:%d got transaction withinvalid "
"data ptr\n" ,proc->pid, thread->pid);
return_error= BR_FAILED_REPLY;
goto err_copy_data_failed;
}
if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) {
binder_user_error( "binder: %d:%d got transaction withinvalid "
"offsets ptr\n" , proc->pid, thread->pid);
return_error= BR_FAILED_REPLY;
goto err_copy_data_failed;
}
off_end= ( void *)offp + tr->offsets_size;
for (; offp <off_end; offp++) { 10
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: { 11
struct binder_ref*ref;
struct binder_node*node = binder_get_node(proc, fp->binder);
if (node == NULL ) {
node= binder_new_node(proc, fp->binder, fp->cookie);
if (node == NULL ) {
return_error= BR_FAILED_REPLY;
goto err_binder_new_node_failed;
}
node->min_priority= fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
node->accept_fds= !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
}
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;
fp->handle= ref->desc;
binder_inc_ref(ref,fp->type == BINDER_TYPE_HANDLE,
&thread->todo);
} break ;
case BINDER_TYPE_HANDLE: 12
case BINDER_TYPE_WEAK_HANDLE: {
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 );
} 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 );
}
} break ;
case BINDER_TYPE_FD: { 13
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);
fp->handle= target_fd;
} break ;
default :
goto err_bad_object_type;
}
}
t->work.type= BINDER_WORK_TRANSACTION; 14
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_interruptible(target_wait);
return ;
}
1 同样是BC_命令,BC_TRANSACTION与BC_REPLY是不一样的,对于BC_REPLY,一般会是基于某个BR_TRANSACTION处理之后的结果,前面处理的transaction也会被保存到binder_thread的transaction_stack里。于是,对于BC_REPLY的处理,就会是通过transaction_stack,再基于它处理BC_REPLY请求。首先会设置进程优先级,返回值传输时的优先级总会由发送时来决定,于是这里会重设优先级。然后把transaction_stack弹出一级(之所有叫stack,会记录transaction的栈式关系),相当于函数调用上已经退出了,然后回退一次栈。最后会设置好target_thread和target_proc,为后续操作作好准备。
2 对于BC_TRANSACTION,不是基于某个已有的transaction,相反是要发起一个新的,会决定后续BC_REPLY操作的transaction。
3 如果有用户态传过来的binder_transaction包含target.handle,这说明是通过Proxy找到明确的操作目标,则会尝试在Binder驱动里查找该target.handle是否已经存在,找到则会直接把它作为操作目标。当然这时可能找不到,因为Binder必然是先有Service,然后才由Proxy引用,如果找不到这时就可以认为是非法访问,出错退出。如果用户态进程没有指定target.handle,这只能说明是通过servicemanager来查找Service的请求,会直接把目标设置为binder_context_mgr_node。
4 如果传输不是单向的,并且binder_thread的transaction_stack不为空,说明此时正在处理某个或者多个BC_TRANSACTION过程中。我们也知道栈式结构里栈顶永远会是最新的上下文现场,这不符合传输的实际需求。于是,这里会通过一次循环找到transaction_stack的目标符合的target_thread。这也有助于线程池操作,如果存在于transaction_stack中,则说明该transaction肯定处于某个处理过程中,运行这个处理过程的Binder线程必然无法被用于接收这一transaction,而主Binder线程按照同样的原则(命令交给处于栈底的Binder线程来读,交互则发生在栈顶的Binder线程里),总会是最“闲”的。
5 跟3的处理原理类似,如果找到目标binder_thread,则说明该线程是属于某个已经被访问过的Service的,直接更新target_list和target_wait信息。如果找不到,则根据目标的binder_proc来不更新这两个列表信息,此时则发生在ServiceManager访问或是Service的Binder线程没有就绪的情况,可以存起来等待就绪后再执行。
6 分配传输时,内核态需要使用的binder_transaction备份。所有经由内核而在两个进程间进行消息传输的数据,出于进程独立性考虑,必须两个用户进程空间内各一份,然后在内核态有一份,处理传输出错恢复或是重试。而另一个t_complete的申请,只是建立一个操作成功的链表而已。
7 根据用户态的当前信息,来更新内核的binder_transaction备份,并把查找到的target_thread和target_proc填入这一binder_transaction。
8 分配内核态binder_transaction所需要使用的存储空间,使用内部的binder_buffer来分配,这一空间源自于进程空间的mmap()得到的用户空间页面,这些页面会映射到内核态,所以也不会造成内核分配上的开销。
9 处于是一个binder_transaction内的数据,都会保存在data.ptr.buffer的用户内存里,并通过用户内存里的另一段data.ptr.offsets来索引buffer里各个对象的拆分情况。于是这里会把这两部分内容通过copy_from_user()拷贝到内核态。
10 通过一次循环处理data.ptr.buffer里所包含的每个对象,当然,在传输到binder时,这些对象已经是被“压扁”了的对象,flat_binder_object。
11 如果对象的类型是Binder对象(WEAK_BINDER,有可能被自动回收的Binder对象),则尝试查找系统使用过的binder_node是否存在这样的对象,不存在则创建一个。然后给其加上引用计数。
12 如果对象类型是HANDLE(或是WEAK_HANDLE,有可能被自动回收的HANDLE对象),这时必须有提供者(Service)提供这一对象,不存在便出错退出。如果存在,并非target_proc与查找到的handle的proc一致,则将对象与handle关联起来,不一致时必然发生在对servicemanager的调用时,则尝试创建一起对handle的引用。
13 如果对象类型是文件描述符FD,则需要对文件本身的一些引用信息进行更新。
14 如果执行到这一步,证明该transaction就已经完成了,把它加入到target_thread的todo尾部,等待下一个binder_thread_read()周期的读取过程。因为加入链表是最后一步操作,于是传输过程里的原子性可以得到保证。
相对于简单直接的binder_thread_write(),binder_thread_read()函数所需要完成的工作则要复杂得多,是一个被动过程。写时操作总是同步写,可以同步返回操作成功与否的结果,但读操作则会是异步发生,用户态程序在某个状态点通过ioctl来监听read请求时,由Binder驱动主动通知用户态程序,BR_系列的命令总会是一系列内核态的线程化操作触发的。所以binder_thread_read()函数,会跟Socket编程时一样,通过复杂的状态检测之后才会通过BR_*命令返回某个线程化操作的结果,甚至会需要在无事可做时强迫用户态进程进入休眠。
binder_thread_read()的实现:
static int binder_thread_read( struct binder_proc *proc,
struct binder_thread *thread,
void __user *buffer, int size,
signed long *consumed, int non_block)
{
void __user *ptr =buffer + *consumed;
void __user *end =buffer + size;
int ret = 0 ;
int wait_for_proc_work;
if (*consumed == 0 ) { 1
if (put_user(BR_NOOP,(uint32_t __user *)ptr))
return -EFAULT;
ptr+= sizeof (uint32_t);
}
retry: 2
wait_for_proc_work= thread->transaction_stack == NULL &&
list_empty(&thread->todo);
if (thread->return_error != BR_OK && ptr < end) { 3
if (thread->return_error2 != BR_OK) {
if (put_user(thread->return_error2, (uint32_t __user *)ptr))
return -EFAULT;
ptr+= sizeof (uint32_t);
if (ptr == end)
goto done;
thread->return_error2= BR_OK;
}
if (put_user(thread->return_error, (uint32_t __user *)ptr))
return -EFAULT;
ptr+= sizeof (uint32_t);
thread->return_error= BR_OK;
goto done;
}
thread->looper|= BINDER_LOOPER_STATE_WAITING; 4
if (wait_for_proc_work)
proc->ready_threads++;
mutex_unlock(&binder_lock);
if (wait_for_proc_work) {
if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED))){
binder_user_error( "binder: %d:%d ERROR: Thread waiting"
"for process work before callingBC_REGISTER_"
"LOOPER or BC_ENTER_LOOPER (state%x)\n" ,
proc->pid,thread->pid, thread->looper);
wait_event_interruptible(binder_user_error_wait,
binder_stop_on_user_error < 2 );
}
binder_set_nice(proc->default_priority);
if (non_block) {
if (!binder_has_proc_work(proc, thread))
ret= -EAGAIN;
} else
ret= wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc,thread));
} else {
if (non_block) {
if (!binder_has_thread_work(thread))
ret= -EAGAIN;
} else
ret= wait_event_interruptible(thread->wait, binder_has_thread_work(thread));
}
mutex_lock(&binder_lock);
if (wait_for_proc_work)
proc->ready_threads--;
thread->looper&= ~BINDER_LOOPER_STATE_WAITING;
if (ret)
return ret;
while ( 1 ) { 5
uint32_tcmd;
struct binder_transaction_data tr;
struct binder_work *w;
struct binder_transaction *t = NULL ;
if (!list_empty(&thread->todo)) 6
w= list_first_entry(&thread->todo, struct binder_work, entry);
else if (!list_empty(&proc->todo) && wait_for_proc_work)
w= list_first_entry(&proc->todo, struct binder_work, entry);
else {
if (ptr - buffer == 4 &&!(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) /* no data added */
goto retry; 7
break ;
}
if (end - ptr < sizeof (tr) + 4 )
break ;
switch (w->type) { 8
case BINDER_WORK_TRANSACTION: { 9
t= container_of(w, struct binder_transaction, work);
} break ;
case BINDER_WORK_TRANSACTION_COMPLETE: { 10
cmd= BR_TRANSACTION_COMPLETE;
if (put_user(cmd,(uint32_t __user *)ptr))
return -EFAULT;
ptr+= sizeof (uint32_t);
list_del(&w->entry);
kfree(w);
} break ;
case BINDER_WORK_NODE:{ 11
struct binder_node*node = container_of(w, struct binder_node, work);
uint32_tcmd = BR_NOOP;
const char *cmd_name;
int strong =node->internal_strong_refs || node->local_strong_refs;
int weak =!hlist_empty(&node->refs) || node->local_weak_refs || strong;
if (weak &&!node->has_weak_ref) {
cmd= BR_INCREFS;
cmd_name= "BR_INCREFS" ;
node->has_weak_ref= 1 ;
node->pending_weak_ref= 1 ;
node->local_weak_refs++;
} else if (strong &&!node->has_strong_ref) {
cmd= BR_ACQUIRE;
cmd_name= "BR_ACQUIRE" ;
node->has_strong_ref= 1 ;
node->pending_strong_ref= 1 ;
node->local_strong_refs++;
} else if (!strong &&node->has_strong_ref) {
cmd= BR_RELEASE;
cmd_name= "BR_RELEASE" ;
node->has_strong_ref= 0 ;
} else if (!weak &&node->has_weak_ref) {
cmd= BR_DECREFS;
cmd_name= "BR_DECREFS" ;
node->has_weak_ref= 0 ;
}
if (cmd != BR_NOOP) {
if (put_user(cmd,(uint32_t __user *)ptr))
return -EFAULT;
ptr+= sizeof (uint32_t);
if (put_user(node->ptr, ( void * __user *)ptr))
return -EFAULT;
ptr+= sizeof ( void *);
if (put_user(node->cookie, ( void * __user *)ptr))
return -EFAULT;
ptr+= sizeof ( void *);
} else {
list_del_init(&w->entry);
if (!weak &&!strong) {
rb_erase(&node->rb_node,&proc->nodes);
kfree(node);
binder_stats_deleted(BINDER_STAT_NODE);
}
}
} break ;
case BINDER_WORK_DEAD_BINDER:
case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {
struct binder_ref_death *death;
uint32_tcmd;
death= container_of(w, struct binder_ref_death, work);
if (w->type ==BINDER_WORK_CLEAR_DEATH_NOTIFICATION)
cmd= BR_CLEAR_DEATH_NOTIFICATION_DONE;
else
cmd= BR_DEAD_BINDER;
if (put_user(cmd,(uint32_t __user *)ptr))
return -EFAULT;
ptr+= sizeof (uint32_t);
if (put_user(death->cookie, ( void * __user *)ptr))
return -EFAULT;
ptr+= sizeof ( void *);
if (w->type ==BINDER_WORK_CLEAR_DEATH_NOTIFICATION) {
list_del(&w->entry);
kfree(death);
binder_stats_deleted(BINDER_STAT_DEATH);
} else
list_move(&w->entry,&proc->delivered_death);
if (cmd ==BR_DEAD_BINDER)
goto done;
} break ;
}
if (!t)
continue ;
BUG_ON(t->buffer== NULL );
if (t->buffer->target_node) { 12
struct binder_node*target_node = t->buffer->target_node;
tr.target.ptr= target_node->ptr;
tr.cookie= target_node->cookie;
t->saved_priority= task_nice(current);
if (t->priority< target_node->min_priority &&
!(t->flags & TF_ONE_WAY))
binder_set_nice(t->priority);
else if (!(t->flags& TF_ONE_WAY) ||
t->saved_priority >target_node->min_priority)
binder_set_nice(target_node->min_priority);
cmd= BR_TRANSACTION;
} else {
tr.target.ptr= NULL ;
tr.cookie= NULL ;
cmd= BR_REPLY;
}
tr.code= t->code;
tr.flags= t->flags;
tr.sender_euid= t->sender_euid;
if (t->from) { 13
struct task_struct*sender = t->from->proc->tsk;
tr.sender_pid= task_tgid_nr_ns(sender,
current->nsproxy->pid_ns);
} else {
tr.sender_pid= 0 ;
}
tr.data_size= t->buffer->data_size;
tr.offsets_size= t->buffer->offsets_size;
tr.data.ptr.buffer= ( void *)t->buffer->data +
proc->user_buffer_offset;
tr.data.ptr.offsets= tr.data.ptr.buffer +
ALIGN(t->buffer->data_size,
sizeof ( void *));
if (put_user(cmd,(uint32_t __user *)ptr))
return -EFAULT;
ptr+= sizeof (uint32_t);
if (copy_to_user(ptr,&tr, sizeof (tr))) 14
return -EFAULT;
ptr+= sizeof (tr);
list_del(&t->work.entry);
t->buffer->allow_user_free= 1 ;
if (cmd ==BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
t->to_parent= thread->transaction_stack;
t->to_thread= thread;
thread->transaction_stack= t;
} else {
t->buffer->transaction= NULL ;
kfree(t);
binder_stats_deleted(BINDER_STAT_TRANSACTION);
}
break ;
}
done: 15
*consumed= ptr - buffer;
if (proc->requested_threads + proc->ready_threads == 0 &&
proc->requested_threads_started <proc->max_threads &&
(thread->looper &(BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED))
proc->requested_threads++;
if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
return -EFAULT;
}
return 0 ;
}
1 如果此时,consumed为零,则会是一个新的读写周期,应该忽略掉4字节的命令信息。否则,此时可认为是借用前面某一些正在进行中的Binder读写周期,ptr指向的会正在需要填写的区域。
2 retry标记一般用来做循环,以降低while使用上的混乱。当我们需要在当前读写周期里进行重试时,都会跳转到retry重新开始检测,比如在休眠中被唤醒,但又无事可做就需要再次进入休眠等待。
3 任何的出错信息,都可以直接返回,不需要再进行后续的操作,比如thread的return_error与return_error2两个标识,以及内存越界等情况。
4 thread->loop是一个标识位,标明现在Binder当前使用的内核线程处于什么操作过程中。这是一个按位来标记状态的变量,于是可能会出现多位同时存在的情况。在这里,会先置上BINDER_LOOPER_STATE_WAITING位,然后检查是否应该进入休眠中,比如用户态进程没有注册或是调用ENTER_LOOP来驱动循环监听,或是没有Binder操作等情况下,都会通过wait_event_interruptible()系列函数进入休眠。需要注意是,这里会通过一个binder_lock来上锁,保护binder驱动的可重入性,于是这里会在休眠前解锁,从休眠函数退出后马上又继续上锁。当退出休眠时,会将BINDER_LOOPER_STATE_WAITING位清空。
5 通过循环来进行读操作。跟Socket的读取一端的实现一样,外围循环进行读取重试,内层循环来保证读取到一个完整的包。在Binder的读过程里,这样的循环也是必要,因为是多线程处理,在外围循环里可以检测到有任务在进行中,而发回发馈前是必须通过循环来得到完整的处理结果的。
6 通过检查thread->todo和proc->todo来判断是否在Binder命令需要给出反馈。
7 如果包仅包含一个4字节命令,而又没有设置需要马上返回操作状态,此时会尝试重试,争取在这个读周期里加载更多内容。
8 根据读取回来不同任务类型来进行具体的返回操作。
9 如果任务是BINDER_WORK_TRANSACTION,则该反馈需要进一步的处理,会尝试在此在todo列表里取出该任务
10如果任务是BINDER_WORK_TRANSACTION_COMPLETE,说明某个TRANSACTION操作已经完成,于是通过BR_TRANSACTION_COMPLETE命令来通知用户态进程。
11 如果任务是BINDER_WORK_NODE,则说明该任务只是修改某个引用对象属性的操作,于是通过内部索引找到该对象的引用,加以修改,然后再结果通过BR_系列命令返回。此时会产生BR_INCREFS、BR_ACQUIRE、BR_RELEASE、BR_DECREFS四种回馈,当然没有任何相当操作,则是返回BR_NOOP。如果任务是BINDER_WORK_DEAD_BINDER之类的,也会是比较简单的列表操作,并将结果返回。
12 处理完任务相关类型的判断,此时基本上可以认为非线程化操作都已经完成了,而需要对线程化操作过的TRANSACTION的返回过程进行处理。如果步骤9取回来的transaction为空,则证明TRANSACTION操作还不成功,于是会继续循环等待其操作成功。如果transaction不为空,则会通过transaction所标明的信息,进行返回后处理,将TRANSACTION任务的操作结果存储起来,指定返回命令是BR_TRANSACTION,并且根据不同传输类型来修改调度优先级。如果是单向传输并且传输时提供的优先级又低于传输目标的最低优先级需求时,使用传输指定的优先级;如果是双向传输,传输时指定的优先级大于传输目标的最低优先级需求,设置该次调度使用传输目标的最低优先级。这样,Binder返回过程总会处于一个比较低的进程优先级状态下。最后,如果transaction任务所指向的操作目标t->buffer->target_node为空,则该操作只是用于返回操作结果,此时不再拷贝数据,只以BR_REPLY返回操作结果。
13 如果t->from不为空,则可以查找到发送者的相关信息,于是将发送进程的pid信息填入binder_transaction_data。如果为空,则肯定是由binder驱动发出的消息,pid被置为0。注意这时的使用的pid,由于Linux是使用的1:1映射的线程实现,所有使用的会是tgid。当然,t->buffer指向的操作结果,也将保存到binder_transaction_data里。
14 将通过binder_transaction处理得到的binder_transaction_data数据结构拷贝回用户态。到这一步,针对TRANSACTION命令的相关读操作也就完成了。我们可以注意到binder_transaction_data数据结构的类型,它跟servicemanager进程使用的binder_txn数据结构一致,而IPCThreadState也是使用同一binder_transaction_data来与binder驱动交互,于是这样的消息体就可用于用户态与Binder驱动之间交换大量数据了。
14 最后,就是返回结果,结束这次binder_thread_read()的调用。最后的操作一个是更新consumed计数,另一个是当线程池无法处理当前操作时,通过一个BR_SPAWN_LOOP来创建新的IPC线程。
通过对binder_thread_write()和binder_thread_read()的分析,我们可以知道binder在处理简单命令交互,以及在transaction完成之后的后处理是如何完成的。通过binder_ioctl()函数提供的命令,再经由上述两个函数的处理,最终可以达到一个进程发起的请求,可交由另一个进程来进行响应。
另外,Binder驱动实现还包括binder_mmap()的实现,binder_poll()的实现。binder_mmap()实际上是一个假实现,它的目的,只是用于在用户态内存空间里创建虚址空间,存放binder传输所需要的buffer,所以虽然说是copy_from_user(),但对于大量数据的读写,也还是发生在用户进程的内存空间里,详情见binder_buffer相关的代码。而binder_poll(),则是用于poll方式来访问binder驱动,这在Android系统里见不到类似的用法。
1.1 总结 --- 独特的Binder驱动
总结Binder驱动,几乎可以说,通过binder_ioctl(),就得到了完整的binder实现。整个Binder驱动,在内部实现上就可以证实我们前面提供的各种优点和缺点:
1) 高效,相对与其他IPC,在很多情况下Binder可以减小内存拷贝的开销,而且由于代码精练,又使用了红黑树等算法来索引,Binder必然得到比较高的的性能。
2) 面向对象,整个Binder驱动,只是libbinder的一种辅助手段,于是会继承libbinder实现上的面向对象的特点。而Binder驱动对于数据传输时的处理,从里到外,都具有面向对象的特点,甚至内部还会使用一个work_queue来完成自动垃圾回收的功能
3) 开销小。Binder驱动在不使用时,所造成的开销几乎可以忽略不记。对于每次数据的读写操作,Binder驱动都是通过binder_transcation这样的元数据来记录,只记录数据变动,开销也降到了最低。而对于大数据量的读写,使用从用户空间“偷”来的内存空间,只针对进程有效,不会影响到整个系统的内存分配,只有一些内部维护的开销。
4) 面向进程。Binder IPC传输的本质便是面向进程,这点可以从binder_transaction的结构构成中可以看出来,而只使用进程的用户空间来处理大数据量buffer,也进一步加强binder的进程化。如果处于binder传输过程中的进程出错死掉,则完全不会带来系统级的开销。
5) 简洁。虽然实现了这么复杂的功能,但代码实现上还是比较简洁的。当然,也有过于简洁的毛病,Binder不拥有Linux内核在实现上的风格,通过代码自我解释,需要结合libbinder的实现才能理解Binder驱动里进行的到底是什么处理。
最后,我们可以拿Linux世界里现有的一些特性来说明Binder的唯一性和优越性。如果拿Socket跟Binder作对比,在性能上也许不会高出多少,还导致了无法跨平台,但对于内存使用、面向对象等特点,则足以让Socket汗颜。如果拿Linux世界里另一个具备面对对象能力,在Android系统也会被用到的D-Bus来跟Binder作对比,D-Bus需要由用户态进程来完成分发,这一条就会使用Binder在内存使用和性能上远超D-bus。
事实上,Socket、D-bus、Binder在Android都有被用到,但前两者只是用于与传统编程环境兼容,比如BlueZ使用D-bus通信,而RIL或是Zygote则会使用Socket(在Zygote之前,Binder的通信环境还不成熟)。
Socket
Binder
Dbus
访问方式
FD 句柄
PID
FD | PID
读写接口
Stream I/O
ioctl
消息队列
网络支持
支持
不支持
不支持
性能
一般
很好
很差
统一访问接口
否
是
是
可拓展性
否
是
是
从这样的对象可以看出,Binder与Socket相比,除了不支持网络,Binder完胜。由于面向对象,使Binder在接口统一,可拓展性上更具优势,而比较讽刺的是,在这种面向对象条件下,Binder性能还比Socket要高,我们从Binder驱动里也看到过了,Binder驱动在节省内存拷贝、简化通讯处理上下足了功夫。
而D-bus则刚好相反,它提供与Binder类似的能力,但它只是用户态的一层IPC“皮”,是将IPC机制进行了用户态的面向对象封装。每次D-bus消息传输都有可能通过多次进程切换来完成分发,性能极低,在桌面系统里或许可以承受,却不是嵌入式环境里可以容忍的,基于D-bus的嵌入式Linux解决方案,一般都有性能或是用户体验不佳的通病,比如Meego。
- Service与Android系统设计
- Service与Android系统设计(2)
- Service与Android系统设计(3)
- Service与Android系统设计-- libbinder 转载
- Service与Android系统设计(6)--- Native Service
- Service与Android系统设计(6)--- Native Service
- Service与Android系统设计(6)--- Native Service
- Service与Android系统设计(6)--- Native Service
- Service与Android系统设计(6)--- Native Service
- Service与Android系统设计(2)-- Parcel
- Service与Android系统设计(3)-- ActivityManager的实现
- Service与Android系统设计(4)-- ServiceManager
- Service与Android系统设计(5)-- libbinder
- Service与Android系统设计(7)--- Binder驱动
- Service与Android系统设计(2)-- Parcel
- Service与Android系统设计(3)-- ActivityManager的实现
- Service与Android系统设计(4)-- ServiceManager
- Service与Android系统设计(5)-- libbinder
- GetWindowLong函数
- Hadoop MapReduce程序中解决第三方jar包问题--终极解决方案
- 全球3大项目外包和接单网站介绍
- Linux安装mysql——源码安装
- oracle创建用户及授权
- Service与Android系统设计
- 通过adb网络连接调试Android应用程序
- C++仿函数(functor)详解
- 关于SQL Server
- Web安全测试-----AppScan扫描工具
- MATLAB——zeros
- ipvs
- IE6 a href onclick兼容性问题
- NSData与NSString Byte UIImage转换