android java进程管理(一)之进程模型

16 篇文章 0 订阅
16 篇文章 2 订阅

本地进程和Java进程的关系

记得曾经在研究gnome桌面系统的时候,看到开发者的一句话:面对一个庞大的开源项目,就像把我们扔到一个一线城市的市中心,然后让你通过自己的双脚丈量出整个地图,何其复杂,真的是一脸懵逼呀,每当这个时候,我是多么希望有人可以总结一份模型文档,超脱代码的境界,深入的了解操作系统是怎么架构出来的,其实在最早开发android系统的时候,一定是会有这样的建模文档的,只不过google不会开放出来,虽然google号称开源,但为了商业利益,系统的文档是不会公布的,所以我们只能在代码和注释中寻找蛛丝马迹,那是一种何尝痛苦的感觉呀!
其实研究开源代码的过程,就是不断在大脑中建模的过程,模型建立起来啦,一切流程都是那么的顺,那么模型和编程方法是什么关系呢?首先面向对象和面向过程这两种编程方法的界限是很模糊的,或者可以说这两种编程方法的统一的,面向过程中有对象(其实一个.c文件就是一个模块,或者可以以一个对象来对待),而面向对象编程最终还是对象合作为一个执行流实现功能(哦哦,听起来就像是废话),所以模型建立起来,使用什么样子的编程方法和编程语言实现是没有本质上的区别的,就像ubuntu操作系统模型可以使用c\c++语言实现,如果使用java语言改写又何尝不可呢?(不过就这有重复制造轮子的嫌疑,但重复制造的轮子更耐用那就另当别论了),对于android也一样,android的系统管理、高级视窗系统都是使用java语言实现的,在相同的模型下使用c\c++语言改写也是没有问题的,以上我想表达的就是模型相对于编程方法和语言的独立性,建模一定是在开发的初始阶段,现在我们在只有源码的情况下,想要自己从头反推出模型是很难的,如果你想提高研究开发操作系统的效率,先花点时间阅读本节是有必要的,本节的目的就是介绍进程模型,当看到推倒出来的模型时,你一定会觉得原来如此简单,对于这么简单的原理模型,是不是有一种想修改一下的冲动?或者来点大的动一个大手术,修改后在写代码实现一下?这样一个全新的操作系统是不是就是专属于我们自己了呢!哈哈,真是不识庐山真面目,只缘身在此山中,ok,从这一刻开始,让我们先研究一下模型,开始我们自己操作系统的第一步–也是最重要的一步,万事开头难,只要迈出第一步(像神级别的Unix linux windows, 哪一个不是因为工程师的一时性起才造就的神话呢?), 下一个神话没准就是你–快速走上人生巅峰,迎娶白富美不在是梦想(意淫一下,来点动力,为了女神)。
让我们先来看一下android有哪些java相关的进程?我们都知道android的内核使用的是linux kernel,所以android的启动原理和ubuntu 就有相似性,都是在内核启动完毕后,启动第一个用户进程init,然后由这个init来解析一个自定义规则的脚本init.rc,通过这个脚本配置系统,启动其他守护进程,所以在脚本中启动的进程都是init的子进程,脚本中类似下面的命令是用来启动进程的

service zygote /system/bin/app_process -Xzygote /system/bin –zygote –start-system-server

  • 1.service 代表这是一条启动进程的命令
  • 2.zygote 是启动进程的名字
  • 3./system/bin/app_process 进程执行的二进制文件路劲
  • 4.-Xzygote /system/bin –zygote –start-system-server 进程启动参数

下面我们先来看一下init 启动了多少个子进程(子进程一般被理解为儿子,为什么不是女儿呢,个人觉得女儿应该更合适,因为子进程还是要生子进程的,更重要的是现在是女权社会呀,遍地都是得拉公主病的,将来生了孩子能保证随自己姓就万幸了,不能要求太高,所以从这里开始我们改为女儿),看一下这个init的老太太生了多少个公主:
这里写图片描述

  • 1.绿色的是鼻祖进程
  • 2.蓝色的是java 进程
  • 3.其他颜色的是本地进程
  • 4.箭头代表父子关系

zygote和system_server进程的关系

