注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

东东的博客

江南烟雨,同大家一起分享

 
 
 

日志

 
 

android binder 讲解(四)  

2011-04-28 11:40:00|  分类: android相关 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

下面再看joinThreadPool的实现:

 


do  
{  
...  
        result = talkWithDriver();  
        if (result >= NO_ERROR) {  
            size_t IN = mIn.dataAvail();  
            if (IN < sizeof(int32_t)) continue;  
            cmd = mIn.readInt32();  
            IF_LOG_COMMANDS() {  
                alog << "Processing top-level Command: "  
                    << getReturnString(cmd) << endl;  
            }  
            result = executeCommand(cmd);  
        }  
...  
while(...);  

do

{

...

        result = talkWithDriver();

        if (result >= NO_ERROR) {

            size_t IN = mIn.dataAvail();

            if (IN < sizeof(int32_t)) continue;

            cmd = mIn.readInt32();

            IF_LOG_COMMANDS() {

                alog << "Processing top-level Command: "

                    << getReturnString(cmd) << endl;

            }

            result = executeCommand(cmd);

        }

...

while(...);

这个函数在循环中重复执行下列动作:


talkWithDriver 通过ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)读取请求和写回结果。
executeCommand 执行相应的请求

在IPCThreadState::executeCommand(int32_t cmd)函数中:


对于控制对象生命周期的请求,像BR_ACQUIRE/BR_RELEASE直接做了处理。
对于BR_TRANSACTION请求,它调用被请求对象的transact函数。

按下列方式调用实际的对象:

 


if (tr.target.ptr) {  
    sp<BBinder> b((BBinder*)tr.cookie);  
    const status_t error = b->transact(tr.code, buffer, &reply, 0);  
    if (error < NO_ERROR) reply.setError(error);   
   
} else {  
    const status_t error = the_context_object->transact(tr.code, buffer, &reply, 0);  
    if (error < NO_ERROR) reply.setError(error);  
}  

if (tr.target.ptr) {

    sp<BBinder> b((BBinder*)tr.cookie);

    const status_t error = b->transact(tr.code, buffer, &reply, 0);

    if (error < NO_ERROR) reply.setError(error);

 

} else {

    const status_t error = the_context_object->transact(tr.code, buffer, &reply, 0);

    if (error < NO_ERROR) reply.setError(error);

}

如果tr.target.ptr不为空,就把tr.cookie转换成一个Binder对象,并调用它的transact函数。如果没有目标对象, 就调用 the_context_object对象的transact函数。奇怪的是,根本没有谁对the_context_object进行初始 化,the_context_object是空指针。原因是context_mgr的请求发给了ServiceManager,所以根本不会走到else 语句里来。

o 内核模块

android使用了一个内核模块binder来中转各个进程之间的消息。模块源代码放在binder.c里,它是一个字符驱动程序,主要通过 binder_ioctl与用户空间的进程交换数据。其中BINDER_WRITE_READ用来读写数据,数据包中有一个cmd域用于区分不同的请求:


binder_thread_write用于发送请求或返回结果。
binder_thread_read用于读取结果。

从binder_thread_write中调用binder_transaction中转请求和返回结果,binder_transaction的实现如下:

对请求的处理:


通过对象的handle找到对象所在的进程,如果handle为空就认为对象是context_mgr,把请求发给context_mgr所在的进程。
把请求中所有的binder对象全部放到一个RB树中。
把请求放到目标进程的队列中,等待目标进程读取。

如何成为context_mgr呢?内核模块提供了BINDER_SET_CONTEXT_MGR调用:

 


static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  
{  
    ...  
    case BINDER_SET_CONTEXT_MGR:  
        if (binder_context_mgr_node != NULL) {  
            printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");  
            ret = -EBUSY;  
            goto err;  
        }  
        if (binder_context_mgr_uid != -1) {  
            if (binder_context_mgr_uid != current->euid) {  
                printk(KERN_ERR "binder: BINDER_SET_"  
                       "CONTEXT_MGR bad uid %d != %d\n",  
                       current->euid,  
                       binder_context_mgr_uid);  
                ret = -EPERM;  
                goto err;  
            }  
        } else  
            binder_context_mgr_uid = current->euid;  
        binder_context_mgr_node = binder_new_node(proc, NULL, NULL);  
        if (binder_context_mgr_node == NULL) {  
            ret = -ENOMEM;  
            goto err;  
        }  
        binder_context_mgr_node->local_weak_refs++;  
        binder_context_mgr_node->local_strong_refs++;  
        binder_context_mgr_node->has_strong_ref = 1;  
        binder_context_mgr_node->has_weak_ref = 1;  
        break;  

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)

{

...

case BINDER_SET_CONTEXT_MGR:

  if (binder_context_mgr_node != NULL) {

   printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");

   ret = -EBUSY;

   goto err;

  }

  if (binder_context_mgr_uid != -1) {

   if (binder_context_mgr_uid != current->euid) {

    printk(KERN_ERR "binder: BINDER_SET_"

           "CONTEXT_MGR bad uid %d != %d\n",

           current->euid,

           binder_context_mgr_uid);

    ret = -EPERM;

    goto err;

   }

  } else

   binder_context_mgr_uid = current->euid;

  binder_context_mgr_node = binder_new_node(proc, NULL, NULL);

  if (binder_context_mgr_node == NULL) {

   ret = -ENOMEM;

   goto err;

  }

  binder_context_mgr_node->local_weak_refs++;

  binder_context_mgr_node->local_strong_refs++;

  binder_context_mgr_node->has_strong_ref = 1;

  binder_context_mgr_node->has_weak_ref = 1;

  break;

ServiceManager(frameworks/base/cmds/servicemanager)通过下列方式成为了context_mgr进程:

 


int binder_become_context_manager(struct binder_state *bs)  
{  
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);  
}  
   
int main(int argc, char **argv)  
{  
    struct binder_state *bs;  
    void *svcmgr = BINDER_SERVICE_MANAGER;  
   
    bs = binder_open(128*1024);  
   
    if (binder_become_context_manager(bs)) {  
        LOGE("cannot become context manager (%s)\n", strerror(errno));  
        return -1;  
    }  
   
    svcmgr_handle = svcmgr;  
    binder_loop(bs, svcmgr_handler);  
    return 0;  
}  

int binder_become_context_manager(struct binder_state *bs)

{

    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);

}

 

