美文网首页工作生活
5. native层的服务

5. native层的服务

作者: Wi1ls努力努力再努力 | 来源:发表于2019-07-04 10:50 被阅读0次
  1. 注册 native 服务到 service_manager。
    以 SurfaceFlinger 服务为例。
//main_surfaceflinger.cpp
int main(int, char**){
  ProcessState::self( )->setThreadPoolMaxThreadCount(4);

  sp<ProcessState> ps(ProcessState::self);
  ps->startThreadPool( );
  sp<SurfaceFinger> flinger = new SurfaceFlinger( );
  setPriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY);
  flinger->init( );
  sp<IServiceManager> sm(defaultServiceManager( ));
  sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, flase);
  
  flinger.run( );
  return 0;
}

首先来看 defaultServiceManager( )是个什么东西,参见defaultServiceManager( )
于是

gDefaultServiceManager() = new BpServiceManager(new BpBinder(0));

这时候调用 addService( )方法

//IServiceManager.cpp
virtual status_t addService(const String16$ name, const sp<IBinder>& service, bool allowIsolated){
  Parcel data, reply;
  data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor( ));
  data.writeString16(name);
  data.writeStrongBinder(service);
  data.writeInt32(allowIsolated ? 1 : 0);
  status_t err = remote( )->transact(ADD_SERVICE_TRANSACTION, data, &reply);
  return err = NO_ERROR ? reply.readExceptiomCode( ): err;
}

先来关注一波 Parce的 writeStrongBinder(service)等方法Parcel 写入 SurfaceFlinger 服务本地对象; 此时 SurfaceFlinger 变被转换成了 flat_binder_object 对象存储在 Parcel 中,flat_binder_object ->type = BINDER_TYPE_BINDER, flat_binder_object ->binder 字段保存了 SurfaceFlinger 服务本地对象的弱引用计数对象,flat_binder_object->cookie 保存了 SurfaceFlinger 服务本地对象的指针。
关于 flat_binder_object 的描述在 Binder 的数据结构介绍中也有介绍


然后来关注 remote( )->transact(ADD_SERVICE_TRANSACTION, data, &reply);

我们知道整理的 remote( )是一个 BpBinder 对象,其 mHandle 为 0。

//BpBinder.cpp
status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags){
  //不传递 flags 的时候,flags 为 0
  status_t status = IPCThreadState::self()->transact(mHandle, code, data, reply, flags);
}

IPCThreadState是一个线程单例, IPCThreadState::self()返回该线程的一个代表。

//IPCThreadState.cpp
status_t IPCThreadState::transact(int32_t handle, uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
  //此时 flags 为 0
  error = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);

  if(reply){
    error =waitForResponse(reply);
  }else{
    Parcel fakeReply;
    error = waitForResponse(&fakeReply);
  }
}

从上面看出,先用 writeTransactionData( )打包请求数据,再用 waitForResponse( )发送请求。

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags, int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer){
  binder_transaction_data tr;
  tr.target.handle = handle;
  tr.code = code;
  tr.flags = binderFlags;
  tr.cookie = 0;
  tr.sender_pid = 0;
  tr.sender_euid = 0;

  if(err == NO_ERROR) {
    //将 Pacel 中的数据转移到 binder_transaction_data 中
    tr.data_size = data.ipcDataSize( ); //数据大小
    tr.data.ptr.buffer = data.ipcData( )  //数据保存的内存地址
    tr.offsets_size = data.ipcObjectsCount( )*sizeof(binder_size_t); //Binder 对象在 Parcel 中以 flag_binder_object 存在
    tr.data.ptr.offsets=data.ipcObjects( );
  }else if(statusBuffer){
    //本例中 statusBuffer 为 NULL,不分析
  }
  //此处的 cmd 为BC_TRANSACTION,表示向 Binder 驱动发送请求调用
  // mOut 是 Parcel 类
  mOut.writeInt32(cmd);
  mOut.write(&tr, sizeof(tr));
}

此处的 binder_transaction_data 在 Binder 的数据结构介绍已经介绍过了。此处 handle = 0,代表目标 Binder 是代理,此处代表 BpServiceManager。code = ADD_SERVICE_TRANSACTION, ServiceManager 进程收到请求后,根据这个 code 判断具体需要调用的方法。

Binder 请求

可以看到现在在 mOut 中写入了一个 cmd 为 BC_TRANSACTION表示此次是 Client 向 Binder发送请求调用。写入了一个包裹了请求数据的 binder_transaction_data;改包封装了向 Server 请求调用的具体参数(包括以 flat_binder_object 包裹的 IBinder 对象)

status_t IPCThread::waitForResponse(Parcel *reply, status_t *acquireResult) {
  //acquireResult 默认为 NULL,此处就为 NULL,
  uint32_t cmd;
  int32_t err;
  while(1) {
    if((err = talkWithDriver() ) < NO_ERROR) break;
    ...
  }
}

