最近看了一下Andriod系统架构方面的内容,本文参考了大量博客,同时也包含了自己对代码的阅读,以及在sbd shell中的测试。尊重前人的成果,本文标记为转载~
主要参考与转载博客如下:
《Android系统架构与系统源码目录》
https://blog.csdn.net/itachi85/article/details/54695046/
《理解Android进程创建流程》
https://www.cnblogs.com/zl1991/p/6867985.html
《Android系统开篇》
Android启动流程 、app安装和启动原理
https://www.jianshu.com/p/12de32b31836
………………………………………………
一、Android系统架构
图1.1 Android系统三大部分
图1.1来自百度百科词条《Android系统构架》,在该词条中,将Android系统分为了三大部分:应用部分、核心部分、底层部分。
图1.2来自CSDN牛人博客,该博客名为《Android系统架构与系统源码目录》,博客地址为:https://blog.csdn.net/itachi85/article/details/54695046/ 在该博客中给出了图2所示的Android五层架构,从上到下依次是应用层、应用框架层、系统运行库层、硬件抽象层和Linux内核层。
(一)应用层
系统内置的应用程序以及非系统级的应用程序都是属于应用层。负责与用户进行直接交互,通常都是用Java进行开发的。
图1.2 Android五层架构
(二)应用框架层(Java Framework)
应用框架层为开发人员提供了可以开发应用程序所需要的API,我们平常开发应用程序都是调用的这一层所提供的API,当然也包括系统的应用。这一层的是由Java代码编写的,可以称为Java Framework,这一层所提供的主要的组件如下表1.1所示。
表1.1 应用框架层组件与功能
(三)系统运行库层(Native)
系统运行库层分为两部分,分别是C/C++程序库和Android运行时库。下面分别来介绍它们。
1.C/C++程序库
C/C++程序库能被Android系统中的不同组件所使用,并通过应用程序框架为开发者提供服务,主要的C/C++程序库如下表1.2所示。
表1.2 C/C++程序库
2.Android运行时库
运行时库又分为核心库和ART(5.0系统之后,Dalvik虚拟机被ART取代)。核心库提供了Java语言核心库的大多数功能,这样开发者可以使用Java语言来编写Android应用。相较于JVM,Dalvik虚拟机是专门为移动设备定制的,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik 应用作为一个独立的Linux 进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。而替代Dalvik虚拟机的ART 的机制与Dalvik 不同。在Dalvik下,应用每次运行的时候,字节码都需要通过即时编译器转换为机器码,这会拖慢应用的运行效率,而在ART 环境中,应用在第一次安装的时候,字节码就会预先编译成机器码,使其成为真正的本地应用。
(四)硬件抽象层(HAL)
硬件抽象层是位于操作系统内核与硬件电路之间的接口层,其目的在于将硬件抽象化,为了保护硬件厂商的知识产权,它隐藏了特定平台的硬件接口细节,为操作系统提供虚拟硬件平台,使其具有硬件无关性,可在多种平台上进行移植。 从软硬件测试的角度来看,软硬件的测试工作都可分别基于硬件抽象层来完成,使得软硬件测试工作的并行进行成为可能。通俗来讲,就是将控制硬件的动作放在硬件抽象层中。
(五)Linux内核层
Android 的核心系统服务基于Linux 内核,在此基础上添加了部分Android专用的驱动。系统的安全性、内存管理、进程管理、网络协议栈和驱动模型等都依赖于该内核。
二、Android进程整理
(一)Android进程网络资料
Android进程理论知识可参考:
1、Android系统启动流程(一)解析init进程启动过程
https://blog.csdn.net/itachi85/article/details/54783506
2、Android系统启动流程(二)解析Zygote进程启动过程
https://blog.csdn.net/itachi85/article/details/55047104
3、Android系统启动流程(三)解析SyetemServer进程启动过程
https://blog.csdn.net/itachi85/article/details/55053356
4、Android系统启动流程(四)Launcher启动过程与系统启动流程
https://blog.csdn.net/itachi85/article/details/56669808
5、Android应用程序进程启动过程(前篇)
https://blog.csdn.net/itachi85/article/details/64123035
6、Android进程整理
https://www.cnblogs.com/android-blogs/p/5632549.html
Android多进程测试代码资料:
1、Android应用内多进程分析和研究
https://blog.csdn.net/goodlixueyong/article/details/49853079
2、Android多进程app中Application回调onCreate()方法被执行多次分析及解决
https://www.jianshu.com/p/539717f2f519
3、Android多进程总结一:生成多进程(android:process属性)
https://blog.csdn.net/fanleiym/article/details/83894399
(二)Android进程知识
进程:每个App在启动前必须先创建一个进程,该进程是由Zygote fork出来的,进程具有独立的资源空间,用于承载App上运行的各种Activity/Service等组件。进程对于上层应用来说是完全透明的,这也是google有意为之,让App程序都是运行在Android Runtime。大多数情况一个App就运行在一个进程中,除非在AndroidManifest.xml中配置Android:process属性,或通过native代码fork进程。
线程:线程对应用开发者来说非常熟悉,比如每次new Thread().start()都会创建一个新的线程,该线程并没有自己独立的地址空间,而是与其所在进程之间资源共享。从Linux角度来说进程与线程都是一个task_struct结构体,除了是否共享资源外,并没有其他本质的区别。
在所有进程中,以父进程的姿态存在的进程,如下:
- kthreadd进程: 是所有内核进程的父进程
- init进程 : 是所有用户进程的父进程(或者父父进程)
- zygote进程 : 是所有上层Java进程的父进程,另外zygote的父进程是init进程。
在Android进程中,有3个非常重要的进程,如下:
system_server
:是由zygote孵化而来的,是zygote的首席大弟子,托起整个Java framework的所有service,比如ActivityManagerService, PowerManagerService等等。mediaserver
:是由init孵化而来的,托起整个C++ framework的所有service,比如AudioFlinger, MediaPlayerService等等。servicemanager
:是由init孵化而来的,是整个Binder架构(IPC)的大管家,所有大大小小的service都需要先请示servicemanager。
其中最重要的两个进程是:
- system_server进程:是用于管理整个Java framework层,包含ActivityManager,PowerManager等各种系统服务;
- Zygote进程:是Android系统的首个Java进程,Zygote是所有Java进程的父进程,包括 system_server进程以及所有的App进程都是Zygote的子进程,注意这里说的是子进程,而非子线程。
(三)测试
使用数据线连接手机,通过adb操作手机,然后使用adb shell进入到手机终端。首先使用ps命令查看zygote进程的pid,如我这里是368,然后使用命令ps -t | grep -E “368”,查看手机中的zygote进程,即其子进程,结果如下图2.1所示。
从类似于图2.1的更多的打印信息中,发现了一个规律,即如图2.2与图2.3所比较可以看出有些子线程在其他进程中也被创建了出来。
其中比较典型的有如下表2.1所示的线程。
表2.1 典型子线程
线程名 | 解释 |
ReferenceQueueD | 引用队列的守护线程 |
FinalizerDaemon | 析构的守护线程 |
FinalizerWatchd | 析构监控的守护线程 |
HeapTrimmerDaem | 堆整理的守护线程 |
GCDaemon | 执行GC的守护线程 |
通过阅读上述理论资料6了解到,这5个线程都是与虚拟机息息相关的线程,之后所有由Zygote直接或间接孵化的子进程,都会包含这5个线程,那么就在其线程说明中,不再重复,而是以“用于GC”的字样来表示。
图2.1 打印出zygote进程及相关子进程
图2.2 com.android.phone进程及其子线程
图2.3 com.netease.cloudmusic进程及其子线程
博客:理解Android进程创建流程
https://www.cnblogs.com/zl1991/p/6867985.html
Android系统开篇
(四)进程创建流程
无论是Android系统,还是各种Linux衍生系统,各个组件、模块往往运行在各种不同的进程和线程内,这里就必然涉及进程/线程之间的通信。对于IPC(Inter-Process Communication, 进程间通信),Linux现有管道、消息队列、共享内存、套接字、信号量、信号这些IPC机制,Android额外还有Binder IPC机制,Android OS中的Zygote进程的IPC采用的是Socket机制,在上层system server、media server以及上层App之间更多的是采用Binder IPC方式来完成跨进程间的通信。对于Android上层架构中,很多时候是在同一个进程的线程之间需要相互通信,例如同一个进程的主线程与工作线程之间的通信,往往采用的Handler消息机制。
想深入理解Android内核层架构,必须先深入理解Linux现有的IPC机制;对于Android上层架构,则最常用的通信方式是Binder、Socket、Handler,当然也有少量其他的IPC方式,比如杀进程Process.killProcess()采用的是signal方式。下面所述的Blinder方式和Socket方式就是进程间通信的两种方式。他们的具体资料可以查看博客:
进程的启动流程如下图2.4所示。
图2.4 进程启动流程
1、App发起进程:当从桌面启动应用,则发起进程便是Launcher所在进程;当从某App内启动远程进程,则发送进程便是该App所在进程。发起进程先通过binder发送消息给system_server进程;
2、system_server进程:调用Process.start()方法,通过socket向zygote进程发送创建新进程的请求;
3、zygote进程:在执行ZygoteInit.main()后便进入runSelectLoop()循环体内,当有客户端连接时便会执行ZygoteConnection.runOnce()方法,再经过层层调用后fork出新的应用进程;
4、新进程:执行handleChildProc方法,最后调用ActivityThread.main()方法。
具体流程可参阅博客:https://www.cnblogs.com/zl1991/p/6867985.html
下面以Android源码8.1.0_r33为例。
1、system_server发起请求
1.1 Process.start
目录:/frameworks/base/core/java/android/os/Process.java,其函数实现原型如下图2.5所示,在Process.start中返回了zygoteProcess.start方法。
图2.5 Process.start方法
1.2 zygoteProcess.start
目录:/frameworks/base/core/java/android/os/ZygoteProcess.java,其函数实现原型如下图2.6所示。
图2.6 zygoteProcess.start方法
在zygoteProcess.start方法中,返回了startViaZygote方法。
1.3 startViaZygote
目录:/frameworks/base/core/java/android/os/ZygoteProcess.java
startViaZygote方法部分内容如下图2.7以及2.8所示,该方法主要工作是生成argsForZygote数组,该数组保存了进程的uid、gid、groups、target-sdk、nice-name等一系列的参数,最后通过zygoteSendArgsAndGetResult方法传递该数组。
图2.7 startViaZygote方法部分代码
图2.8 startViaZygote方法结尾返回
1.4 zygoteSendArgsAndGetResult
目录:/frameworks/base/core/java/android/os/ZygoteProcess.java
zygoteSendArgsAndGetResult方法及其内容如下图2.9所示,这个方法的主要功能是通过socket通道向Zygote进程发送一个参数列表,然后进入阻塞等待状态,直到远端的socket服务端发送回来新创建的进程pid才返回。
图2.9 zygoteSendArgsAndGetResult方法
1.5 openZygoteSocketIfNeeded
目录:/frameworks/base/core/java/android/os/ZygoteProcess.java
在图2.8中可以看到在startViaZygote方法中返回的zygoteSendArgsAndGetResult方法其中一个参数为openZygoteSocketIfNeeded(abi),进入到该方法中查看其内容,其内容如下图2.10所示。
openZygoteSocketIfNeeded(abi)方法是根据当前的abi来选择与zygote还是zygote64来进行通信。
既然system_server进程的zygoteSendArgsAndGetResult()方法通过socket向Zygote进程发送消息,这时便会唤醒Zygote进程,来响应socket客户端的请求(即system_server端),接下来的操作便是在Zygote来创建进程。
图2.10 openZygoteSocketIfNeeded方法
2、Zygote创建进程
Zygote进程是由由init进程而创建的,进程启动之后调用ZygoteInit.main()方法,经过创建socket管道,预加载资源后,便进程runSelectLoop()方法。
2.1 ZygoteInit.main
目录:/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
图2.11 ZygoteInit.main前部分代码
图2.12 ZygoteInit.main后部分代码
在ZygoteInit.main方法中,使用runSelectLoop(abiList)……
之后的发展查看前面给出的参考博客,下面直接对创建进程的方法,即forkAndSpecialize。
2.2 forkAndSpecialize
目录:/frameworks/base/core/java/com/android/internal/os/Zygote.java
forkAndSpecialize方法内容如下图2.13所示,VM_HOOKS是Zygote对象的静态成员变量:VM_HOOKS = new ZygoteHooks()。
图2.13 forkAndSpecialize方法内容
在forkAndSpecialize这个方法中返回了进程的pid,从2.13中可以看出是通过nativeForkAndSpecialize方法返回了这个进程的pid。
2.3 nativeForkAndSpecialize
通过查阅资料发现nativeForkAndSpecialize通过JNI最终调用调用了C++编写的com_android_internal_os_Zygote_nativeForkAndSpecialize方法,其内容如下图2.14所示。
目录:/frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
该方法最终返回ForkAndSpecializeCommon方法,再进入到该方法中,查看其中的内容。
图2.14 com_android_internal_os_Zygote_nativeForkAndSpecialize方法
2.4 ForkAndSpecializeCommon
目录:/frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
该函数内容比较长,其中比较重要的是里面使用了fork()
pid_t pid = fork();
三、Android系统启动流程
(一)init进程启动流程
init进程是Android系统中用户空间的第一个进程,作为第一个进程,它被赋予了很多极其重要的工作职责,比如创建zygote(孵化器)和属性服务等。init进程是由多个源文件共同组成的,这些文件位于源码目录system/core/init。
这一部分可以看前面提到的理论资料1/2/3/4等。
图3.1 系统启动
四、重要的类
(一)Process类
1、Process.java文件中的Process类
目录:/frameworks/base/core/java/android/os/Process.java
Process类部分内容如下图4.1所示,在Process类中包含了大量关于UID以及GID的相关知识。
图4.1 Process类部分内容截图
(二)ProcessRecord类
目录:
/frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java
ProcessRecord类包含了一个正在运行的进程的所有信息,其部分内容如下图4.2所示
图4.2 ProcessRecord类部分内容
(三)ApplicationInfo类
目录:/frameworks/base/core/java/android/content/pm/ApplicationInfo.java
五、APP启动运行
图5.1 Application Launch
图5.1中描述了一个APP的启动流程。
可参考博客:Android启动流程 、app安装和启动原理
https://www.jianshu.com/p/12de32b31836
(一)ActivityManager
在Android编程过程中,涉及到许多 Activity 跳转的事情,在 Launcher 中点击 Icon 进行跳转也是同样的道理,调用 context.startActivity(intent)方法。Launcher处于一个线程,而启动的 App 则运行在另一个进程中,在这其中势必牵涉到跨进程 (IPC) 调用,这样复杂的过程显然需要一种中介者,或者一个系统来进行中转和管理,而这个服务就是 ActivityManagerService。
ActivityManagerService 作为一个守护进程运行在 Android Framework中,如果让开发者直接接触这个类的话,就需要开发者自行处理 IPC 调用的问题,且这有不利于 Android 系统进行安全校验等工作。因而Android系统实现了ActivityManager,通过这个 ActivityManager作为一个入口,变相地和ActivityManagerService打交道。
(二)启动流程简述
要想启动一个应用程序,首先要保证这个应用程序所需要的应用程序进程已经被启动。ActivityManagerService在启动应用程序时会检查这个应用程序需要的应用程序进程是否存在,不存在就会请求Zygote进程将需要的应用程序进程启动。
Zygote的Java框架层中,会创建一个Server端的Socket,这个Socket用来等待ActivityManagerService来请求Zygote来创建新的应用程序进程的。我们知道Zygote进程通过fock自身创建的应用程序进程,这样应用程序程序进程就会获得Zygote进程在启动时创建的虚拟机实例。当然,在应用程序创建过程中除了获取虚拟机实例,还可以获得Binder线程池和消息循环,这样运行在应用进程中应用程序就可以方便的使用Binder进行进程间通信以及消息处理机制了。关于Binder线程池和消息循环是如何启动或者创建的会在下一篇文章给出答案。先给出应用程序进程启动过程的时序图,然后对每一个步骤进行详细分析,如下图5.2所示。
图5.2应用程序启动创建进程流程
ActivityManagerService会通过调用startProcessLocked方法来向Zygote进程发送请求,startProcessLocked目录为:
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
图5.3 startProcessLocked部分代码
private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
...
try {
try {
final int userId = UserHandle.getUserId(app.uid);
AppGlobals.getPackageManager().checkPackageStartable(app.info.packageName, userId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
int uid = app.uid; //1
int[] gids = null;
int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
if (!app.isolated) {
...
/**
* 2 对gids进行创建和赋值
*/
if (ArrayUtils.isEmpty(permGids)) {
gids = new int[2];
} else {
gids = new int[permGids.length + 2];
System.arraycopy(permGids, 0, gids, 2, permGids.length);
}
gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
gids[1] = UserHandle.getUserGid(UserHandle.getUserId(uid));
}
...
if (entryPoint == null) entryPoint = "android.app.ActivityThread"; //3
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
app.processName);
checkTime(startTime, "startProcess: asking zygote to start proc");
/**
* 4
*/
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);
...
} catch (RuntimeException e) {
...
}
}
...
}
在注释1处的达到创建应用程序进程的用户ID,在注释2处对用户组ID:gids进行创建和赋值。注释3处如果entryPoint 为null则赋值为”android.app.ActivityThread”。在注释4处调用Process的start函数,将此前得到的应用程序进程用户ID和用户组ID传进去,第一个参数entryPoint我们得知是”android.app.ActivityThread”
(三)应用进程测试
在应用启动测试方面,先写了一个含有两个进程的APP进行测试,该APP代码如下。
3.1 AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.test.testprocess">
<application
android:name="com.example.test.testprocess.MyApplication"
android:label="@string/app_name">
<activity
android:name=".ProcessTestActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".ProcessTestService"
android:process=":remote">
</service>
</application>
</manifest>
3.2 MyApplication.java
package com.example.test.testprocess;
import android.app.Application;
import android.util.Log;
public class MyApplication extends Application {
public static final String TAG = "viclee";
@Override
public void onCreate() {
super.onCreate();
int pid = android.os.Process.myPid();
Log.d(TAG, "MyApplication onCreate");
Log.d(TAG, "MyApplication pid is " + pid);
}
}
3.3 ProcessTestActivity.java
package com.example.test.testprocess;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
public class ProcessTestActivity extends Activity {
public final static String TAG = "viclee";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(TAG, "ProcessTestActivity onCreate");
this.startService(new Intent(this, ProcessTestService.class));
}
}
3.4 ProcessTestService.java
package com.example.test.testprocess;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class ProcessTestService extends Service {
public static final String TAG = "viclee";
@Override
public void onCreate() {
Log.i(TAG, "ProcessTestService onCreate");
}
@Override
public IBinder onBind(Intent arg0) {
return null;
}
}
3.5 layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="layout"/>
</LinearLayout>
将APP下载进手机中,然后使用adb shell进入到手机中,通过ps命令查看当前系统中进程的情况,结果如下图5.4所示。
图5.4 测试APP的进程情况