这个老太太还真挺能生,除了zygote 这个女儿比较特殊外,其他的全是本地进程(c\c++),zygote其实在一开始的时候也是一个纯本地进程,在成长的过程中zygote启动了一个引擎–java虚拟机,使她变成唯一可以执行java代码的女儿,因此这个女儿继承了生育能力,开始无限繁殖apk进程,其中system_server 进程就是她的长女,我们将来要分析的java 语言实现的重量级系统服务基本全在这个进程中,包括但不限于AMS、WMS、PMS等等,这个长女一但长大成人(启动完毕),会来一次身份的华丽大转身,确切的说,她和zygote的关系不在是母女了,应该是变身为zygote的老公(这个只是形象的比喻而已,进程在创建之后关系是不会改变的),zygote 全职负责生孩子,system_server提出要孩子的请求,只要有请求,zygote就会满足立马生一个孩子,然后这个孩子向自己的爸爸报告,system_server就知道自己的老婆又生了一个孩子,然后就把这个孩子加入管理队列,开始管理这个孩子(apk进程),每一个孩子都需要接受父亲的命令,父亲说什么做什么,同时作为父亲,也会反过来满足孩子的请求,做一个合格的父亲,其乐融融,组成一个五好家庭(不过感觉作为父亲好像累了点,不像现实中洒完种就可以不管了),到次为止,日子算是过起来啦,java进程的模型看起来应该就是下面这个样子了,完美了一点:

这里写图片描述

1.绿色双箭头代表binder通信
2.黑色粗箭头代表socket通信
3.黑色细箭头代表父子进程关系

为什么system_server 需要通知zygote来孵化进程,而不是system_server 自己直接孵化呢? 或者是通过init 来创建呢? 主要有两点原因:一是为了安全,二是优化apk进程启动的效率,这里需要普及一下进程创建的最基本的一些原理,进程都是由父进程直接fork出来的,在刚刚创建的时候,子进程和父进程是共享地址空间的,就像同一个进程中不同的线程表现是一样的,看到这点是不是会有点小兴奋,如果这样子的,是不是可以攻击父进程啦(apk进程攻击zygote进程)?事情当然没有那么简单,这种父子进程共享是有条件的,当父进程或者子进程想要修改数据的时候,内核会为这个进程复制一份数据再修改,这就是大名鼎鼎的写时复制机制,所以只要没有修改的部分,父子进程还是共享的,而对于代码段,属于只读数据,一般不会被修改,就会一直处于共享状态,所以如果system_server直接孵化apk进程的话,apk进程会共享system_server进程的所有服务,通过一定的技术,很容易实现直接调用,到时候每一个apk进程就都会变成system_server服务进程了,系统还怎么运行?! 而没有使用init进程来孵化apk进程,主要是出于效率的考虑,一个进程启动虚拟机、配置虚拟机是很耗时的操作,android的apk进程又是只有在使用时才会启动,会很影响用户体验,所以zygote就闪亮登场拉,zygote作为一个虚拟机运行的最小环境,可以通过写时复制技术直接共享这个虚拟机环境给apk进程使用,apk进程就不需要在启动一次虚拟机、配置一次虚拟机了,大大提高了启动效率的同时,也节约内存占用(因为虚拟机环境在所有的java进程(包括zygote、system_server、apk进程)中是共享的)。
那这些进程和我们熟悉的apk之间的关系呢? 这个才是我们最最关注的,是的,这些所谓的孩子和父亲就是apk运行的环境,最常用的场景下,一个孩子运行一个apk,像camera这样的进程,只要有一个camera apk就可以了,但俗话说,龙生九子,各不相同,当然会有一些牛x的进程想要运行一组apk,android也是支持的,这个时候,一定会有一些同学迫不及待的想要知道android是怎么实现共享apk的了,哈哈,不急,很多时候我们之所以被代码的汪洋大海包围,不是我们不够聪明,是因为我们太聪明,想的太多,关注点太深,才会泥足深陷,这个曾经我在研究opengl的时候,opengl的开篇就有醒目的提醒:不要过早的关注像素级别的操作,把关注点集中到建模上,事情会马上变得简单很多。我们接下来的关于android 操作系统的文章,也是这个思路,开篇先介绍模型,然后所有的叙述不会超出子系统的范围,例如:一个activity的显示: 涉及到的子系统包括:ActivityManagerService, ActivityThread, WindowManagerService , SurfaceFlinger(这四个系统都会分开来讲解); 为什么需要分开来讲解,一个重要的原因是每一个子系统都是独立并行运行的,如果强行将各个系统按照一个执行流的方式分析的话,很多现象很难理解,如果有人研究过《深入理解linux内核》这本kernel圣经的话,就会发现,每一章都是一个子系统,每一个子系统的讲解基本都可以分为三个部分:一是模型和原理 二是数据结构 三是核心算法(函数);例如open()这个系统调用按如下的流程讲解的话:虚拟文件系统->具体的文件系统->通用块层->相应的总线驱动框架->磁盘驱动,一口气下来,对于对kernel一无所知的人来说,肯定懵逼(当然我是可以理解的,因为我的知识架构已经建立起来,对于任何一个知识点已经可以自动架构在特定的子系统模型中,我的目标就是将这样子的子系统模型快照在你的脑海中),方法很重要,正确的方法可以大大提高我们的效率,让我们汲取这些好的方法,踏着前辈的脚印前行吧!

