Android进程框架:进程的创建、启动与调度流程,Android学习笔记在互联网上火了

本文详细阐述了Android系统中Zygote进程的启动流程,包括接收Socket连接、创建子进程、应用初始化以及进程调度策略。重点介绍了ZygoteConnection和RuntimeInit.zygoteInit方法的工作原理,展示了Android进程管理和通信的关键环节。
摘要由CSDN通过智能技术生成

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 child
IoUtils.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 failure
IoUtils.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 main
invokeStaticMain(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(“”);
//创建主线程looper
Looper.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

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后

针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

资料领取:点赞免费获取Android IOC架构设计

领取获取往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、混合式开发(ReactNative+Weex)全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。

开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!**

资料领取:点赞免费获取Android IOC架构设计

领取获取往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、混合式开发(ReactNative+Weex)全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。

[外链图片转存中…(img-jANiqwzz-1711172050220)]

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值