写点什么

Android 进程框架:进程的创建、启动与调度流程

发布于: 2021 年 11 月 07 日

Arrays.asList(abiListString.split(",")));}}


建立 Socket 连接的流程很明朗了,如下所示:


  1. 创建 LocalSocket 对象。

  2. 将 LocalSocket 与 LocalServerSocket 建立连接,建立连接的过程就是 LocalSocket 对象在/dev/socket 目录下查找一个名称为"zygote"的文件,然后将自己与其绑定起来,这样就建立了连接。

  3. 创建 LocalSocket 的输入流,以便可以接收 Zygote 进程发送过来的数据。

  4. 创建 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 发来的请求。请求分为两种:


  1. 连接请求

  2. 数据请求


没有连接请求时 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 出新进程,该方法是创建新进程的核心方法,它主要会陆续调用三个 方法来完成工作:


  1. preFork():先停止 Zygote 进程的四个 Daemon 子线程的运行以及初始化 GC 堆。这四个 Daemon 子线程分别为:Java 堆内存管理现场、堆线下引用队列线程、析构线程与监控线程。

  2. nativeForkAndSpecialize():调用 Linux 系统函数 fork()创建新进程,创建 Java 堆处理的线程池,重置 GC 性能数据,设置进程的信号处理函数,启动 JDWP 线程。

  3. 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);}}


该方法主要完成三件事:


  1. 调用 commonInit()方法创建应用进程的时区和键盘等通用信息。

  2. 调用 nativeZygoteInit()方法在应用进程中创建一个 Binder 线程池。

  3. 调用 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_


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


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

评论

发布
暂无评论
Android进程框架:进程的创建、启动与调度流程