Android进程间通信(IPC)之实现细节篇(二) ServiceManager

一、简介

上一篇文章介绍了Binder驱动,接下来分析的是服务管理器程序ServiceManager(SMgr)。ServiceManager的代码读起来应会比Binder驱动顺畅许多,文章亦如是。而其原因在于Binder驱动的代码一来晦涩难懂(牵涉许多内核机制),二来是读者无法流程化地去跟代码(因为Binder驱动就是内核的一个模块,应用程序何时何地调用都是不可知)。而ServiceManager就是一个用户态程序,代码就是从main函数开始一步一步运行,有迹可循的流程让代码解析变得轻松了许多。ServiceManager程序的代码位于framework/base/cmds/servicemanager文件夹下。

本文主要想说明两个问题:

1、 ServiceManager如何管理Android系统中所有的服务项

2、 与Binder驱动的交互细节

二、main流程

ServiceManager是在Android系统启动的时候就运行的后台服务程序(由init进程解析init.rc中的service servicemanager /system/bin/servicemanager启动),servicemanager程序的main函数如下:

int main(intargc, char **argv)
{
struct binder_state *bs;
// BINDER_SERVICE_MANAGER定义为0
    void *svcmgr = BINDER_SERVICE_MANAGER;
       //打开设备结点,并设置缓存区大小
    bs = binder_open(128*1024);
       //注册成为管理者
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become contextmanager (%s)\n", strerror(errno));
        return -1;
    }
       //进入loop循环,不停提供服务
    svcmgr_handle = svcmgr;
    binder_loop(bs, svcmgr_handler);
    return 0;
}

 

       接下来分析binder_open函数:

structbinder_state *binder_open(unsigned mapsize)
{
    struct binder_state *bs;
    bs->fd =open("/dev/binder", O_RDWR);
    bs->mapsize = mapsize;
    bs->mapped = mmap(NULL,mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    return bs;
}


该函数的功能很简单,首先创建一个结构体变量bs;然后打开binder设备结点,并把文件描述符保存到bs中;再根据传入参数通过mmap设置缓冲区大小,且把缓存区大小和映射出来的地址保存到bs中,从代码中可知,缓存区的大小为128k。

接下来分析函数binder_become_context_manager:

int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd,BINDER_SET_CONTEXT_MGR, 0);
}

其代码意思就是告知驱动,我要申请成为管理者,返回值表示是否申请成功。

而驱动对BINDER_SET_CONTEXT_MGR命令的处理也很简单:

static long binder_ioctl(struct file *filp, unsigned int cmd,unsigned long arg)
{
       int ret;
       struct binder_proc *proc= filp->private_data;
       struct binder_thread*thread;
             
              //ioctl调用不允许同时调用
       binder_lock(__func__);
       //获取proc的一个线程,如果没有创建一个线程
       thread =binder_get_thread(proc);
 
       switch (cmd) {
       caseBINDER_SET_CONTEXT_MGR:
              //是否管理者已经有人了
              if(binder_context_mgr_node != NULL) {
                     pr_err("BINDER_SET_CONTEXT_MGRalready set\n");
                     ret = -EBUSY;
                     goto err;
              }
              //以下代码为安全性检查
              ret =security_binder_set_context_mgr(proc->tsk);
              if (ret < 0)
                     goto err;
              if(uid_valid(binder_context_mgr_uid)) {
                     if(!uid_eq(binder_context_mgr_uid, current->cred->euid)) {
                            pr_err("BINDER_SET_CONTEXT_MGRbad uid %d != %d\n",
                                   from_kuid(&init_user_ns,current->cred->euid),
                                   from_kuid(&init_user_ns,binder_context_mgr_uid));
                            ret= -EPERM;
                            gotoerr;
                     }
              } else
                     binder_context_mgr_uid= current->cred->euid;
              //为servicemanager服务创建Node结点---Binder实体
              binder_context_mgr_node= binder_new_node(proc,
                     (binder_ptr)0, (binder_ptr) 0);
              if(binder_context_mgr_node == NULL) {
                     ret =-ENOMEM;
                     goto err;
              }
              //更新binder实体的引用计数
              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;
       }
       binder_unlock(__func__);
       return ret;
}

三、binder_loop流程

接下来分析函数binder_loop:

