0%

(03)2021.12.14-Android的启动流程---Leo老师

一、粗略过一遍

在内核里面没有进程和线程的区分的。

init 是用户空间鼻祖。zygote 是Java 进程的鼻祖,此前一直在 native 层。

Systemserver 进程里面大约有 90 多个进程。

init 进程中,做挂载、创建文件夹之类的操作

SetupSelinux 里面实现了 linux 这块的安全策略,

native 层这些知识,比如 init.rc 的解析等,看刘望舒的书籍就可以了,所以这里大概略过,总结一下init处理的重要事情:

  1. 挂载文件

  2. 设置 selinux –> 安全策略

  3. 开启属性服务,注册到 epoll 机制

  4. 解析 init.rc 文件

  5. 循环处理脚本,启动zygote 进程

  6. 循环等待,主要就是接受子进程的 SIGCHLD 信号,防止子进程变为僵尸进程

linux 中一切接文件,输入输出也是,在代码中写 system.out.println() 也是将内容写入到某个目录,然后再显示出来。

二、Zygote

Zygote 有一部分运行在 native 层,有一部分运行在 Java 层,从这里开始,我们后续的代码就进入了 java 层运行。

adb shell

kill 9 6158

上述命令就可以将 zygote 进程(其中6158是zygote 进程的进程号,需要确认下是不是这个)杀掉,然后 Android 就崩了。说明 Android 的关键进程是不能被结束的

每一个进程启动的时候,都是执行 main 方法

Android的运行环境(Android RunTime)是 zygote 给启动的,调用的是 runTime.start(xx,xx,xx) 方法,在 runtime.start()方法中,做了几件事情:

  • 通过 startVM 就是启动虚拟机

  • 之后通过 startReg 来注册 JNI ,

从上面看出来,我们在 zygote 进程中startVM 启动了虚拟机,虚拟机的作用: 进程管理。所以,直播课学员问的,先有进程还是先有VM 虚拟机,答案就明确了:先有进程,VM 只是进程里面的一段功能代码而已;如果从内核空间和用户空间来讲,VM 显然是在用户空间

每个 App 的内核空间,在物理上都对应同一块地方。

2.1 注册 JNI?

Java 与 Native 代码互相调用, 注册 JNI 就是将 Java 的本地方法和 native 方法关联起来。

进程是没有 Java进程 和 Native进程 的区分的,所以,zygote 执行的时候,不论是在 Native 层还是 Java 层,我们都说是在 Zygote 进程。看源码的时候,如何判断是否进入到了其他进程,就看是否 fork 了。

2.2 zygoteServer

它是一个 Socket ,为什么要用 Socket 而不用 Binder 进程间通信?

因为 Binder 是多线程的,fork 可能会导致死锁,所以这里用 Socket

Zygote 的 preload 加载资源啊(Android内部的资源com.android.internal.R.xx 之类的)、类啊(有个文件配置了需要预加载哪些类),这样在fork出来的 App 进程中不用去加载了,加快速度

关于预加载,老师的一张图很有说明性,复制下:

Android应用进程共享内存图

2.3 总结:zygote 进程启动干了什么事情?

native 层面:

  • 初始化运行环境,创建vm

  • 注册jni

  • 调用 zygoteinit.main 进入 Java 环境

Java 层面

  • 预加载–加快APp进程启动

  • 创建 server 类型的 socket 接收fork 新进程的信息

  • 通过 fork 创建 SystemServer 进程

  • 循环等待,等待 SystemServer 进程的 fork 其你去

三、fork (老师发的资料里面的补充内容)

3.1 fork 如何导致死锁

在 POSIX 标准中,fork 行为是这样的:复制整个用户空间的数据(通常使用 copy-on-write 策略)以及所有系统对象,然后仅仅复制当前线程到子进程,这就意味着,所有父进程中其他线程,到了子进程中都是突然政法掉了。

所以,如果非当前线程获取到了锁,fork完成后,当前线程去获取锁,会发现一直占用,无法获取到

3.2 fork 返回值

  • 返回 0 表示成功创建子进程,并且接下来进入子进程

  • 返回 pid > 0 ,表示成功创建子进程,并且继续执行父进程流程

  • 返回 pid < 0 创建子进程失败(可能内存不足等原因)

3.3 孤儿进程、僵尸进程

fork 调用后,父子进程交替执行,执行顺序不定:

  • 如果父进程先退出,子进程还没退出,则子进程的父进程会变为init进程(托孤,因为任何一个进程都必须有父进程)

  • 如果子进程先退出,父进程还没退出,那么子进程必须等到父进程捕捉到了子进程的退出状态才真正结束,否则这个子进程会变为僵尸进程(只保留一些退出信息供父进程查询)

谢谢你的鼓励