工作生活

4.ServiceManager

2019-07-03  本文已影响0人  Wi1ls努力努力再努力

service_manager 作为打开 Binder 驱动的上下文管理者,是打开 "/dev/binder"的第一人。
特别注意的是 service_manager 自身实现了 binder.c。
service_manager由 init.rc 解析的时候初始化,入口函数是 main( )@service_manager

//service_manager.c
int main(int argc, char **argv){
  struct binder_state *bs;
  //调用的是 ./frameworks/native/cmds/servicemanager/binder.c 中的 binder_open( )来打开驱动,申请 128K 字节的内存
  bs = binder_open(128*1024)
  //和 Binder 驱动通讯,申请成为上下文管理者
  binder_become_context_manager(bs);
  ...  
  //开启 loop,处理 client 发来的请求,大部分都是 Service 的注册和查询
  //处理函数就是 svcmgr_handler 这个函数指针
  binder_loop(bs, svcmgr_handler);
}
struct binder_state
{
  int fd; //service_manager 打开的 /dev/binder 的文件描述符
  void *mmaped; //映射的内存
  size_t mapsize; //映射的内存大小
}
//参数 mapsize 为 128K
struct binder_state *binder_open(size_t mapsize){
  strcut binder_state *bs;
  struct binder_version vers;
  //分配空间
  bs = malloc(sizeof(*bs));
  //系统调用 Binder 驱动的 binder_open( )
  //前面已经描述了,为了打开驱动,在内核空间产生描述 service_manager进程的 binder_proc
  bs->fd = open("/dev/binder", O_RDWR);
  //向 Binder 驱动发起 ioctl( )通讯,检查 Binder 版本
  ioctl(bs->fd, BINDER_VERSION, &vers);
  bs->mapsize = mapsize;
  //映射,将一段用户空间和内核空间映射到物理内存。可以知道 service_manager 指定的映射大小为 128K。此时 mmaped 指向的大小为 mapsize 的内存,便是和 Binder 驱动通信的数据内存
  bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
}

在 service_manager 执行完 binder_open( )后,在 Binder 驱动中便有了 service_manager 进程对应的结构 binder_proc,同时还将 bs->mmaped 映射到了一块物理空间(Binder 驱动也有一块映射到相同的物理空间);
然后 service_manager 进行 ioctl( )通信告诉 Binder 驱动自己要称为上下文管理者。
接下来不得不看重要的通讯函数 binder_ioctl( ) 了
函数原型

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

可以猜测,通过判断 cmd 的类型来执行不同的操作

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
  int ret;
  //从 filp 取出当前进程的 binder_proc
  struct binder_proc *proc = filp->private_data;
  //休眠当前进程
  ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
  //获得当前线程的信息
  thread = binder_get_thread(proc);
  swicth(cmd){
    ...
    case BINDER_SET_CONTEXT_MGR:
      bindr_context_mgr_node = binder_new_node(proc, 0, 0);
      binder_context_mgr_node->local_weak_refs++;
      binder_context_mgr_node->local_strong_ref++;
      binder_context_mgr_node->has_strong_ref = 1;
      binder_context_mgr_node->has_weak_ref = 1;
  }
//从参数可以指定,service_manager 在 Binder 驱动对应的 Binder 实体binder_node的 ptr 和 cookie 都是 0;
  static struct binder_node *binder_new_node(struct binder_proc *proc, binder_uintptr_t ptr, binder_uintptr_t cookie){
    struct rb_node **p = &proc->nodes.rb_node;
    struct rb_node *parent = NULL;
    struct binder_node *node;
    //当前进程首次进来,proc->nodes 维护的 Binder 实体红黑树中没有数据
    while(*p){
      //以下操作是遍历这个红黑树 rb_root,寻找是否有匹配的 binder_node,如果有说明该 binder_node 已经在 Binder 驱动注册,直接返回。
      // 否则创建改 binder_node,并且加入到binder_proc 的 维护binder_node的红黑树中。
      parent = *p;
      //通过成员变量的指针反推出其所处的对象的指针
      node = rb_entry(parent, struct binder_node, rb_node);
    
      if(ptr < node->ptr)
        p = &(*p)->rb_left;
      else if(ptr > node->ptr)
        p = &(*p)->rb_right;
      else
        return NULL;
    }
    //分配 binder_node 内存
    node = kzalloc(sizeof(*node), GFP_KERNEL);
    //将 binder_node 添加到当前进程的红黑树
    rb_link_node(&node->rb_node, parent, p);
    rb_insert_color(&node->rb_node, &proc-nodes);

    node->proc = proc;
    node->ptr = ptr;
    node->cookie = cookie;
    node->work.type = BINDER_WORK_NODE;
    INIT_LIST_HEAD(&node->work.entry);
    INIT_LIST_HEAD(&node->async_todo)
  }
}

题外,对 rb_entry( )比较感兴趣,看方法的实现貌似是根据 binder_node 的成员变量 rb_node 的地址,反推 binder_node 的地址。因为 Java 屏蔽了指针这个概念,因此在 Java 中是不可能实现的。因为 C++是可以直接操作指针和内存的,让这个看似无法理解变成了可能。来看看是怎么实现的

struct rb_node *n = proce->nodes.rb_node;
struct binder_node *node;
node = rb_entry(n, struct binder_node, rb_node);

//rbtree.h
#define rb_entry(ptr, type, member) container_of(ptr, type, member)

//kernel.h
#define container_of(ptr, type, member) ({      \
  const typeof( (type*)0)->member ) *__mptr = (ptr);      \
  (type *)( (char*)__mptr - offsetof(type, member) );})

可以看到,是根据 member 的指针,再通过 member 在 type 中的偏移量,反推到 ptr 的指针。
可以这么做的依据便是 C++对于对象内存分配的原理了。
由于内存对齐的存在,虚拟机引擎会对类的对象模型的成员变量会进行重排以减少对象的内存大小。而 C++的对象内存的优化需要程序员自己进行。于是根据一个成员变量在一个类或者结构中的偏移就已经确定下来了。


这样在 Binder 驱动描述 service_manager 进程的结构 binder_proc 中便将该服务对应的Binder 实体即 service_manager 服务在内核中创建了一个对应的 binder_node,并且其 ptr 和 cookie均为 0。同时将 binder_node->rb_node 添加到了 binder_proc 的 nodes 以维护改进程所有的 Binder 实体。同时这个 binder_node 也称为了 Binder 驱动的binder_context_mgr_node,即这个 Binder实体就是 service_manager,其他进程需要注册或者得到服务,直接通过这个binder_context_mgr_node转发就可以了。

上一篇下一篇

猜你喜欢

热点阅读