void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
       //创建传递给驱动的数据结构
    struct binder_write_readbwr;
    unsigned readbuf[32];
    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;
   
//告知驱动,我将进入循环
    readbuf[0] =BC_ENTER_LOOPER;
    binder_write(bs, readbuf, sizeof(unsigned));
      
       //进入循环体,来处理服务请求
    for (;;) {
        bwr.read_size =sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer =(unsigned) readbuf;
             
              //主动去读驱动是否有给我的数据,如果没有就阻塞,直到并唤醒
        res = ioctl(bs->fd,BINDER_WRITE_READ, &bwr);
 
              //处理接收到的请求
        res = binder_parse(bs,0, readbuf, bwr.read_consumed, func);
    }
}

现在开始我们会频繁地接触Binder中的一些命令字,上面代码中就出现了BINDER_WRITE_READ和BC_ENTER_LOOPER,这些命令字是理解流程的关键。我们可以认为BINDER_WRITE_READ一类的命令字为一级命令,由binder_ioctl函数处理,主要包括以下:

命令字

描述

BINDER_WRITE_READ

最常用的命令,收发数据包的时候用这个。用该命令时传递的参数是binder_write_read结构体

BINDER_SET_MAX_THREADS

设置proc最多能够创建的线程数,用于服务程序告知驱动:如果线程数达到了最大值,就算服务请求处理不来也别再让我再启动新的线程了

BINDER_SET_CONTEXT_MGR

注册成为管理者,只有servicemanager程序会用该命令。驱动中管理者只能有一个,因此杜绝了其他应用程序来骗取管理权限

BINDER_THREAD_EXIT

当线程退出的时候使用,方便驱动释放与之相关的数据机构

BINDER_VERSION

该命令用于获取驱动的版本号,如果获得的版本号与Android框架中版本号不匹配,则报错以提示开发者注意

      

而BC_ENTER_LOOPER命令可以理解为BINDER_WRITE_READ下一级的命令,姑且称之为二级命令吧,这一类命令由binder_thread_write函数处理,主要包括以下:

命令字

描述

BC_TRANSACTION

该命令用于发送请求,使用该命令时传递的数据包中包含一个binder_transaction_data结构体来表示要传递的数据。

BC_REPLY

该命令与BC_TRANSACTION是对应的,当服务程序处理完请求后,就会把处理结构存放到binder_transaction_data中,并使用该命令来返回给发送方应用程序

BC_FREE_BUFFER

binder_transaction_data中具体传递的数据,驱动会为其分配内存来保存,并发送给接收方服务程序,服务程序处理完请求后,会使用该命令来告知驱动回收该块内存

BC_INCREFS

BC_ACQUIRE

BC_RELEASE

BC_DECREFS

该组命令用于增加或减少引用计数

BC_REGISTER_LOOPER

BC_ENTER_LOOPER

BC_EXIT_LOOPER

BC_REGISTER_LOOPER命令用于告知驱动一个服务线程启动了,BC_ENTER_LOOPER表示该线程可以提供服务了,BC_EXIT_LOOPER表示线程停止提供服务

BC_REQUEST_DEATH_NOTIFICATION

