从Activity的启动细窥BinderIPC(1)

android基于linux内核,有丰富的进程通信机制,比如传统的pipe,signal,trace机制,然而android毅然放弃祖传遗产 另起一招,名曰BinderIPC。。。

更新

下面来进行详细的讨论:

本文章从最简单最基本的activity的启动说起,通过观察callstack,发现activity的启动要从native的main函数,即kernel层的系统调用启动,然后通过callstack可以发现,在我们触摸图标,系统将会启动一个luancher进程,这个进程会给一个消息给ActivityManagerService,这是一个service binder(在内核中存在于system server进程的一个线程中,这个进程还有很多不同的binderserver,不过都运行在不同的线程)ActivityManagerService会检测这个应用程序的入口activity,一般这个应用理应获得一个属于它自己的进程,有时候事情很不幸,app并没有获得自己应得的.
这时候有个重要的角色要登场啦,那就是一个叫做zygote的进程,看过callstack的估计对这个进程会觉得有点熟悉,看这个家伙的名字这么奇葩,查查字典也并不知道是什么意思,其实它一点都不神秘,在kernel层看来,它只干两件事情,一个就是调用下进程的fork函数,fork出来的进程给谁呢,当然是给我们的app使用啦,只是调用个fork函数要它何用,好歹是个进程,所以它还干了一件伟大的任务,那就是完成了堆内存的共享,zygote首先会android的公用类进行预加载以及一个初始化过程,数据保存在它的堆内存中(heap),我们的系统在zygote上安装了个间谍,本质上就是一个socket,对于socket,肯定有一个用于监听的端口,当ActivityManagerService通知它还没有分配进程给app时(以一个startcommand的形式),就会触发回调进行fork新的进程理论上是跟zygote共享堆空间的,怎么共享呢,首先我们知道在linux的内核空间,内存都是以一种映射关系存在的,每个进程都拥有自己的一块线性并且连续的虚拟内存,系统会完成内存的映射(mapping),将虚拟的的内存映射到真实的一块物理地址,以此来保证每个进程的安全性(跟线程池的线程安全同样重要哈),进程间的交换信息,在android中叫做transaction,都是通过binder IPC机制实现,在kernel层,binder Driver就是相当于构造一块虚拟的映射空间来共享两个进程的数据,对于zygote和我们的app也是类似的道理,构造一块虚拟的共享虚拟内存来共享zygote的heap空间数据,这样就可以将zygote跟app的新进程连接起来啦,下面分析两种情况,
首先,我们的app要请求读取heap空间,这个毫无疑问,看了就走,好像上面的整个体系没有什么变化,好吧,那如果app要往内存中写入什么东西呢,这下有事情要发生了,heap中东西改变了,显然共享区的数据也会被修改,那zygote呢,神奇的地方就是zygote也被修改了而且是直接放弃原来的内存页,选择重开天下,不过系统比较机智,在另开辟一个内存页时,不忘把上次的zygote heap内存中的数据备份,这样相当于把整个体系重构了一遍,一个新的link就产生了,可见,zzygote是可以允许app肆无忌惮的读写的,而且还会很聪明地备份。
有人会说,zygote这么做是何苦啊,这就是dvm(dalvik vm)的巧妙之处之一了,app通过java语言编写总是需要dvm的运行环境才能转化为android平台的可执行程序代码,然而这么多的app都需要,dvm就觉得忙不过来了,至于为什么要搞这么多进程,所有app就不能共用一个dvm进程么,当然android不是这么玩得为了考虑安全性,还是要把进程隔离开来啦,但这样也累啊,android最后就巧妙的想了个办法,人在忙不过来的时候总会想要是我可以分身该多好啊,有句话叫分身乏术,然而dvm却有这个技能,它找到zygote进程(其实也是dvm的一个server),每次系统启动dvm只完成一次实例化,后面都是通过zygote进程的克隆体,对于heap空间也是一笔不小的节约啊,这就是所谓的一次实例化,然而多次复用,zygote因此有个外号,叫进程孵化器。
我们来看看zygote进程是怎样被构造的,ZygoteInit就是它的启动类,调用个main()方法就好啦,再注册一个ZygoteSocket用于监听进程孵化的请求,runSelectLoop()就是用于轮询端口对请求事件进行监听,read the funcking code,好,我们来看下这个轮询函数的源代码,看代码时关注注释的地方就可以了,精髓就在哪些地方。

private static void runSelectLoopMode() throws MethodAndArgsCaller {
  //死循环果断用于对事件的轮询
  while (true) {
     //...
      try {
          //linux内核的fd文件描述符的形式来进行时间轮询
          fdArray = fds.toArray(fdArray);
          index = selectReadable(fdArray);
      } catch (IOException ex) {
          throw new RuntimeException("Error in select()", ex);
      }
      if (index < 0) {
          //这个明显不可能发生,马上抛出异常
          throw new RuntimeException("Error in select()");
      } else if (index == 0) {
          //这个代表有client端提交连接请求,这里的client端就是ActivityManagerService啦
          //构造一个用于连接的对象,进行连接
          ZygoteConnection newPeer = acceptCommandPeer();
          //连接这个地方显然要阻塞那么一段时间,就耐心等下咯
          //连接成功后就往client列表中添加新加入的伙伴
          peers.add(newPeer);
          fds.add(newPeer.getFileDesciptor());
      } else {
         //这代表client发送孵化进程的请求过来
         //此时便需要调用ZygoteConnection的runOnce方法孵化进程,在内核其实就是fork一个进程
          boolean done;
          done = peers.get(index).runOnce();
          if (done) {
              //完事了就把人家client踢掉咯
              peers.remove(index);
              fds.remove(index);
          }
      }
  }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值