阅读 494

带你读源码!Android研习社:技术分享第二期

前言

Android研习社第二期技术分享的录播视频,已经放到B站上了,大家可以戳链接www.bilibili.com/video/av712…食用

这篇文章,一来是Android研习社第二期技术分享的视频讲义,二来也对上一篇的Zygote源码分析进行了一个补充,水平有限,还请大家多多指教

源码阅读工具

understand 5.1

源码阅读方法

  1. 如何把源码读起来

    阅读相关技术文章,总结重要的知识点,给自己提问题,去源码中找答案,有一个针对性

  2. 如何抓大放小

    源码很庞大,涉及的细节和深度也比较多,要清楚自己当前,想搞懂的是什么问题

  3. 如何把握主要问题

    没有定论,多阅读多体会,阅读相关技术文章和读源码穿插结合

源码领读:Zygote源码分析

主要问题分析:Zygote进程主要流程分析

本来的想法是分析Zygote进程是如何被启动起来的,后来发现没啥东西,所以今天还是分析下Zyogte主要做了些什么事情;目的呢,其实还是让大家知道如何去读源码,正所谓授人以渔,不如授人以渔

请戳链接食用:www.bilibili.com/video/av712…

再谈Zygote,由厚到薄

Zygote进程的启动都经历了哪些流程

我们先来大概梳理下Zygote进程的启动都经历了哪些流程

如上手稿所示,首先呢,init进程需要对相关脚本进行解析,根据设备的不同,可能会解析不同的脚本文件,比如32位设备就是32为的zygote脚本,脚本中又根据设备的不同,可能会有两个zygote来分别对32位应用和64位应用进行fork。

深入分析

为什么要使用脚本

我们来考虑下,这里为什么要使用脚本,同时又要搞一套脚本解析的逻辑,来对zygote进行初始化呢?如果写死到逻辑里,是否可以呢?这里我们先留一个问号,先接着往下分析

通过对脚本的解析,我们执行app_process的二进制文件开启一个进程,并把这个进程名改为zygote,然后对该进程进行初始化操作,这里我们还需要进一步分析,初始化过程中,究竟做了什么事情;

最后,进入一个死循环,用socket通信来接收子进程发送过来的消息,这里的socket是服务端,其他进程相当于客户端,所以代码中命名为ZygoteServer,就是这个意思

在这个死循环中,使用了多路IO复用的机制(select机制),这个也需要去进一步分析

进入死循环后,相当于当前进程一直处于运行状态,那么我们也要接着去执行下面的逻辑呀,不能卡死在这里呀,所以我们又fork出一个重要进程,SystemServer,由这个进程去开启一系列的系统服务,比如AMS,PMS,WMS,这些系统服务都是跑在一个进程下的(这个需要进一步确认),由这些系统服务提供一些公共的东西给其他进程来调度和使用

为什么这么设计

这么设计的原因,我们来简单的猜测下。如果没有这些系统服务,那我们如何去调度系统资源呢?每个进程都需要主动找系统层去要,那么系统层就要把这些逻辑,比如跨进程的消息处理,多线程的安全问题,都要写在复杂的逻辑中去。如果把这些逻辑抽离出来,一来,相当于收拢了相关功能的处理权限;二来,各个服务职责单一,也简化了代码,提升了代码的可阅读性和可维护性

另外,为什么要单开一个进程来维护这些系统服务呢?就算我调用了runSelectLoop()进入了死循环,那其实还可以另开一个线程啊!

我觉得这个就是跟整体的设计相关了,比如说,咱们做一个项目,其中有一个模块是相对独立的,跟其他业务是没有什么关系的,比如音视频播放,或者Webview展示H5页面,类似的功能,那我们其实一般是会单独开启一个子进程的。我觉得系统层面的设计也有类似的考虑,如果zygote进程出了问题,那就重新初始化zygote就行了,如果在init进程出了问题,就针对init做单独的业务处理,保证他们互相的独立性,和业务上的解耦,互相都不会影响;如果是开启一个线程,就可能需要处理各种各样的问题

SystemServer,可以说是Java世界的头号进程--Zygote--诞生一来,fork出来的第一个子进程,这个fork出的进程,内部是如何设计的,如何维护这个系统服务的,这个也是我们接下来的SystemServer源码要分析的部分

我们再来探讨下,Zygote为什么要设计一个跟子进程通信的ZygoteServer,上面我们也提到了,它内部是维护了两个Socket对象,zygoteSocketuaspSocket,那么我们就要了解,Linux中,经典跨进程通信的方案有哪些了。Socket是进程间通信的一种方式,而且,是可以无视Native世界和Java世界的。也可以说,它是一种协定好的通信方案。像我们之前做的游戏辅助应用,我们需要通过adb来开启一个native进程,这个进程又是需要跟App所在的进程进行通信的,那么,在这种场景下,我们就是使用了Socket通信的方式,一个作为server端,一个作为client端,进行数据的传递。

Zygote进程,是由init进程通过执行exec(需要确认)的系统调用开启的,它是沟通Java世界和native世界的桥梁,zygote进程最终是在Java世界中的,这里我们的zygote跟它的子进程通信,也需要一个一对多的数据通信设计(一个Zyogte会fork出多个子进程)。

为什么要有一个母体

我这里还想谈一谈,为什么要设计有一个母体,fork出子进程这种进程的运行方式。有一些资源是可以共享的,比如VM的运行环境,那么我想再开启一个子进程,又想同时对VM的运行环境进行复用,这么来看,fork出一个子进程,是一个比较好的方式;同时,32位的zygote和64位的zygote需要在不同的VM环境下运行,所以才会有两个zygote--primeryZygotesecondZygote

那么什么又是VM呢?简单来讲,就是运行环境,这个运行环境不是硬件层的,而是软件层面的,比如像我们开发App,也需要又SDK,NDK之类的工具包,才能进行开发。VM也是同理,提供一些共有的环境,供进程来使用,比如各自的私有目录,进程及进程组的权限,等等

接下来要做的事

  1. SystemServer源码分析(下周)
  2. 系统启动流程整体分析(下下周?)
  3. 10分钟搞定源码,系列短视频(下下下周?)
  4. 源码动态调试(待定)

写在最后

原创不易,坚持更难。 如果你想继续看到我接下来的分享,请通过点赞的方式告诉我,你的鼓励是我继续创作的最大动力!

郑重声明

本文由Android研习社社群创作,版权©️归Android研习社所有,侵权必究!