进程创建前

public static final ProcessStartResult start(final String processClass,

final String niceName,

int uid, int gid, int[] gids,

int debugFlags, int mountExternal,

int targetSdkVersion,

String seInfo,

String abi,

String instructionSet,

String appDataDir,

String[] zygoteArgs) {

try {

return startViaZygote(processClass, niceName, uid, gid, gids,

debugFlags, mountExternal, targetSdkVersion, seInfo,

abi, instructionSet, appDataDir, zygoteArgs);

} catch (ZygoteStartFailedEx ex) {

Log.e(LOG_TAG,

"Starting VM process through Zygote failed");

throw new RuntimeException(

"Starting VM process through Zygote failed", ex);

}

}

start函数里面没有做太多的事情,直接交给了startViaZygote

private static ProcessStartResult startViaZygote(final String processClass,

final String niceName,

final int uid, final int gid,

final int[] gids,

int debugFlags, int mountExternal,

int targetSdkVersion,

String seInfo,

String abi,

String instructionSet,

String appDataDir,

String[] extraArgs)

throws ZygoteStartFailedEx {

synchronized(Process.class) {

ArrayList argsForZygote = new ArrayList();

// --runtime-args, --setuid=, --setgid=,

// and --setgroups= must go first

argsForZygote.add("--runtime-args");

argsForZygote.add("--setuid=" + uid);

argsForZygote.add("--setgid=" + gid);

if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) {

argsForZygote.add("--enable-jni-logging");

}

if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {

argsForZygote.add("--enable-safemode");

}

....

argsForZygote.add(processClass);

if (extraArgs != null) {

for (String arg : extraArgs) {

argsForZygote.add(arg);

}

}

return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);

}

}

startViaZygote 函数前面都是对参数的整理。

后面交给了zygoteSendArgsAndGetResult。

但是这里我们需要注意的这边 openZygoteSocketIfNeeded 会打开一个socket,用于和zygote通讯,这个zygote之所以要一个abi参数因为在64位系统中有两个zygote进程。

root 264 1 1173156 127704 0 0000000000 S zygote64

root 265 1 934112 114496 0 0000000000 S zygote

就分别通讯的意思。

private static ProcessStartResult zygoteSendArgsAndGetResult(

ZygoteState zygoteState, ArrayList args)

throws ZygoteStartFailedEx {

try {

int sz = args.size();

for (int i = 0; i < sz; i++) {

if (args.get(i).indexOf('\n') >= 0) {

throw new ZygoteStartFailedEx("embedded newlines not allowed");

}

}

final BufferedWriter writer = zygoteState.writer;

final DataInputStream inputStream = zygoteState.inputStream;

writer.write(Integer.toString(args.size()));

writer.newLine();

for (int i = 0; i < sz; i++) {

String arg = args.get(i);

writer.write(arg);

writer.newLine();

}

writer.flush();

ProcessStartResult result = new ProcessStartResult();

result.pid = inputStream.readInt();

result.usingWrapper = inputStream.readBoolean();

if (result.pid < 0) {

throw new ZygoteStartFailedEx("fork() failed");

}

return result;

} catch (IOException ex) {

zygoteState.close();

throw new ZygoteStartFailedEx(ex);

}

}

上一步拿到了zygoteState 现在进行通讯,首先进行的参数的校验,如果没有问题就通过一个一个参数write传输过去给zygote。

zygote拿到这些参数就会给你创建好需要的进程。

然后返回结果通过read读取出来。

那么zygote那边是怎么创建进程呢?我们来看下zygote那边的工作。

进程的创建

zygote循环

zygoteInit.main()函数是zygote启动的时候会执行的函数,关于zygote启动这里不在详细解析。

public static void main(String argv[]) {

try {

runSelectLoop(abiList);

} catch (MethodAndArgsCaller caller) {

caller.run();

} catch (RuntimeException ex) {

closeServerSocket();

throw ex;

}

}

在main函数中会调用。runSelectLoop开启socket等待。

我们这里留意下这MethodAndArgsCaller异常。

private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {

ArrayList fds = new ArrayList();

ArrayList peers = new ArrayList();

fds.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 {

Os.poll(pollFds, -1);

} catch (ErrnoException ex) {

throw new RuntimeException("poll failed", ex);

}

for (int i = pollFds.length - 1; i >= 0; --i) {

if ((pollFds[i].revents & POLLIN) == 0) {

continue;

}

if (i == 0) {

ZygoteConnection newPeer = acceptCommandPeer(abiList);

peers.add(newPeer);

fds.add(newPeer.getFileDesciptor());

} else {

boolean done = peers.get(i).runOnce();

if (done) {

peers.remove(i);

fds.remove(i);

}

}

}

}

}