taklWithDriver( )是真正和 Binder Driver 通信的的方法,下面的方法是处理通信结果的。

statis_t IPCThreadState::talkWithDriver(bool doReceiver){
  //doReceiver 默认为 true,此次便取默认值 true

  binder_write_read bwr;

  const bool needRead = mIn.dataPosition( ) >= mIn.dataSize();
  const size_t outAvail = (!doReceiver || needRead) ? mOut.dataSize() : 0;
  bwr.write_size = outAvail;
  //指向实际的参数的内存地址
  bwr.write_buffer = (uintptr_t)mOut.data( );
  if(doReceiver && needRead){
    bwr.read_size = mIn.dataCapacity( );
    bwr.read_buffer = (uintptr_t)mIn.data( );
  }else {
    bwr.read_size = 0;
    bwr.read_buffer = 0;
  }
  ...
  ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr);
}
//binder.c
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;
  unsigned int size = _IOC_SIZE(cmd);
  void __user *ubuf = (void __user *)arg;

  thread = binder_get_thread(proc);
 switch(cmd){
  case BINDER_WRITE_READ:
   struct binder_write_read bwr;
   //内核空间无法使用用户空间的虚拟地址,因此需要经过 copy_from_user 才可以使用
  copy_from_user(&bwr, ubuf, sizeof(bwr));
  if(bwr.write_size >0){
    //此时,可以认为内核空间的 binder_write_read bwr 和用户空间的 binder_write_read 等价
    ret = binder_thread_write(proc, thread, bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
  }
 ...
  }
}

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;
  void __user *buffer = (void __usr *)(uintptr_t)binder_buffer;
  void __user *ptr = buffer + *consumed; //此处认为刚开始处理,因此 ptr 就是开始
  void __user *end = buffer + size;

  while(ptr < end && thread->return_error = BR_OK){
    //前面说过,这里的 buffer 内的数据是 cmd + content的形式存在的
    get_user(cmd, (uint32_t __user *)ptr); //先获取 cmd
    ptr += sizeof(uint32_t);
    if(_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
      //统计工作
      binder_stats.bc[_IOC_NR(cmd))]++;
      proc->stats.bc[_IOC_NR(cmd))]++;
      thread->Stats.bc[_IOC_NR(cmd))]++;
      swicth(cmd) {
        case BC_TRANSACTION:
        case BC_REPLY: {
          struct binder_transaction_data tr:
          //BC_TRANSACTION 为 cmd 下,数据内容为 binder_transaction_data;
         //同理,内核空间需要 copy_from_user( )才可以使用
          copy_from_user(&tr, ptr, sizeof(tr));
          ptr += sizeof(tr);
          binder_transacaction(proc, thread, &tr, cmd == BC_REPLY);
          break;
        }
      }
    }
  }
}

binder_transaction( )内部非常复杂,有必要单独拎出来