int main(int argc, char **argv)

{

    struct binder_state *bs;

    void *svcmgr = BINDER_SERVICE_MANAGER;

 

    bs = binder_open(128*1024);

 

    if (binder_become_context_manager(bs)) {

        LOGE("cannot become context manager (%s)\n", strerror(errno));

        return -1;

    }

 

    svcmgr_handle = svcmgr;

    binder_loop(bs, svcmgr_handler);

    return 0;

}

o 如何得到服务对象的handle


服务提供者通过defaultServiceManager得到ServiceManager对象,然后调用addService向服务管理器注册。
服务使用者通过defaultServiceManager得到ServiceManager对象,然后调用getService通过服务名称查找到服务对象的handle。

o 如何通过服务对象的handle找到服务所在的进程

0表示服务管理器的handle,getService可以查找到系统服务的handle。这个handle只是代表了服务对象,内核模块是如何通过handle找到服务所在的进程的呢?


对于ServiceManager: ServiceManager调用了binder_become_context_manager使用自己成为context_mgr,所有handle为0的请求都会被转发给ServiceManager。
对于系统服务和应用程序的Listener,在第一次请求内核模块时(比如调用addService),内核模块在一个RB树中建立了服务对象和进程的对应关系。

 


off_end = (void *)offp + tr->offsets_size;  
for (; offp < off_end; offp++) {  
    struct flat_binder_object *fp;  
    if (*offp > t->buffer->data_size - sizeof(*fp)) {  
        binder_user_error("binder: %d:%d got transaction with "  
            "invalid offset, %d\n",  
            proc->pid, thread->pid, *offp);  
        return_error = BR_FAILED_REPLY;  
        goto err_bad_offset;  
    }  
    fp = (struct flat_binder_object *)(t->buffer->data + *offp);  
    switch (fp->type) {  
    case BINDER_TYPE_BINDER:  
    case BINDER_TYPE_WEAK_BINDER: {  
        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);  
        }  
        if (fp->cookie != node->cookie) {  
            binder_user_error("binder: %d:%d sending u%p "  
                "node %d, cookie mismatch %p != %p\n",  
                proc->pid, thread->pid,  
                fp->binder, node->debug_id,  
                fp->cookie, node->cookie);  
            goto err_binder_get_ref_for_node_failed;  
        }  
        ref = binder_get_ref_for_node(target_proc, node);  
        if (ref == NULL) {  
            return_error = BR_FAILED_REPLY;  
            goto err_binder_get_ref_for_node_failed;  
        }  
        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);  
        if (binder_debug_mask & BINDER_DEBUG_TRANSACTION)  
            printk(KERN_INFO "        node %d u%p -> ref %d desc %d\n",  
                   node->debug_id, node->ptr, ref->debug_id, ref->desc);  
    } break;  

  off_end = (void *)offp + tr->offsets_size;

  for (; offp < off_end; offp++) {

    struct flat_binder_object *fp;

    if (*offp > t->buffer->data_size - sizeof(*fp)) {

      binder_user_error("binder: %d:%d got transaction with "

        "invalid offset, %d\n",

        proc->pid, thread->pid, *offp);

      return_error = BR_FAILED_REPLY;

      goto err_bad_offset;

    }

    fp = (struct flat_binder_object *)(t->buffer->data + *offp);

    switch (fp->type) {

    case BINDER_TYPE_BINDER:

    case BINDER_TYPE_WEAK_BINDER: {

      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);

      }

      if (fp->cookie != node->cookie) {

        binder_user_error("binder: %d:%d sending u%p "

          "node %d, cookie mismatch %p != %p\n",

          proc->pid, thread->pid,

          fp->binder, node->debug_id,

          fp->cookie, node->cookie);

        goto err_binder_get_ref_for_node_failed;

      }

      ref = binder_get_ref_for_node(target_proc, node);

      if (ref == NULL) {

        return_error = BR_FAILED_REPLY;

        goto err_binder_get_ref_for_node_failed;

      }

      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);

      if (binder_debug_mask & BINDER_DEBUG_TRANSACTION)

        printk(KERN_INFO "        node %d u%p -> ref %d desc %d\n",

               node->debug_id, node->ptr, ref->debug_id, ref->desc);

    } break;