使用该命令来告知驱动,当对应的Binder实体死亡的时候通知我,用于服务程序异常退出的情况

       了解完这些命令字的用法之后,前面那段代码就比较好理解了,binder_loop函数首先通过BC_ENTER_LOOPER告知驱动我已经开始准备提供服务了,然后再for(;;)无限循环地开始处理服务请求。驱动对BC_ENTER_LOOPER命令字的处理很简洁:

              case BC_ENTER_LOOPER:
                     thread->looper|= BINDER_LOOPER_STATE_ENTERED;
                     break;

       这里有一个细节提前说一下,BINDER_WRITE_READ命令表示收发数据,具体是收还是发则由参数binder_write_read中write_size和read_size控制,当write_size大于0且read_size等于0的时候表示只发送数据,read_size大于0且write_size等于0的情况表示只接收数据,而write_size大于0且read_size也大于0的时候表示先发送数据然后等待接收结果。因此在binder_loop函数for循环体中,binder_write_read结构体的write_size为0,read_size不为0的情况时,表示这次通过BINDER_WRITE_READ命令是想从驱动接受请求。当没有请求数据的时候服务线程就会阻塞在等待队列上,直到被唤醒来提供服务:

       case BINDER_WRITE_READ: {
              structbinder_write_read bwr;
              //从用户态读取参数到内核态结构体变量中
              if(copy_from_user(&bwr, ubuf, sizeof(bwr))) {
                     ret= -EFAULT;
                     gotoerr;
              }
              if(bwr.write_size > 0) {
                     //发送请求的时候走该流程
              }
              if(bwr.read_size > 0) {
                     signedlong consumed = 0;
                     /*the BINDER_ULONG_TO_ULONG type-casting here is to */
                     /*avoid compiler warning of int-->pointer cast.     */
                     //filp->f_flags默认是阻塞的,所以没有数据可读时程序会被阻塞
                     ret= binder_thread_read(proc, thread,
                            (void__user *) BINDER_ULONG_TO_ULONG(bwr.read_buffer),
                            bwr.read_size,
                            &consumed,filp->f_flags & O_NONBLOCK);
                     trace_binder_read_done(ret);
                     bwr.read_consumed= (binder_long) consumed;
                     if(!list_empty(&proc->todo))
                            wake_up_interruptible(&proc->wait);
                     if(ret < 0) {
                            //出错的情况下,也把错误结果返回给用户态程序
                            if(copy_to_user(ubuf, &bwr, sizeof(bwr)))
                                   ret= -EFAULT;
                            gotoerr;
                     }
              }
              //结果返回给用户态程序
              if(copy_to_user(ubuf, &bwr, sizeof(bwr))) {
                     ret= -EFAULT;
                     gotoerr;
              }
              break;
       }

四、驱动流程---binder_thread_read

       接下来分析一个重要的函数binder_thread_read:

static int binder_thread_read(structbinder_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;
       intret = 0;
       intwait_for_proc_work;
 
       //首先往返回结果buffer中写入一个BR_NOOP
       if(*consumed == 0) {
              if(put_user(BR_NOOP, (uint32_t __user *)ptr))
                     return-EFAULT;
              ptr+= sizeof(uint32_t);
       }
 
retry:
       //如果线程中没有处理事务,且其todo列表上也没有等待处理数据,则//wait_for_proc_work为true
       wait_for_proc_work= thread->transaction_stack == NULL &&
                            list_empty(&thread->todo);
       //错误情况检测,一般不会出现
       if(thread->return_error != BR_OK && ptr < end) {
       }
 
 
       thread->looper|= BINDER_LOOPER_STATE_WAITING;
       if(wait_for_proc_work)
              proc->ready_threads++;
 
       //解开在binder_ioctl中的锁,允许其他程序使用binder
       binder_unlock(__func__);
 
       if(wait_for_proc_work) {
              //这里也是错误情况检测,因为servicemanager在这之前就通过//BC_ENTER_LOOPER命令告知了驱动设置了thread->looper为//BINDER_LOOPER_STATE_ENTERED,所以不会进入错误流程
              if(!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
                                   BINDER_LOOPER_STATE_ENTERED))){
              }
              binder_set_nice(proc->default_priority);
              //因为filp->f_flags默认是阻塞的,所以会走else流程
              if(non_block) {
                     if(!binder_has_proc_work(proc, thread))
                            ret= -EAGAIN;
              }else
                     //阻塞在proc->wait等待队列上,等待被唤醒
                     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
                     //如果wait_for_proc_work为false则阻塞在thread->wait上
                     ret= wait_event_interruptible(thread->wait, binder_has_thread_work(thread));
       }
       //再次锁定binder锁,阻止其他程序使用binder
       binder_lock(__func__);
 
       if(wait_for_proc_work)
              proc->ready_threads--;
       thread->looper&= ~BINDER_LOOPER_STATE_WAITING;
 
       //被唤醒的时候必然是proc或者thread中todo列表上有待处理请求
       while(1) {
              uint32_tcmd;
              structbinder_transaction_data tr;
              structbinder_work *w;
              structbinder_transaction *t = NULL;
              //先接收请求数据
              if(!list_empty(&thread->todo))
                     w= list_first_entry(&thread->todo, struct binder_work, entry);
              elseif (!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 */
                            gotoretry;
                     break;
              }
              //错误情况检测,表示接收数据不正确
              if(end - ptr < sizeof(tr) + 4)
                     break;
              //判断请求的类型
              switch(w->type) {
              //一般情况下是该类型,表示一个请求事务,该type在binder_transaction函数中被设置
              caseBINDER_WORK_TRANSACTION: {
                     t= container_of(w, struct binder_transaction, work);
              }break;
              caseBINDER_WORK_TRANSACTION_COMPLETE:
              caseBINDER_WORK_NODE:
              caseBINDER_WORK_DEAD_BINDER:
              caseBINDER_WORK_DEAD_BINDER_AND_CLEAR:
              caseBINDER_WORK_CLEAR_DEATH_NOTIFICATION:
              }
 
              if(t->buffer->target_node) {
                     structbinder_node *target_node = t->buffer->target_node;
                     /*target设置成为Binder实体的指针,在发送方的时候target.handle设置为服务的引用号,在这个地方转换为具体服务的指针,这就是Binder通讯中引用和实体对应转换的秘密,对应于程序而言,就感觉像是调用本地接口一样访问了另一个进程的服务*/
tr.target.ptr = target_node->ptr;   
                     tr.cookie= target_node->cookie;
                     //对应发送方命令的BC_TRANSACTION,表示当前接收的数据是请求
                     cmd= BR_TRANSACTION;
              }else {
                     tr.target.ptr= 0;
                     tr.cookie= 0;
                     对应发送方命令的BC_REPLY,表示当前接收的数据是回复
                     cmd= BR_REPLY;
              }
              tr.code= t->code;
              tr.flags= t->flags;
              tr.sender_euid= from_kuid(current_user_ns(), t->sender_euid);
 
              tr.data_size= t->buffer->data_size;
              tr.offsets_size= t->buffer->offsets_size;
              tr.data.ptr.buffer= t->buffer->data + proc->user_buffer_offset;
              tr.data.ptr.offsets= tr.data.ptr.buffer + ALIGN(t->buffer->data_size,sizeof(binder_ptr));
              //返回结果通过系统调用返回给用户空间的servicemanager程序
              if(put_user(cmd, (uint32_t __user *)ptr))
                     return-EFAULT;
              ptr+= sizeof(uint32_t);
              if(copy_to_user(ptr, &tr, sizeof(tr)))
                     return-EFAULT;
              ptr+= sizeof(tr);
              break;
       }
