Android Framework学习2- 系统启动流程分析/初始化篇

启动流程

image.png

1.BootLoader阶段(加载引导程序BootLoader到RAM中)

Android 设备上电后,首先会从处理器到Rom 的启动引导代码开始执行,Rom 会找 Boot loader 的代码,并加载到内存中。这一步由“新品厂商”负责设计和实现的。

2. kernel 阶段

Linux 内核开始启动,初始化各种软硬件环境、加载驱动程序、挂载根文件系统、并执行 init 程序,由此开启 Android 的世界。
内核启动的执行的第一条的代码在head.S文件中,主要功能是实现压缩内核的解压和跳转到内核vmlinux内核的入口,kernel的C启动阶段可以理解为真正的启动阶段,从head.S看到,最终调用的是kernel/init/main.c的start_kernel()函数
1)start_kernel()函数中执行了大量的初始化操作:
2)setup_arch():主要做一些板级初始化,cpu初始化,tag参数解析,u-boot传递的cmdline解析,建立mmu工作页表,初始化内存布局,调用mmap_io建立GPIO、IRQ、MEMCTRL、UART,及其他外设的静态映射表,对时钟,定时器,uart进行初始化
3)sched_init():初始化每个处理器的可运行队列,设置系统初始化进程即0号进程
4)softirq_init():内核的软中断机制初始化函数
5)console_init():初始化系统的控制台结构
6)rest_init():调用kernel_thread()创建1号内核线程,调用schedule()函数切换当前进程,在调用该函数之前,Linux系统中只有两个进程,即0号进程init_task和1号进程kernel_init,其中kernel_init进程也是刚刚被创建的。调用该函数后,1号进程kernel_init将会运行

3. init 进程启动

iinit进程是Linux系统中用户空间的第一个进程,进程号固定为1。Kernel启动后,在用户空间启动init进程,并调用init中的main()方法执行init进程的职责,init在初始化过程中会启动很多守护进程。对于init进程的功能分为几部分:

  • 挂载目录,比如/sys、/dev、/proc
  • 解析并运行所有的init.rc相关文件(init是进程,init.rc是脚本,init.rc文件可以在/system/core/rootdir/init.rc找到)
  • 根据rc文件,生成相应的设备驱动节点
  • 处理子进程的终止(signal方式)
  • 提供属性服务的功能
image.png

init进程的入口是system\core\init\init.cpp里的main函数:

int main(int argc, char** argv) {
.........
parser.ParseConfig("/init.rc");
parser.set_is_system_etc_init_loaded(
parser.ParseConfig("/system/etc/init"));
parser.set_is_vendor_etc_init_loaded(
parser.ParseConfig("/vendor/etc/init"));
parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
.........
}

在init.cpp中,加载system\core\rootdir\init.rc 配置文件,init.rc配置文件会进行加载很多配置,创建文件夹及文件,然后初始化一些Android 驱动器。各个极端的顺序是:
on early-init --> on init --> on late-init --> on early-fs --> on post-fs --> on late-fs --> on post-fs-data(在这个时候才开始 mkdir data) --> on zygote-start --> on boot
在boot阶段会启动下面两个模块的服务

on boot
........
class_start core
class_start hal
.......

总结一下就是:init进程(pid=1)是Linux系统中用户空间的第一个进程,主要工作如下:

  • 创建一块共享的内存空间,用于属性服务器;
  • 解析各个rc文件,并启动相应属性服务进程;
  • 初始化epoll,依次设置signal、property、keychord这3个fd可读时相对应的回调函数;
  • 进入无限循环状态,执行如下流程:
    --检查action_queue列表是否为空,若不为空则执行相应的action;
    --检查是否需要重启的进程,若有则将其重新启动;
    --进入epoll_wait等待状态,直到系统属性变化事件(property_set改变属性值),或者收到子进程的信号SIGCHLD,再或者keychord 键盘输入事件,则会退出等待状态,执行相应的回调函数。

可见init进程在开机之后的核心工作就是响应property变化事件和回收僵尸进程。当某个进程调用property_set来改变一个系统属性值时,系统会通过socket向init进程发送一个property变化的事件通知,那么property fd会变成可读,init进程采用epoll机制监听该fd则会 触发回调handle_property_set_fd()方法。
回收僵尸进程,在Linux内核中,如父进程不等待子进程的结束直接退出,会导致子进程在结束后变成僵尸进程,占用系统资源。为此,init进程专门安装了SIGCHLD信号接收器,当某些子进程退出时发现其父进程已经退出,则会向init进程发送SIGCHLD信号,init进程调用回调方法handle_signal()来回收僵尸子进程。

4. Zygote启动

image.png

Zygote是由init进程通过解析init.zygote.rc文件而创建的,zygote所对应的可执行程序app_process,所对应的源文件是App_main.cpp,进程名为zygote。

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd

从App_main()开始,Zygote启动过程的函数调用类大致流程如下:


image.png

AndroidRuntime.start()执行到最后通过反射调用到ZygoteInit.java的main() (进入java层面), 重点代码:

        registerZygoteSocket(socketName); //为Zygote注册socket
        preload(); // 预加载类和资源
        SamplingProfilerIntegration.writeZygoteSnapshot();
        gcAndFinalize(); //GC操作
        if (startSystemServer) {
            startSystemServer(abiList, socketName);//启动system_server
        }
        runSelectLoop(abiList); //进入循环模式,Zygote采用高效的I/O多路复用机制,保证在没有客户端连接请求或数据处理时休眠,否则响应客户端的请求。


Zygote进程能够重启的地方:

  • servicemanager进程被杀; (onresart)
  • surfaceflinger进程被杀; (onresart)
  • Zygote进程自己被杀; (oneshot=false)
  • system_server进程被杀; (waitpid)

在 Zygote 进程启动完成之后,Init 进程会启动 Runtime 进程。Runtime 进程首先初始化服务管理器(Service Manager),并把它注册为绑定服务(Binder services)的默认上下文管理器,负责绑定服务的注册与查找。然后 Runtime 进程会向 Zygote进程发送启动系统服务(System Service)的请求,Zygote 进程收到请求后,会“孵化”出一个新的 Dalvik VM 实例并启动系统服务进程。Runtime 进程的启动流程如下图所示:


image.png

5. zygote启动SystemServer

通过代码我们看到ZygoteInit.java里面通过startSystemServer() fork出了SystemServer进程,SystemServer和Zygote进程是Android框架中两个重要的进程,系统里重要的进程都在SystemServer里开启,如AMS、WMS、PMS等。

startSystemServer()最终经过一系列调用,会调用到SystemServer.java的main函数中。SystemServer主要的功能,个人总结为:

  1. 创建系统服务管理:

//创建系统服务管理
mSystemServiceManager = new SystemServiceManager(mSystemContext);
//将mSystemServiceManager添加到本地服务的成员sLocalServiceObjects
LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);

  1. 启动各种系统服务

    try {
    startBootstrapServices(); // 启动引导服务
    startCoreServices(); // 启动核心服务
    startOtherServices(); // 启动其他服务
    } catch (Throwable ex) {
    Slog.e("System", "************ Failure starting system services", ex);
    throw ex;
    }

startBootstrapServices()该方法所创建的服务:ActivityManagerService, PowerManagerService, LightsService, DisplayManagerService, PackageManagerService, UserManagerService, sensor服务.

system_server进程,从源码角度划分为引导服务、核心服务、其他服务3类。

  • 引导服务(7个):ActivityManagerService、PowerManagerService、LightsService、DisplayManagerService、PackageManagerService、UserManagerService、SensorService;
  • 核心服务(3个):BatteryService、UsageStatsService、WebViewUpdateService;
  • 其他服务(70个+):AlarmManagerService、VibratorService等。
  1. 进入死循环接收AMS传过来的消息

6. 启动桌面

SystemServer启动后会初始化ActivityManagerService,同时加载本地系统服务库,调用createSystemContext()创建系统上下文,创建ActivityThread及各种服务。

SystemServer 中创建了一个 Socket 客户端,并有AMS负责管理该客户端,之后所有的 Dalvik 进程都将通过该 Socket 客户端间接被启动。当要启动新的 APK 进程时 ,AMS中会通过该 Socket 客户端向 zygote 进程的 Socket服务端发送一个启动命令,然后zygote会孵化出新的进程。

PS:此处涉及Android进程中通信的一种方法Socket,学过计算机网络的读者应该对此有一定的概念。以后还会提及pipe、binder两种进程通信方法,无论如何,它们最终的目的都是为了让开发者跨进程调用时都像是在进行本地调用。

AMS开启后会调用finishRooting()完成系统引导过程,同时发送开机广播,ActivityManagerService会与zygote的Socket通信,请求启动Home。zygote收到AMS的连接请求后,zygote处理请求会通过fork启动新的应用进程,并最终启动Home。完成系统界面的加载与显示。
具体:
当 SystemService 加载了所有的系统服务后就意味着系统就准备好了,它会向所有服务发送一个系统准备完毕(systemready) 广播。当ActivityManagerService 接收到 systemready 广播后,会向Zygoute进程发送创建Dalvik 虚拟机实例的请求,Zygoute 进程会负责生成一个新的 Dalvik 虚拟机实例,然后 ActivityManagerService 在系统中查找具有属性的Activity,并启动它。ActivityManagerService 同时也会使用同样的方法启动 Contact 应用程序。

你可能感兴趣的:(Android Framework学习2- 系统启动流程分析/初始化篇)