//binder_proc * proc 代表 SurfaceFlinger进程
//binder_thread *thread 代表SurfaceFlinger 进程中发起本次 IPC 的进程
//binder_transaction_data *tr 代表 SurfaceFlinger 传递给 service_manager 的参数
//int reply 在此处为 false
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;
  binder_size_t *offp, *off_end;
  binder_size_t off_minl
  struct binder_proc *target_proc;//目标进程,在这里代表 service_manager 进程
  struct binder_thread *target_thread=NULL;//目标线程
  struct binder_node *target_node =NULL; //目标 Binder 对应的 binder 实体
  struct list_head *target_list; //目标 todo 任务队列
  wait_queue_head_t *target_wait; //目标 wait 队列
  struct binder_transaction *in_reply_to =NULL;
  struct binder_transaction_log_entry *e; //日志相关,可以忽略
  uint32_t return_error;
  if(reply){
    //这里是 false,分析
    ...
  }else{
    if(tr->target.handle){
      //在本例中,target.handle = 0,不进入
      ...
    }else{
      //在 service_manager 进行 setContextManager的时候创建
      target_node = binder_contet_mgr_node;
    }
    //从 target_node 找到目标进程
    target_proc = target_node->proc;
    ...
    //一些额外的校验,还有 from_parent 和 to_parent 的依赖判断
  }
  //指定了线程,则使用指定线程的 todo 和 wait,否则使用目标进程的 todo 和 wait
  if(target_thread){
    //如果指定了线程
    target_list = &target_thread->todo;
    target_wait = &target_thread->wait;
  }else{
    //未指定目标线程
    target_list = &target_proc->todo;
    target_wait = &target_proc_wait;
  }
  //为 binder_transaction 分配内存,表示一个事务
  t = kzalloc(sizeof(*t), GFP_KERNEL);
  binder_stats_created(BINDER_STAT_TRANSACTION);
  //为 binder_work 分配内存,表示一个待处理的工作项
  tcomplect = kzalloc(sizeof(*tcomplete), GFP_KENER);
  binder_stats_create(BINDER_START_TRANSACTION_COMPLETE);

  if(!reply &!(tr->falsg & TF_ONE_WAY))
    t->from = thread;
  else
    t->from = NULL;

  t->send_euid = proc->tsk->cred->euid;
  t->to_proc = target_proc;//service_manager进程
  t->to_thread = target_thread;
  t->code = tr->code;//ADD_SERVICE
  t->flags - tr->flags;
  t->priority = task_nice(current);
  //从 target_proc 中的 buffer 中选取合适的内存块
  t->buffer = binder_alloc_buf(target_proc, tr->data_size, tr_offset_size, !reply &&(t->falses & TF_ONE_WAY));

  t->buffer->allow_user_free = 0;
  t->buffer->transaction = t;
  t->buffer->target_node = target_node;

  if(target_node)
    //增加强引用计数
    binder_inc_node(target_node, 1, 0, NULL);

  //--开始分析传输的数据中的 IBinder 对象
  //如果是 binder 实体,在红黑树添加节点
    
  //我们知道,binder 对象在 data 后面
  offp = (binder_size_t *)(t->buffer-data + ALIGN(tr->data_size, sizeof(void *)));uintptr_t
  copy_from_user(t->buffer->data, (const void __user *) tr->data.ptr.buffer, tr->data_size));

  copy_from_user(offp, (const void __user *)(uintptr_t)tr->data.ptr.offsets, tr->offsets_size)
  //此时 offp 边是传输的 IBinder对象首,off_end 是传输的 Ibinder 对象未
  off_end = (void *) offp + tr->offsets_size;
  off_min = 0

  for(; off < off_end; offp++){
    //遍历处理传输的 IBinder 对象,这里传输的对象的 SurfaceFlinger 的 Binder 实体
    fp = (struct flag_binder_object *)(t->buffer->data + *offp);
    off_min = *off +sizeof(struct flat_binder_object);
    switch(fp->type){
      case BINDER_TYPE_BINDER:
      case BINDER_TYPE_WEAK_BINDER:{
        //只分析此分支
        struct binder_ref *ref;
        //查看当前进程是否已经有改 Binder 实体的 binder_node结构,当然是从进程的 proc->nodes 红黑树查询
        struct binder_node *node = binder_get_node(proc, fp->binder);
        if(node == NULL){
          //表示该 SurfaceFlinger Binder 实体是第一次穿越 Binder 驱动,则创建 binder_node 对应 SurfaceFlinger
          node = binder_new_node(proc, fp->binder, fp->cookie);
          node -> min_priority = fp>flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
          node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
          //为目标进程创建一个 Binder 实体对应的 Binder 引用 binder_ref,并且保存到目标进程的管理 binder_ref 的红黑树中(如果目标进程没有改 Binder 实体对应的 Binder引用)
          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;
        }
      }
    }
  }
 t->work.type = BINDER_WORK_TRANSACTION;
  //将该事务插入到 target_list,即 Todo 队列
  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;
}

  • 通过 defaultManagerService( )获得 service_manager 在本地的代理 BpServiceManager(BpBinder(0))
  • 打包参数到 Parcel,其中 IBinder 对象封装为 binder_flat_object 对象
  • 通过 IPCThreadState 与 Binder 驱动通信
  • 将 Parcel 中的参数转到 binder_transaction_data中,在其中写入 Parce 的数据大小,数据内存地址,IBinder 对象大小,IBinder 对象偏移。
  • 然后将 binder_transaction_data 写入 IPCThreadState 的 Parcel mOut中,cmd 为 BC_TRANSACTION 表示此次是希望 Binder 驱动转发请求。此时这个 mOut 在内存分布上是:
Code = BC_TRANSACTION Content of Code = binder_transaction_data
  • 将 mOut 中的数据内存地址,即 code+content 保存到 binder_write_data。随后 ioctl( )与 Binder Driver 通信。ioctl( )层的 cmd 为 BINDER_WRITE_READ

上面的通讯过程有一种网络分层的感觉。在 SurfaceFlinger 和 service_manager 的服务层的 code 就是希望调用的方法代码:ADD_SERVICE。在 IPCThread 层代码是 BC_TRANSACTION/BR_TRANSACTION/BC_REPLY/BR_REPLY。在 Binder 驱动层的 cmd 就是 BINDER_WRITE_READ/BINDER_SET_CONTEXT_MGR等。
SurfaceFlinger/service_manager 层用 Parce 封装数据写入该层的 cmd。在 IPCThread 层用 binder_transaction_data 封装 Parcel 数据,同时写入该层的 cmd。在请求 Binder 驱动时,将cmd 与binder_transaction_data 与用 binder_write_read 封装

相关文章

网友评论

    本文标题:5. native层的服务

    本文链接:https://www.haomeiwen.com/subject/stufhctx.html