zygote起来以后会一直在这边循环等待,等待你们连接我并把需要创建进程的参数传输给我。有连接过来了,就会执行runOnce函数。

runOnce

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 {

parsedArgs = new Arguments(args);

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 {

if (pid == 0) {

handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);

// should never get here, the child is expected to either

// throw ZygoteInit.MethodAndArgsCaller or exec().

return true;

} else {

return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);

}

} finally {

IoUtils.closeQuietly(childPipeFd);

IoUtils.closeQuietly(serverPipeFd);

}

}

这里关键的地方就是forkAndSpecialize 前面都是收集参数等待。

后forkAndSpecialize fork进程后,父进程和紫禁城分道扬镳。----这个是linux进程创建的知识了

接下来我们要分开两条不同关注点去看进程的创建了

---  forkAndSpecialize 会调用linux的fork系统调用创建进程,创建后我们关注它的一些环境的建立。

---handleChildProc 进程创建后回去加载app的入口也就是ActivityThread。我们关注它是怎去加载的。

如果是单单看应用的启动,往应用层去理解呢,其实不太需要知道fork流程,如果想更深入了解系统的运行机制,可以一起来看下forkAndSpecialize到底做来什么东西。

进程的fork

上面讲到forkAndSpecialize 函数,我们这节的目的,看下forkAndSpecialize是怎么到底层调用linux的fork系统调用,从而开辟一个进程的。

要了解fork系统调用和运用的需要去了解linux的应用开发。这样才比较好了解进程的启动,在linux里面为什么用一个fork就创建了一个进程。

这个是需要一个基础知识的。

public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,

int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,

String instructionSet, String appDataDir) {

...

int pid = nativeForkAndSpecialize(

uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,

instructionSet, appDataDir);

...

return pid;

}

native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int debugFlags,

int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,

String instructionSet, String appDataDir);

我们来看到forkAndSpecialize 什么都没有做直接交给了nativeForkAndSpecialize,而nativeForkAndSpecialize是一个jni底层的函数。

这个函数的实现在com_android_internal_os_Zygote.cpp (frameworks\base\core\jni)

static const JNINativeMethod gMethods[] = {

{ "nativeForkAndSpecialize",

"(II[II[[IILjava/lang/String;Ljava/lang/String;[ILjava/lang/String;Ljava/lang/String;)I",

(void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },

在这个com_android_internal_os_Zygote.cpp 文件里面,我们看到它的jni实现是com_android_internal_os_Zygote_nativeForkAndSpecialize

static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(

JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,

jint debug_flags, jobjectArray rlimits,

jint mount_external, jstring se_info, jstring se_name,

jintArray fdsToClose, jstring instructionSet, jstring appDataDir) {

jlong capabilities = 0;

return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags,

rlimits, capabilities, capabilities, mount_external, se_info,

se_name, false, fdsToClose, instructionSet, appDataDir);

}

com_android_internal_os_Zygote_nativeForkAndSpecialize 这个函数也没有做太多的事情,直接交给了ForkAndSpecializeCommon。

static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,

jint debug_flags, jobjectArray javaRlimits,

jlong permittedCapabilities, jlong effectiveCapabilities,

jint mount_external,

jstring java_se_info, jstring java_se_name,

bool is_system_server, jintArray fdsToClose,

jstring instructionSet, jstring dataDir) {

SetSigChldHandler();

...

pid_t pid = fork();

if (pid == 0) {

..

if (!is_system_server) {

int rc = createProcessGroup(uid, getpid());

if (rc != 0) {

if (rc == -EROFS) {

ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");

} else {

ALOGE("createProcessGroup(%d, %d) failed: %s", uid, pid, strerror(-rc));

}

}

}

SetGids(env, javaGids);

...

int rc = setresgid(gid, gid, gid);

...

SetCapabilities(env, permittedCapabilities, effectiveCapabilities);

SetSchedulerPolicy(env);

...

rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);

...

if (se_info_c_str != NULL) {

SetThreadName(se_name_c_str);

}

...

} else if (pid > 0) {

...

}

return pid;

}

