点击上方 "程序员小乐"关注公众号, 星标或置顶一起成长
每天凌晨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不变。普通应用程序而言,GID = UID。
adb shell ps | grap packageName
可以查看在运行的应用程序信息
图中APP使用了多进程,因此根据包名查处了两个进程信息。横行信息依次为 当前用户(UID)、进程ID(PID)、父进程ID(PPID)、进程的虚拟内存大小、实际内存占用大小、进程正在睡眠的内核函数名称。
如所说,无论多少次杀掉此APP进程再重启,UID均不变。
GIDS则与Application申请的具体权限有关,申请的相应permission被granted时,则有对应的GIDS,因此,GIDS是关于允许或限制应用程序访问设备资源。比如常在manifest申请访问网络权限
<uses-permission android:name="android.permission.INTERNET"/>被允许时,组在GIDS中有相应的ID
在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
欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,学习能力的提升上有新的认识,欢迎转发分享给更多人。
猜你还想看