在群里被问到......!应用是如何运行起来的?

mac2024-12-19  7

点击上方 "程序员小乐"关注公众号, 星标或置顶一起成长

每天凌晨00点00分, 第一时间与你相约

每日英文

Destiny decides who enters your life, but you get to decide who stays.

谁走进你的生命,是由命运决定;谁停留在你生命中,却是由你自己决定。

每日掏心话

人终将会变老,事物也随之变更,很多时候,一个人发现自己爱上了一个人,都是在跟他分别的时候。

来自:MxsQ | 责编:乐乐

链接:jianshu.com/p/dcff7ac41fd8

程序员小乐(ID:study_tech)第 673 次推文   图片来自网络

   00 前言   

APP各自运行于自己的进程中,每一个进程中都拥有一个独立的Dalvik虚拟机实例,拥有了Dalvik,Android的Java程序才能运行起来。可以理解为,进程在以隔离了用户环境下运行,使各不干扰。常用的四大组件,要能运行起来,首先就需要APP的进程已准备完毕。

本文的目的,是学习进程是如如何被创建出来的。

note:源码版本为8.0,各版本间有差异,但不影响核心设计

   01 进程启动必要参数准备   

如在Activity的启动过程中,当AMS发现此Activity所依赖的应用进程还没有启动起来,则会请求Zygote进程将此应用进程启动起来。

https://www.jianshu.com/p/4fe598f3c235

代码移步ActivityManagerService.AMS.startProcessLocked()