system_server和apk进程之间的关系

以上涉及了一些方法论的东西,接下来继续完善我们的进程模型。zygote 在生完孩子(apk进程)之后,就撒手不管了,交给system_server进程管理,正如上文提到的,system_server 进程包含很多java服务,管理进程只是其中的一个服务(AMS)负责,这个服务(AMS)对所有的孩子都是开放的,也就是apk进程可以直接持有这个服务的客户端(ActivityManagerNative),但是这个时候system_server 进程还不知道有了这个孩子(这个父亲当的也是够悲惨的),所以这个孩子进程启动起来后第一件是就是到父亲那里报道(父亲大人,我可以接收您的命令了,给我发送命令吧),然后父亲完善这个孩子的花名册(ProcessRecord),才算是确定父子关系,建立了纽带,父子之间的关系看起来如下图:

这里写图片描述

  • 1.ActivityManagerService: 进程管理 和 四大组件管理
  • 2.ActivityManagerNative: ActivityManagerService 的客户端(每个apk进程一个实例)
  • 3.ApplicationThread: 负责接收ActivityManagerService发送给进程的指令,然后异步分发给UI线程(每个apk进程一个实例)
  • 4.IApplicationThread:ApplicationThread 的客户端,AMS通过这个对象发送命令给apk进程
  • 5.ActivityThread:apk进程的入口,和真正控制所有组件的生命周期
  • 6.ProcessRecord:AMS中的进程描述符

一个apk进程的启动基本流程:

  • 1.system_server通过socket发送请求给zygote,当前执行流结束
  • 2.zygote 调用fork 孵化一个apk进程
  • 3.apk进程执行ActivityThread的main方法:将自己attach到ActivityManagerService
  • 4.AMS执行attachApplication()方法:
    (1)发送bind命令给apk进程
    (2)检查是否有组件需要启动(android进程是需要的时候才启动的,所以这个时候基本是有组件需要启动的),发送启动组件命令
  • 5.apk进程执行bindApplication()方法
  • 6.启动完成
  • 7.apk进程执行组件命令

apk进程处理系统命令

这里写图片描述
(以下的消息、命令、事件本质上没有区别,为避免引起误解,特此说明)

  • 1.系统通过IApplicationThread 给ApplicationThread发送消息
  • 2.ApplicationThread将消息发送给消息队列
  • 3.系统命令发送完成,系统继续处理其他事情
  • 4.UI线程从消息队列取出消息
  • 5.UI线程执行消息
  • 6执行消息的末尾,UI线程上报结果给ActivityManagerService
  • 7.UI线程继续执行下一条消息

通过以上执行流,可以看出,系统给apk进程的指令都是异步的,即发送消息给apk进程之后,立马返回,然后UI线程循环执行各个消息,没有消息的时候(这个消息不止系统发送过来的),UI线程睡眠,这很像领导分配任务,领导分配完任务就不管啦,等你完成后汇报一下就ok啦,既然是任务,那就一定会有时间,如果你固定的时间内完不成任务,领导就会主动找你谈话啦,所以UI线程执行消息都是有时间限制的,比如发送前台广播这个消息,系统给的时间就是10s,10s内UI线程没有处理完(即上面的第6步没有执行到),系统就要处理这个apk进程啦。

系统给apk进程发送命令为什么是异步的?

其中一个重要的原因就是为了提高系统响应apk进程请求的速度,如果是同步执行的话,系统的执行流就会被apk进程绑架,如果apk进程是恶意的,那就更危险啦,直接导致整个手机卡死。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值