done:
       *consumed= ptr - buffer;
       /*这种情况下表示服务程序不能处理所有请求,且线程数没有达到最大,则驱动通过BR_SPAWN_LOOPER告知服务程序创建新的线程来提供服务*/
       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)) /* theuser-space code fails to */
            /*spawn a new thread if we leave this out*/) {
              proc->requested_threads++;
              binder_debug(BINDER_DEBUG_THREADS,
                          "%d:%d BR_SPAWN_LOOPER\n",
                          proc->pid, thread->pid);
              if(put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
                     return-EFAULT;
              binder_stat_br(proc,thread, BR_SPAWN_LOOPER);
       }
       return0;
}

       从上面的流程中可以得知,一般情况下,接收的数据首先是一个BR_NOOP命令;对于servicemanager接下来会收到一个BR_TRANSACTION,该命令会紧跟着一个binder_transaction_data结构体。

五、服务请求处理流程

至此servicemanager与Binder驱动的交互过程就比较清晰了,接下来如果服务程序被唤醒的话,binder_loop中for循环体下res =ioctl(bs->fd, BINDER_WRITE_READ, &bwr);就会返回,且bwr中存放中接收到的服务请求,然后就调用binder_parse函数来处理具体的请求:

int binder_parse(struct binder_state *bs,struct binder_io *bio,
                 uint32_t *ptr, uint32_t size,binder_handler func)
{
   int r = 1;
   uint32_t *end = ptr + (size / 4);
   while (ptr < end) {
       uint32_t cmd = *ptr++;
       switch(cmd) {
              //该命令不做任何错误
       case BR_NOOP: break;
       case BR_TRANSACTION_COMPLETE:
       case BR_INCREFS:
       case BR_ACQUIRE:
       case BR_RELEASE:
       case BR_DECREFS:
              //BR_TRANSACTION表示接收到的数据时一个请求
       case BR_TRANSACTION: {
                     //把接收数据缓存区转换为一个对应的结构体
           struct binder_txn *txn = (void *) ptr;
           if ((end - ptr) * sizeof(uint32_t) < sizeof(struct binder_txn)) {
                ALOGE("parse: txn toosmall!\n");
               return -1;
           }
           binder_dump_txn(txn);
                     //func是一个函数指针(svcmgr_handler),作为参数传递给binder_loop
           if (func) {
                unsigned rdata[256/4];
                struct binder_io msg;
                struct binder_io reply;
                int res;
 
                bio_init(&reply, rdata,sizeof(rdata), 4);
                bio_init_from_txn(&msg,txn);
                            //处理具体服务请求
                res = func(bs, txn, &msg,&reply);
                binder_send_reply(bs,&reply, txn->data, res);
           }
           ptr += sizeof(*txn) / sizeof(uint32_t);
           break;
       }
       case BR_REPLY:
       case BR_DEAD_BINDER:
       case BR_FAILED_REPLY:
       case BR_DEAD_REPLY:
       default:
           ALOGE("parse: OOPS %d\n", cmd);
           return -1;
       }
    }
   return r;
}

       因此svcmgr_handler回调函数才是真正处理服务请求的功能函数,servicemanager程序的功能也在该函数中体现:

