Android系统启动流程?
- 当按电源键触发开机,首先会从 ROM中预定义的地方加载引导程序BootLoader 到 RAM 中,并执行BootLoader程序启动Linux Kernel;
- 然后启动用户级别的第一个进程:init进程。init进程会解析init.rc脚本做一些初始化工作,包括挂载文件系统、创建工作目录以及启动系统服务进程等,其中系统服务进程包括 Zygote、service manager、media 等。
- 在Zygote 中会进一步去启动system_server进程,然后在system_server 进程中会启动 AMS、WMS、PMS等服务,等这些服务启动之后,AMS中就会打开Launcher应用的home Activity,最终就看到了手机的 "桌面"。
system_seryer 为什么要在Zygote 中启动,而不是由init直接启动呢?
Zygote作为一个孵化器,可以提前加载一些资源,这样 fork()时基于Copy-On-Write 机制创建的其他进程就能直接使用这些资源,而不用重新加载。比如 system_server就可以直接使用Zygote 中的JNI函数、共享库、常用的类、以及主题资源。
为什么要专门使用Zygote进程去孵化应用进程,而不是让system_server 去孵化呢?
- 首先 system_server相比 Zygote 多运行了AMS、WMS等服务,这些对一个应用程序来说是不需要的。
- 另外进程的 fork()对多线程不友好,仅会将发起调用的线程拷贝到子进程,这可能会导致死锁,而system_server中肯定是有很多线程的。
能说说具体是怎么导致死锁的吗?
在POSIN标准中,fork 的行为是这样的︰复制整个用户空间的数据(通常使用copy-on-write 的策略,所以可以实现的速度很快)以及所有系统对象,然后仅复制当前线程到子进程。这里所有父进程中别的线程,到了子进程中都是突然蒸发掉的。
对于锁来说,从OS 看,每个锁有一个所有者,即最后一次 lock它的线程。假设这么一个环境,在 fork 之前,有一个子线程lock了某个锁,获得了对锁的所有权。fork以后/在子进程中,所有的额外线程都人间蒸发了。而锁却被正常复制了,在子进程看来,这个锁没有主人,所以没有任何人可以对它解锁。当子进程想lock这个锁时,不再有任何手段可以解开了。程序发生死锁。
Zygote为什么不采用Binder机制进行IPC通信?
A: Binder 机制中存在 Binder线程池,是多线程的,如果Zygote 采用Binder 的话就存在上面说的fork()与多线程的问题了。其实严格来说,Binder 机制不一定要多线程,所谓的 Binder线程只不过是在循环读取Binder 驱动的消息而已,只注册一个Binder线程也是可以工作的,比如service manager就是这样的。实际上Zygote 尽管没有采取 Binder 机制,它也不是单线程的,但它在fork()前主动停止了其他线程,fork()后重新启动了。
Binder
Binder有什么优点?
性能方面
- 共享内存:0次数据拷贝
- Binder:1次数据拷贝
- Socket/管道/消息队列:2次数据拷贝
稳定性方面
- Binder:基于C/S架构,客户端_(Client)有什么需求就丢给服务端(Server)去完成,架构
清晰、职责明确又相互独立,自然稳定性更好。 - 共享内存:虽然无需拷贝,但是控制复杂,难以使用。·
- 从稳定性的角度讲,Binder机制是优于内存共享的。
安全性方面
- ·传统的IPC没有任何安全措施,安全依赖上层协议来确保。
- 传统的IPC方法无法获得对方可靠的进程用户ID/进程UI (UID/PID),从而无法鉴别对方身份
- 传统的IPC只能由用户在数据包中填入UID/PID,容易被恶意程序利用。
- 传统的IPC访问接入点是开放的,无法阻止恶意程序通过猜测接收方地址获得连接。Binder既支持实名Binder,又支持匿名Binder,安全性高。
Binder是如何做到一次拷贝的
主要是因为Linux是使用的虚拟内存寻址方式,它有如下特征:
- 用户空间的虚拟内存地址是映射到物理内存中的。
- 对虚拟内存的读写实际上是对物理内存的读写,这个过程就是内存映射这个内存映射过程是通过系统调用mmap()来实现的。
- Binder借助了内存映射的方法,在内核空间和接收方用户空间的数据缓存区之间做了一层内存映射,就相当于直接拷贝到了接收方用户空间的数据缓存区,从而减少了一次数据拷贝。
Binder机制是如何跨进程的
- Binder驱动
- 在内核空间创建一块接收缓存区。
- 实现地址映射:将内核缓存区,接收进程用户空间映射到同一接收缓存区。
- 2.发送进程通过系统调用(copy_from_user)将数据发送到内核缓存区。由于内核缓存区和接收进程用户空间存在映射关系,故相当于也发送了接收进程的用户空间,实现了跨进程通信。