Android 进程框架:进程的创建、启动与调度流程
Arrays.asList(abiListString.split(",")));}}
建立 Socket 连接的流程很明朗了,如下所示:
创建 LocalSocket 对象。
将 LocalSocket 与 LocalServerSocket 建立连接,建立连接的过程就是 LocalSocket 对象在/dev/socket 目录下查找一个名称为"zygote"的文件,然后将自己与其绑定起来,这样就建立了连接。
创建 LocalSocket 的输入流,以便可以接收 Zygote 进程发送过来的数据。
创建 LocalSocket 的输出流,以便可以向 Zygote 进程发送数据。
1.2 ZygoteInit.main(String argv[])
ZygoteInit 是 Zygote 进程的启动类,该类会预加载一些类,然后便开启一个循环,等待通过 Socket 发过来的创建新进程的命令,fork 出新的 子进程。
ZygoteInit 的入口函数就是 main()方法,如下所示:
public class ZygoteInit {
public static void main(String argv[]) {// Mark zygote start. This ensures that thread creation will throw// an error.ZygoteHooks.startZygoteNoThreadCreation();
try {//...registerZygoteSocket(socketName);//...//开启循环 runSelectLoop(abiList);
closeServerSocket();} catch (MethodAndArgsCaller caller) {caller.run();} catch (Throwable ex) {Log.e(TAG, "Zygote died with exception", ex);closeServerSocket();throw ex;}}
// 开启一个选择循环,接收通过 Socket 发过来的命令,创建新线程 private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
//sServerSocket 指的是 Socket 通信的服务端,在 fds 中的索引为 0fds.add(sServerSocket.getFileDescriptor());peers.add(null);
//开启循环 while (true) {StructPollfd[] pollFds = new StructPollfd[fds.size()];for (int i = 0; i < pollFds.length; ++i) {pollFds[i] = new StructPollfd();pollFds[i].fd = fds.get(i);pollFds[i].events = (short) POLLIN;}try {//处理轮询状态,当 pollFds 有时间到来时则往下执行,否则阻塞在这里。Os.poll(pollFds, -1);} catch (ErrnoException ex) {throw new RuntimeException("poll failed", ex);}for (int i = pollFds.length - 1; i >= 0; --i) {
//采用 IO 多路复用机制,当接收到客户端发出的连接请求时或者数据处理请求到来时则//往下执行,否则进入 continue 跳出本次循环。if ((pollFds[i].revents & POLLIN) == 0) {continue;}//索引为 0,即为 sServerSocket,表示接收到客户端发来的连接请求。if (i == 0) {ZygoteConnection newPeer = acceptCommandPeer(abiList);peers.add(newPeer);fds.add(newPeer.getFileDesciptor());}//索引不为 0,表示通过 Socket 接收来自对端的数据,并执行相应的操作。else {boolean done = peers.get(i).runOnce();//处理完成后移除相应的文件描述符。if (done) {peers.remove(i);fds.remove(i);}}}}}}
可以发现 ZygoteInit 在其入口函数 main()方法里调用 runSelectLoop()开启了循环,接收 Socket 发来的请求。请求分为两种:
连接请求
数据请求
没有连接请求时 Zygote 进程会进入休眠状态,当有连接请求到来时,Zygote 进程会被唤醒,调用 acceptCommadPeer()方法创建 Socket 通道 ZygoteConnection
private static ZygoteConnection acceptCommandPeer(String abiList) {try {return new ZygoteConnection(sServerSocket.accept(), abiList);} catch (IOException ex) {throw new RuntimeException("IOException during accept()", ex);}}
然后调用 runOnce()方法读取连接请求里的数据,然后创建新进程。
此外,连接的过程中服务端接受的到客户端的 connect()操作会执行 accpet()操作,建立连接手,客户端通过 write()写数据,服务端通过 read()读数据。
1.3 ZygoteConnection.runOnce()
class ZygoteConnection {
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
String args[];Arguments parsedArgs = null;FileDescriptor[] descriptors;
try {//读取客户端发过来的参数列表 args = readArgumentList();descriptors = mSocket.getAncillaryFileDescriptors();} catch (IOException ex) {Log.w(TAG, "IOException on command socket " + ex.getMessage());closeSocket();return true;}
//... 参数处理
try {
//... 参数处理
//调用 Zygote.forkAndSpecialize(来 fork 出新进程 pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,parsedArgs.appDataDir);} catch (ErrnoException ex) {logAndPrintError(newStderr, "Exception creating pipe", ex);} catch (IllegalArgumentException ex) {logAndPrintError(newStderr, "Invalid zygote arguments", ex);} catch (ZygoteSecurityException ex) {logAndPrintError(newStderr,"Zygote security policy prevents request: ", ex);}
try {//pid == 0 时表示当前是在新创建的子进程重磅执行 if (pid == 0) {// in childIoUtils.closeQuietly(serverPipeFd);serverPipeFd = null;handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
// should never get here, the child is expected to either// throw ZygoteInit.MethodAndArgsCaller or exec().return true;}// pid < 0 表示创建新进程失败,pid > 0 表示当前是在父进程中执行 else {// in parent...pid of < 0 means failureIoUtils.closeQuietly(childPipeFd);childPipeFd = null;return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);}} finally {IoUtils.closeQuietly(childPipeFd);IoUtils.closeQuietly(serverPipeFd);}}}
该方法主要用来读取进程启动参数,然后调用 Zygote.forkAndSpecialize()方法 fork 出新进程,该方法是创建新进程的核心方法,它主要会陆续调用三个 方法来完成工作:
preFork():先停止 Zygote 进程的四个 Daemon 子线程的运行以及初始化 GC 堆。这四个 Daemon 子线程分别为:Java 堆内存管理现场、堆线下引用队列线程、析构线程与监控线程。
nativeForkAndSpecialize():调用 Linux 系统函数 fork()创建新进程,创建 Java 堆处理的线程池,重置 GC 性能数据,设置进程的信号处理函数,启动 JDWP 线程。
postForkCommon():启动之前停止的 Zygote 进程的四个 Daemon 子线程。
上面的方法都完成会后,新进程会创建完成,并返回 pid,接着就调用 handleChildProc()来启动新进程。handleChildProc()方法会接着调用 RuntimeInit.zygoteInit()来 完成新进程的启动。
1.4 RuntimeInit.zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
这个就是个关键的方法了,它主要用来创建一些运行时环境,我们来看一看。
public class RuntimeInit {
public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)throws ZygoteInit.MethodAndArgsCaller {if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");redirectLogStreams();//创建应用进程的时区和键盘等通用信息 commonInit();//在应用进程中创建一个 Binder 线程池 nativeZygoteInit();//创建应用信息 applicationInit(targetSdkVersion, argv, classLoader);}}
该方法主要完成三件事:
调用 commonInit()方法创建应用进程的时区和键盘等通用信息。
调用 nativeZygoteInit()方法在应用进程中创建一个 Binder 线程池。
调用 applicationInit(targetSdkVersion, argv, classLoader)方法创建应用信息。
Binder 线程池我们后续的文章会分析,我们重点来看看 applicationInit(targetSdkVersion, argv, classLoader)方法的实现,它主要用来完成应用的创建。
该方法里的 argv 参数指的就是 ActivityThread,该方法会调用 invokeStaticMain()通过反射的方式调用 ActivityThread 类的 main()方法。如下所示:
public class RuntimeInit {
private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)throws ZygoteInit.MethodAndArgsCaller {//...
// Remaining arguments are passed to the start class's static maininvokeStaticMain(args.startClass, args.startArgs, classLoader);}
private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)throws ZygoteInit.MethodAndArgsCaller {Class<?> cl;
//通过反射调用 ActivityThread 类的 main()方法 try {cl = Class.forName(className, true, classLoader);} catch (ClassNotFoundException ex) {throw new RuntimeException("Missing class when invoking static main " + className,ex);}
Method m;try {m = cl.getMethod("main", new Class[] { String[].class });} catch (NoSuchMethodException ex) {throw new RuntimeException("Missing static main on " + className, ex);} catch (SecurityException ex) {throw new RuntimeException("Problem getting static main on " + className, ex);}//...}
}
走到 ActivityThread 类的 main()方法,我们就很熟悉了,我们知道在 main()方法里,会创建主线程 Looper,并开启消息循环,如下所示:
public final class ActivityThread {
public static void main(String[] args) {//...Environment.initForCurrentUser();//...Process.setArgV0("<pre-initialized>");//创建主线程 looperLooper.prepareMainLooper();
ActivityThread thread = new ActivityThread();//attach 到系统进程 thread.attach(false);
if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}
//主线程进入循环状态 Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");}}
前面我们从 Process.start()开始讲起,分析了应用进程的创建及启动流程,既然有启动就会有结束,接下来我们从 Process.killProcess()开始讲起,继续分析进程的结束流程。
二 进程的优先级
进程按照优先级大小不同又可以分为实时进程与普通进程。
prio 值越小表示进程优先级越高,
静态优先级:优先级不会随时间改变,内核也不会修改,只能通过系统调用改变 nice 值,优先级映射公式为:static_prio = MAX_RT_PRIO + nice + 20,其中 MAX_RT_PRIO = 100,那么取值区间为[100, 139];对应普通进程;
实时优先级:取值区间为[0, MAX_RT_PRIO -1],其中 MAX_RT_PRIO = 100,那么取值区间为[0, 99];对应实时进程;
懂爱优先级:调度程序通过增加或者减少进程优先级,来达到奖励 IO 消耗型或按照惩罚 CPU 消耗型的进程的效果。区间范围[0, MX_PRIO-1],其中 MX_PRIO = 140,那么取值区间为[0,139];
三 进程调度流程
进程的调度在 Process 类里完成。
3.1 优先级调度
优先级调度方法
setThreadPriority(int tid, int priority)
进程的优先级以及对应的 nice 值如下所示:
THREAD_PRIORITY_LOWEST 19 最低优先级
THREAD_PRIORITY_BACKGROUND 10 后台
THREAD_PRIORITY_LESS_FAVORABLE 1 比默认略低
THREAD_PRIORITY_DEFAULT 0 默认
THREAD_PRIORITY_MORE_FAVORABLE -1 比默认略高
THREAD_PRIORITY_FOREGROUND -2 前台
THREAD_PRIORITY_DISPLAY -4 显示相关
THREAD_PRIORITY_URGENT_DISPLAY -8 显示(更为重要),input 事件
THREAD_PRIORITY_AUDIO -16 音频相关
THREAD_PRIORITY_URGENT_AUDIO -19 音频(更为重要)
3.2 组优先级调度
进程组优先级调度方法
setProcessGroup(int pid, int group)setThreadGroup(int tid, int group)
组优先级及对应取值
THREAD_GROUP_DEFAULT -1 仅用于 setProcessGroup,将优先级<=10 的进程提升到-2
THREAD_GROUP_BG_NONINTERACTIVE 0 CPU 分时的时长缩短
THREAD_GROUP_FOREGROUND 1 CPU 分时的时长正常
THREAD_GROUP_SYSTEM 2 系统线程组
THREAD_GROUP_AUDIO_
APP 3 应用程序音频
THREAD_GROUP_AUDIO_SYS 4 系统程序音频
3.3 调度策略
调度策略设置方法
setThreadScheduler(int tid, int policy, int priority)
SCHED_OTHER 默认 标准 round-robin 分时共享策略
SCHED_BATCH 批处理调度 针对具有 batch 风格(批处理)进程的调度策略
SCHED_IDLE 空闲调度 针对优先级非常低的适合在后台运行的进程
SCHED_FIFO 先进先出 实时调度策略,android 暂未实现
SCHED_RR 循环调度 实时调度策略,android 暂未实现
3.4 进程 adj 调度
另外除了这些基本的调度策略,Android 系统还定义了两个和进程相关的状态值,一个就是定义在 ProcessList.java 里的 adj 值,另一个 是定义在 ActivityManager.java 里的 procState 值。
定义在 ProcessList.java 文件,oom_adj 划分为 16 级,从-17 到 16 之间取值。
UNKNOWN_ADJ 16 一般指将要会缓存进程,无法获取确定值
CACHED_APP_MAX_ADJ 15 不可见进程的 adj 最大值 1
CACHED_APP_MIN_ADJ 9 不可见进程的 adj 最小值 2
SERVICE_B_AD 8 B List 中的 Service(较老的、使用可能性更小)
PREVIOUS_APP_ADJ 7 上一个 App 的进程(往往通过按返回键)
HOME_APP_ADJ 6 Home 进程
SERVICE_ADJ 5 服务进程(Service process)
HEAVY_WEIGHT_APP_ADJ 4 后台的重量级进程,system/rootdir/init.rc 文件中设置
BACKUP_APP_ADJ 3 备份进程 3
PERCEPTIBLE_APP_ADJ 2 可感知进程,比如后台音乐播放 4
VISIBLE_APP_ADJ 1 可见进程(Visible process) 5
FOREGROUND_APP_ADJ 0 前台进程(Foreground process) 6
PERSISTENT_SERVICE_ADJ -11 关联着系统或 persistent 进程
PERSISTENT_PROC_ADJ -12 系统 persistent 进程,比如 telephony
SYSTEM_ADJ -16 系统进程
NATIVE_ADJ -17 native 进程(不被系统管理)
更新进程 adj 值的方法定义在 ActivityManagerService 中,分别为:
updateOomAdjLocked:更新 adj,当目标进程为空,或者被杀则返回 false;否则返回 true;
computeOomAdjLocked:计算 adj,返回计算后 RawAdj 值;
applyOomAdjLocked:应用 adj,当需要杀掉目标进程则返回 false;否则返回 true。
那么进程的 adj 值什么时候会被更新呢???
Activity
ActivityManagerService.realStartActivityLocked: 启动 Activity
ActivityStack.resumeTopActivityInnerLocked: 恢复栈顶 Activity
ActivityStack.finishCurrentActivityLocked: 结束当前 Activity
ActivityStack.destroyActivityLocked: 摧毁当前 Activity
Service
ActiveServices.realStartServiceLocked: 启动服务
ActiveServices.bindServiceLocked: 绑定服务(只更新当前 app)
ActiveServices.unbindServiceLocked: 解绑服务 (只更新当前 app)
ActiveServices.bringDownServiceLocked: 结束服务 (只更新当前 app)
ActiveServices.sendServiceArgsLocked: 在 bringup 或则 cleanup 服务过程调用 (只更新当前 app)
BroadcastReceiver
BroadcastQueue.processNextBroadcast: 处理下一个广播
BroadcastQueue.processCurBroadcastLocked: 处理当前广播
BroadcastQueue.deliverToRegisteredReceiverLocked: 分发已注册的广播 (只更新当前 app)
ContentProvider
ActivityManagerService.removeContentProvider: 移除 provider
ActivityManagerService.publishContentProviders: 发布 provider (只更新当前 app)
ActivityManagerService.getContentProviderImpl: 获取 provider (只更新当前 app)
另外,Lowmemorykiller 也会根据当前的内存情况逐级进行进程释放,一共有六个级别(上面加粗的部分):
CACHED_APP_MAX_ADJ
CACHED_APP_MIN_ADJ
BACKUP_APP_ADJ
PERCEPTIBLE_APP_ADJ
VISIBLE_APP_ADJ
FOREGROUND_APP_ADJ
定义在 ActivityManager.java 文件,process_state 划分 18 类,从-1 到 16 之间取值
PROCESS_STATE_CACHED_EMPTY 16 进程处于 cached 状态,且为空进程
PROCESS_STATE_CACHED_ACTIVITY_CLIENT 15 进程处于 cached 状态,且为另一个 cached 进程(内含 Activity)的 client 进程
PROCESS_STATE_CACHED_ACTIVITY 14 进程处于 cached 状态,且内含 Activity
PROCESS_STATE_LAST_ACTIVITY 13 后台进程,且拥有上一次显示的 Activity
PROCESS_STATE_HOME 12 后台进程,且拥有 home Activity
PROCESS_STATE_RECEIVER 11 后台进程,且正在运行 receiver
PROCESS_STATE_SERVICE 10 后台进程,且正在运行 service
评论