int svcmgr_handler(struct binder_state *bs,
                   struct binder_txn *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
   struct svcinfo *si;
   uint8_t *s;
   unsigned len;
   void *ptr;
   uint32_t strict_policy;
   int allow_isolated;
       //错误情况排查,表示接收到的数据服务引用号不是0(servicemanager的服务引用号)
   if (txn->target != svcmgr_handle)
       return -1;
      
/*错误情况排查,表示收到的字符串与约定的不一样,其实就是通过名字匹配来进一步确保通讯协议的稳定性*/
   strict_policy = bio_get_uint32(msg);
    s= bio_get_string8(msg, &len);
   if ((len != (sizeof(svcmgr_id)/* / 2*/)) ||
       memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {
       fprintf(stderr,"invalid id %s\n", str8(s));
       return -1;
    }
       /*具体的服务请求,从case中就可得知,servicemanager提供一下几种服务:查找服务、检查服务、添加服务、得到所有服务项*/
   switch(txn->code) {
   case SVC_MGR_GET_SERVICE:
   case SVC_MGR_CHECK_SERVICE:
       s = bio_get_string8(msg, &len);
       ptr = do_find_service(bs, s, len, txn->sender_euid);
       if (!ptr)
           break;
       bio_put_ref(reply, ptr);
       return 0;
 
   case SVC_MGR_ADD_SERVICE:
       s = bio_get_string8(msg, &len);
       ptr = bio_get_ref(msg);
       allow_isolated = bio_get_uint32(msg) ? 1 : 0;
       if (do_add_service(bs, s, len, ptr, txn->sender_euid,allow_isolated))
           return -1;
       break;
 
   case SVC_MGR_LIST_SERVICES: {
       unsigned n = bio_get_uint32(msg);
 
       si = svclist;
       while ((n-- > 0) && si)
           si = si->next;
       if (si) {
           bio_put_string8(reply, si->name);
           return 0;
       }
       return -1;
    }
   default:
       ALOGE("unknown code %d\n", txn->code);
       return -1;
    }
 
   bio_put_uint32(reply, 0);
   return 0;
}


六、小结

       至此servicemanager程序的代码细节只剩下查找服务、检查服务、添加服务、得到所有服务项如何实现,以及服务请求处理完后的内存释放、引用计数改变等善后工作了。查找服务、检查服务、添加服务、得到所有服务项这些功能的实现很简单,稍微看一下代码就可以明白,就是通过名字(程序指定)和引用号(驱动分配)建立一个列表,查找和检查服务的时候通过名字找到对应的引用号返回,添加服务的时候就是往列表中添加一项,列出所有服务项就是遍历列表。而请求处理完后的内存释放即引用计数更新跟之前描述的与驱动交互的过程差不多,多分析一下代码应该可以得到答案。

       还有一个流程没涉及的地方就是servicemanager被唤醒时从proc或者thread的todo列表中得到数据是什么时候添加的,而这也是下一篇文章要说明的问题。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值