这个函数做的事情就有点多了,关键的是我们看到了fork()函数。

---在这个fork()函数之前做的是一些signal的设置

---fork()完成了以后兵分两路,子进程会去做很多gid 、scheduler 和 selinux等等的设置。

到这里我们就完整的看到了一个进程创建的过程。返回pid。

handleChildProc

现在进程出来了,我们需要一路返回,看看我们的进程会去做那些工作,是怎么走入到我们的apk代码里面的,主要是走到ActivityThread的过程。

我们回到handleChildProc,里面来。

private void handleChildProc(Arguments parsedArgs,

FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)

throws ZygoteInit.MethodAndArgsCaller {

if (parsedArgs.invokeWith != null) {

WrapperInit.execApplication(parsedArgs.invokeWith,

parsedArgs.niceName, parsedArgs.targetSdkVersion,

VMRuntime.getCurrentInstructionSet(),

pipeFd, parsedArgs.remainingArgs);

} else {

RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,

parsedArgs.remainingArgs, null /* classLoader */);

}

}

}

我们的进程已经创建完成来。handleChildProc是首先会调用的函数,而这个函数又调用来RuntimeInit.zygoteInit,为什么是讲这个函数而不去讲上面的函数?

我这里讲一个简单的linux知识。

linux进程创建也是一样的会直接fork,fork完成后如果你要加载代码一般是用execv系统调用去加载代码的,但是Android,使用的是java虚拟机。

所以,上面的流程是给一些本地进程走的。而java是通过Class进行类加载,来我们来一口气读完下面三个函数。

看下是怎么类加载的。

public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)

throws ZygoteInit.MethodAndArgsCaller {

...

applicationInit(targetSdkVersion, argv, classLoader);

}

private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)

throws ZygoteInit.MethodAndArgsCaller {

...

VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);

VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);

...

invokeStaticMain(args.startClass, args.startArgs, classLoader);

}

private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)

throws ZygoteInit.MethodAndArgsCaller {

Class> cl;

try {

cl = Class.forName(className, true, classLoader);

} catch (ClassNotFoundException ex) {

...

}

Method m;

try {

m = cl.getMethod("main", new Class[] { String[].class });

} catch (NoSuchMethodException ex) {

...

}

...

throw new ZygoteInit.MethodAndArgsCaller(m, argv);

}

这里我们关注下面流程:

1. 首先设置虚拟机的环境

2. 调用invokeStaticMain

3.在invokeStaticMain,我们找到这个类也就是ActivityThread类,这到这个类的Method,也就是main函数。

4.最后居然没有运行这个类而是抛出一个异常。很匪夷所思,根据反射调用的话,应该是要 m.invoke(null, arg);才对的。

那么它抛出这个异常是在哪里catch的呢?

还记得文章最前面我们说要关注的抛出的异常吗?

在前面zygote循环的时候我说要关注的异常。

public static void main(String argv[]) {

try {

runSelectLoop(abiList);

} catch (MethodAndArgsCaller caller) {

caller.run();

} catch (RuntimeException ex) {

closeServerSocket();

throw ex;

}

}

他就是在这里catch了这个异常然后调用了这个异常的run函数。

public MethodAndArgsCaller(Method method, String[] args) {

mMethod = method;

mArgs = args;

}

public void run() {

try {

mMethod.invoke(null, new Object[] { mArgs });

} catch (IllegalAccessException ex) {

throw new RuntimeException(ex);

} catch (InvocationTargetException ex) {

...

throw new RuntimeException(ex);

}

}

然后再这里进行了invoke反射调用。

至于为什么要通过抛出异常的方法去做这个调用而不是直接调用,网上早有人给出了答案。这里就不多说。

到现在整个进程启动的流程就结束了。

ActivityThread类加载起来以后,会和ams交互,接下来会调用到你的activity的onCreate方法,onResume方法。

然后你的apk就完美运行了。