private final void startProcessLocked(ProcessRecord app, String hostingType,         String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {         ......     try {         ......         // 进程uid         int uid = app.uid;         // GIDS         int[] gids = null;         int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;         if(!app.isolated){         ......             // 记住资源访问权限             if (ArrayUtils.isEmpty(permGids)) {                 gids = new int[3];             } else {                 gids = new int[permGids.length + 3];                 System.arraycopy(permGids, 0, gids, 3, permGids.length);             }             gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));             gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));             // shareUserId             gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));         }         ......         // 指明应用程序入口         if (entryPoint == null) entryPoint = "android.app.ActivityThread";         if(...){         } else{             //交给 Zygote 去孵化             startResult = Process.start(entryPoint,                     app.processName, uid, uid, gids, debugFlags, mountExternal,                     app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,                     app.info.dataDir, invokeWith, entryPointArgs);         }     }  }

代码总app为ProcessRecord,ProcessRecord描述了一个进程,与进程相关的信息存于ProcessRecord中,AMS也是通过ProcessRecord才能得知进程状况。

UID

UID标识一个应用进程,应用程序在安装时被分配UID,当此应用程序续存期间(没卸载),UID不变。普通应用程序而言,GID = UID。

adb shell ps | grap packageName

可以查看在运行的应用程序信息

图中APP使用了多进程,因此根据包名查处了两个进程信息。横行信息依次为 当前用户(UID)、进程ID(PID)、父进程ID(PPID)、进程的虚拟内存大小、实际内存占用大小、进程正在睡眠的内核函数名称。

如所说,无论多少次杀掉此APP进程再重启,UID均不变。

GIDS

GIDS则与Application申请的具体权限有关,申请的相应permission被granted时,则有对应的GIDS,因此,GIDS是关于允许或限制应用程序访问设备资源。比如常在manifest申请访问网络权限

<uses-permission android:name="android.permission.INTERNET"/>

被允许时,组在GIDS中有相应的ID

shareUserId

在GIDS中,shareUserId也比较重要,需简单了解沙箱模型。

各个应用程序运行与各自的环境中(沙箱),默认情况下,程序间无法交互。运行在沙箱内的应用程序如果没有被分配权限,无法访问系统或资源,因此运行去Dalvik虚拟机的应用程序都能得到同样的安全保护,被限制在各自沙箱内的程序互不干涉,使得对其它或受其它程序的损害降到最低。

在沙箱机制下,应用程序间互不信任,相互隔离,各自运行。

通过shareUserId,可是程序相互信任,公共运行与同一进程空间内,可以像访问自有程序一样访问对方程序资源。

https://blog.csdn.net/ljheee/article/details/53191397

回到正文,在准备好启动进程的相关数据后,交接给下一节点继续执行。还需注意被赋值为“android.app.ActivityThread”的变量entryPoint,后续会通过此变量启动ActivityThread.main(),也就指定了应用程序的入口。

   02 启动准备   

此后调用链路为

->  ZygoteProcess.start()->  ZygoteProcess.startViaZygote()

private Process.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 invokeWith,  String[] extraArgs) throws ZygoteStartFailedEx {   // 存储启动参数   ArrayList<String> argsForZygote = new ArrayList<String>();   // 以下三参数必有   argsForZygote.add("--runtime-args");   argsForZygote.add("--setuid=" + uid);   argsForZygote.add("--setgid=" + gid);   // 其它参数看具体情况   ......   synchronized(mLock) {       // 获取LocalkSocket以连接Zygote进程,后将参数通过LocalSocket       // 写入Zygote进程,让Zygote进程进行创建       return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);   }  }

视情况准备进程启动需要的参数,再获取用以和进程进行连接的ZygoteState,便可以向Zygote进程写入此次新建进程的请求。

private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {     Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held");     if (primaryZygoteState == null || primaryZygoteState.isClosed()) {         try {             primaryZygoteState = ZygoteState.connect(mSocket);         } catch (IOException ioe) {             throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);         }     }     if (primaryZygoteState.matches(abi)) {         return primaryZygoteState;     }     // The primary zygote didn't match. Try the secondary.     if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {         try {             secondaryZygoteState = ZygoteState.connect(mSecondarySocket);         } catch (IOException ioe) {             throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);         }     }     if (secondaryZygoteState.matches(abi)) {         return secondaryZygoteState;     }     throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi); }

primaryZygoteState 和 secondaryZygoteState 均为 ZygoteState, Android 5.0以后,Android开始支持64位编译,Zygote进程也随之引入了32/64位的区别。

顾名思义,primaryZygoteState为主Zygote,secondaryZygoteState为辅Zygote。至于是32位为主,还是64位辅,就要看具体Zygote文件的配置了。这里以ABI架构类型为引子,找到合适的ZygoteSate。

如果所查找的ZygoteState未创建或连接状态已关闭,则会获取此ZygoteState。

public static ZygoteState connect(String socketAddress) throws IOException {     DataInputStream zygoteInputStream = null;     BufferedWriter zygoteWriter = null;     final LocalSocket zygoteSocket = new LocalSocket();     try {         zygoteSocket.connect(new LocalSocketAddress(socketAddress,                 LocalSocketAddress.Namespace.RESERVED));         zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());         zygoteWriter = new BufferedWriter(new OutputStreamWriter(                 zygoteSocket.getOutputStream()), 256);     } catch (IOException ex) {         try {             zygoteSocket.close();         } catch (IOException ignore) {         }         throw ex;     }     String abiListString = getAbiList(zygoteWriter, zygoteInputStream);     Log.i("Zygote", "Process: zygote socket " + socketAddress + " opened, supported ABIS: "             + abiListString);     return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,             Arrays.asList(abiListString.split(","))); }

新建的LocalSocket为ZygoteState成员变量,ZygoteState使用的输入流与输出流均从LocalSocket拿到。

参数socketAddress为Process创建ZygoteProcess传入的ZYGOTE_SOCKET,即"zygote"。

因此LocalSocket.connect()将与Zygote进程中名为“zygote”的Socket进行连接。在有了能与Zygote进程通信的媒介之后,已可以向Zygote进程传入数据或从Zygote进程传输数据。

   03 创建进程   

回到

-> ZygoteProcess.startViaZygote()-> zygoteSendArgsAndGetResult()

private static Process.ProcessStartResult zygoteSendArgsAndGetResult(     ZygoteState zygoteState, ArrayList<String> args)     throws ZygoteStartFailedEx {     ......   // 写入启动参数长度     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();     // Should there be a timeout on this?     Process.ProcessStartResult result = new Process.ProcessStartResult();     // 拿到进程创建的结果     result.pid = inputStream.readInt();     result.usingWrapper = inputStream.readBoolean();     if (result.pid < 0) {         throw new ZygoteStartFailedEx("fork() failed");     }     return result; }

通过输出流向Zygote进程写入启动数据后,进入阻塞状态,直到能接受到Zygote进程反馈的数据。拿到了创建的结果,意味着进程创建的结束。

目前来看,Process通过LocalSocket与Zygote进程进行通信,传输关键数据即可创建进程,因此还可对Zygote如何创建进程加以了解。

   04 Zygote进程   

简要了解一下Zygote进程的创建,见ZygoteInit.main()

public static void main(String argv[]) {         // 创建ZygoteServer         ZygoteServer zygoteServer = new ZygoteServer();         ......         try{         ......         // 注册名为 "zygote的socket"         zygoteServer.registerServerSocket(socketName);         ......         // 接收请求         zygoteServer.runSelectLoop(abiList);         ......         }    }

在Zygote进程创建时,向ZygoteServer注册一个名为"zygote"的服务端Socket,之前所说的新建进程请求到来时,即是与此Socket进行通信。当请求到来,在zygoteServer.runSelectLoop()处接收请求。

void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {     ......     boolean done = peers.get(i).runOnce(this); }

实际由ZygoteConnection.runOnce()进程处理

boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller { String args[]; Arguments parsedArgs = null; FileDescriptor[] descriptors; try {     // 获取LocalSocket传来的启动参数     args = readArgumentList();     descriptors = mSocket.getAncillaryFileDescriptors(); } ...... tyr{     // 解析参数     parsedArgs = new Arguments(args);     ......     // frok()     pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,             parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,             parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,             parsedArgs.appDataDir); } ...... try{     // pid = 0 说明创建了进程     if (pid == 0) {         // in child         zygoteServer.closeServerSocket();         IoUtils.closeQuietly(serverPipeFd);         serverPipeFd = null;         handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);         return true;     } }

在接收并解析出LocalSocket所传输过来的启动参数后,通过Zygote.forkAndSpecialize()让native真正fork()出进程。

当PID = 0 时,说明Zygote进程创建出了子进程。

当前情况下,需要Zygote进程孵化出的进程。Zygote进程作为Android进程的母体,包含各应用进程所需要的进程信息和资源信息,因从从Zygote进程fork()出的子进程具有Zygote进程信息如数据段、代码段等,自然也包括Dalvik虚拟机实体。

在进程创建完后,由

ZygoteConnection.handleChildProc()-> ZygoteInit.zygoteInit() 处理进一步事宜

applicationInit()通过反射唤醒ActivityThread.main(),之前在AMS请求启动新进程时特意调到的entryPoint,也一并通过LocalSocket作为参数之一传入Zygote进程,因此在这里可以知道此唤醒信息。

AMS在请求启动新进程的同时,不会一直等待进程创建,延迟发送消息PROC_START_TIMEOUT_MSG,如果进程为内在PROC_START_TIMEOUT_MSG时间内成功创建,则AMS会认为进程启动失败。

因此在,在唤醒ActivityThread.main()后attach()时机,会通知AMS取消PROC_START_TIMEOUT_MSG消息,以后保证后续流程进行。

至此,也就了解了进程时如何被创建出来。

总结

进程创建如下:

参考

浅析Android沙箱模型

blog.csdn.net/ljheee/article/details/53191397

Android -- 系统进程Zygote的启动分析

blog.csdn.net/csdn_of_coder/article/details/52892266

Android ABI的浅析

jianshu.com/p/d2119b3880d8

欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,学习能力的提升上有新的认识,欢迎转发分享给更多人。

猜你还想看

最新回复(0)