请求服务时,内核先通过handle找到对应的进程,然后把请求放到服务进程的队列中。

o C调用JAVA

前面我们分析的是C代码的处理。对于JAVA代码,JAVA调用C的函数通过JNI调用即可。从内核时读取请求是在C代码(executeCommand)里进行了,那如何在C代码中调用那些用JAVA实现的服务呢?

android_os_Binder_init里的JavaBBinder对Java里的Binder对象进行包装。

JavaBBinder::onTransact调用Java里的execTransact函数:

 


        jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,  
            code, (int32_t)&data, (int32_t)reply, flags);  
        jthrowable excep = env->ExceptionOccurred();  
        if (excep) {  
            report_exception(env, excep,  
                "*** Uncaught remote exception!  "  
                "(Exceptions are not yet supported across processes.)");  
            res = JNI_FALSE;   
   
            /* clean up JNI local ref -- we don't return to Java code */  
            env->DeleteLocalRef(excep);  
        }  

        jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,

            code, (int32_t)&data, (int32_t)reply, flags);

        jthrowable excep = env->ExceptionOccurred();

        if (excep) {

            report_exception(env, excep,

                "*** Uncaught remote exception!  "

                "(Exceptions are not yet supported across processes.)");

            res = JNI_FALSE;

 

            /* clean up JNI local ref -- we don't return to Java code */

            env->DeleteLocalRef(excep);

        }

o 广播消息

binder不提供广播消息,不过可以ActivityManagerService服务来实现广播。
(frameworks/base/core/java/android/app/ActivityManagerNative.java)

接收广播消息需要实现接口BroadcastReceiver,然后调用ActivityManagerProxy::registerReceiver注册。

触发广播调用ActivityManagerProxy::broadcastIntent。(应用程序并不直接调用它,而是调用Context对它的包装)

  评论这张
 
阅读(1605)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017