android android:process=,Android app启动流程相关推荐

  1. android源码学习- APP启动流程(android12源码)

    前言: 百度一搜能找到很多讲APP启动流程的,但是往往要么就是太老旧(还是基于android6去分析的),要么就是不全(往往只讲了整个流程的一小部分).所以我结合网上现有的文章,以及源码的阅读和调试, ...

  2. Android App启动流程详解

    前言:在之前的文章中已经写了apk的打包流程.安装流程,今天就是梳理一下apk系列的最后的流程--app启动流程.经过今天的梳理以后咱们就可以对apk包是怎么编译生成的.apk是怎么被安装到安卓手机的 ...

  3. Android入门之APP启动流程

    俗话说,要想优化好,流程不可少.作为一款App的开发者,首先要把它的启动流程做好了,简单明了的启动流程不仅拥有很好的体验感,还能获得更多用户对App的肯定.本篇文章就带大家了解下app启动流程的三个进 ...

  4. 【Android】之【App启动】

    一.启动方式 Android 应用的启动方式大概分为热启动.冷启动.温启动三种,关于冷启动.热启动.温启动三者启动方式对比可以参考下面的流程图学习. 1.1 冷启动 冷启动具有耗时最多,衡量标准的特征 ...

  5. APP启动流程:从点击应用图标到Activity启动流程

    app启动流程: ①点击桌面App图标,Launcher进程采用Binder IPC向system_server进程发起startActivity请求: ②system_server进程接收到请求后判 ...

  6. 【线上沙龙直播报名】App 启动流程详解及其优化

    点击上方"公众号"可以订阅哦 [美团点评技术沙龙Online]是美团点评技术团队推出的线上分享课程,每月2-3期,采用目前最火热的线上直播形式,邀请美团点评技术专家,面向互联网技术 ...

  7. 12.源码阅读(app启动流程-android api 26)

    activity的启动流程之前已经通过源码了解了,那么app的启动流程是怎样的,从我们按下app的图标,到应用启动起来显示出画面,中间都经历了什么? 安卓是基于java的,所以和java有一定的相似性 ...

  8. Android 11(platfrom 30)APP启动流程(含Activity)核心点记录

    前言:边分析,边记录 不断更新. 注意:阅读此文请同时打开android-30的源码,一步一步按文章同步跟进阅读源码,因为源码量太大,这里只能贴出部分核心代码. 场景一.从桌面点击APP图标进行启动 ...

  9. android开发进阶(一)-- android app启动流程

    这篇文章写得很详细: http://www.jianshu.com/p/a5532ecc8377 作者曾经在高通的Android性能组工作, 主要工作是优化Android Application的启动 ...

  10. android app启动流程分析,Android应用开发之Android 7.0 Launcher3的启动和加载流程分析...

    本文将带你了解Android应用开发Android 7.0 Launcher3的启动和加载流程分析,希望本文对大家学Android有所帮助. Android 7.0 Launcher3的启动和加载流程 ...

最新文章

  1. 皮一皮:你有没有为中国大数据力量做一份贡献!
  2. 【Android 安全】DEX 加密 ( Java 工具开发 | 加密解密算法 API | 编译代理 Application 依赖库 | 解压依赖库 aar 文件 )
  3. 如何提高数据安全性与可用性——行云管家堡垒机
  4. 笔记-信息化与系统集成技术-供应链管理
  5. WPF Slider Menu
  6. 日期减三个月oracle_ORACLE 日期加减操作
  7. LiveVideoStackCon深圳 - VR/AR基础技术更成熟
  8. matlab怎么安装compiler,关于MATLAB中compiler配置问题
  9. matlab算出中心差分方法_方差分析、T检验、卡方分析如何区分?
  10. LeetCode--34.在排序数组中查找元素第一个和最后一个位置(二分法)
  11. 用Java开发一个停车场系统
  12. 1.5不同类型的循环神经网络
  13. NBU查看备份集大小
  14. windows驱动安装卸载的实用小工具-InstDrv.exe
  15. 非平稳序列的随机分析
  16. 【动画消消乐】HTML+CSS 自定义加载动画 055
  17. AJAX IE7清除缓存
  18. MDT 2013 Update 1 Preview 部署 Windows 10之WDS部署服务配置
  19. golang基础教程
  20. 渗压计和水位计的区别

热门文章

  1. STM32 基础系列教程 12 – ADC 中断
  2. 竞争解决定时器时长( mac-ContentionResolutionTimer)
  3. ERROR: This virtual machine appears to be in use
  4. C++异常处理(try和catch)
  5. ASP.NET中数据库数据导入Excel并打印
  6. O029、教你看懂OpenStack日志
  7. 人工智能最佳学习实践
  8. 怎么给工作中重要的pdf文件加密
  9. CrazyWing:Python自动化运维开发实战 九、Python数据类型之列表
  10. 2017 OWASP十大安全趋势榜单变化解析