目录
一,背景介绍
二,应用进程启动过程
三,原因分析
AMS通知Zygote去fork进程的时候,使用的是socket的方式,而不是binder。我们都知道,安卓中跨进程的方式是binder,而为什么这里偏偏使用Socket呢?有点人会说LocalSocket本来就是用来本地进程间通讯的,这没错,但知其然更要知其所以然。
android系统点击桌面上的APP图标时,如果应用进程不存在,就会通过AMS通知zygote进程fork新进程,启动流程如下,
1,实现在ActivityManagerService的startProcessLocked方法中,路径frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
HostingRecord hostingRecord, int zygotePolicyFlags, boolean allowWhileBooting,
boolean isolated, boolean keepIfLarge) {
return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
hostingRecord, zygotePolicyFlags, allowWhileBooting, isolated, 0 /* isolatedUid */,
keepIfLarge, null /* ABI override */, null /* entryPoint */,
null /* entryPointArgs */, null /* crashHandler */);
}
2,调用到frameworks/base/services/core/java/com/android/server/am/ProcessList.java,
private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
ProcessRecord app, int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags,
int mountExternal, String seInfo, String requiredAbi, String instructionSet,
String invokeWith, long startTime) {
-----------------------------------------------------------------------------
if (hostingRecord.usesWebviewZygote()) {
startResult = startWebView(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName, app.mDisabledCompatChanges,
new String[]{PROC_START_SEQ_IDENT + app.startSeq});
} else if (hostingRecord.usesAppZygote()) {
final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
// We can't isolate app data and storage data as parent zygote already did that.
startResult = appZygote.getProcess().start(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
/*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
app.mDisabledCompatChanges, pkgDataInfoMap, whitelistedAppDataInfoMap,
false, false,
new String[]{PROC_START_SEQ_IDENT + app.startSeq});
} else {
startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap,
whitelistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
new String[]{PROC_START_SEQ_IDENT + app.startSeq});
}
-------------------------------------------------------------------------------
}
3,调用frameworks/base/core/java/android/os/Process.java
public static ProcessStartResult start(@NonNull final String processClass,
@Nullable final String niceName,
int uid, int gid, @Nullable int[] gids,
int runtimeFlags,
int mountExternal,
int targetSdkVersion,
@Nullable String seInfo,
@NonNull String abi,
@Nullable String instructionSet,
@Nullable String appDataDir,
@Nullable String invokeWith,
@Nullable String packageName,
int zygotePolicyFlags,
boolean isTopApp,
@Nullable long[] disabledCompatChanges,
@Nullable Map>
pkgDataInfoMap,
@Nullable Map>
whitelistedDataInfoMap,
boolean bindMountAppsData,
boolean bindMountAppStorageDirs,
@Nullable String[] zygoteArgs) {
return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, packageName,
zygotePolicyFlags, isTopApp, disabledCompatChanges,
pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
bindMountAppStorageDirs, zygoteArgs);
}
4,路径frameworks/base/core/java/android/os/ZygoteProcess.java,
private Process.ProcessStartResult startViaZygote(@NonNull final String processClass,
@Nullable final String niceName,
final int uid, final int gid,
@Nullable final int[] gids,
int runtimeFlags, int mountExternal,
int targetSdkVersion,
@Nullable String seInfo,
@NonNull String abi,
@Nullable String instructionSet,
@Nullable String appDataDir,
@Nullable String invokeWith,
boolean startChildZygote,
@Nullable String packageName,
int zygotePolicyFlags,
boolean isTopApp,
@Nullable long[] disabledCompatChanges,
@Nullable Map>
pkgDataInfoMap,
@Nullable Map>
whitelistedDataInfoMap,
boolean bindMountAppsData,
boolean bindMountAppStorageDirs,
@Nullable String[] extraArgs)
throws ZygoteStartFailedEx {
-------------------------------------------------------------------------------
synchronized(mLock) {
// The USAP pool can not be used if the application will not use the systems graphics
// driver. If that driver is requested use the Zygote application start path.
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
zygotePolicyFlags,
argsForZygote);
}
-------------------------------------------------------------------------------
}
5,其中,关键调用如下,
/**
* Tries to open a session socket to a Zygote process with a compatible ABI if one is not
* already open. If a compatible session socket is already open that session socket is returned.
* This function may block and may have to try connecting to multiple Zygotes to find the
* appropriate one. Requires that mLock be held.
*/
@GuardedBy("mLock")
private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
try {
attemptConnectionToPrimaryZygote();
if (primaryZygoteState.matches(abi)) {
return primaryZygoteState;
}
if (mZygoteSecondarySocketAddress != null) {
// The primary zygote didn't match. Try the secondary.
attemptConnectionToSecondaryZygote();
if (secondaryZygoteState.matches(abi)) {
return secondaryZygoteState;
}
}
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to zygote", ioe);
}
throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
}
调用了zygoteSendArgsAndGetResult方法,传入的参数为openZygoteSocketIfNeeded返回值ZygoteState,ZygoteState是ZygoteProcess的静态内部类,表示了与ZYgote进程的通信状态。openZygoteSocketIfNeeded其实就是与Zogote进程中创建的Socket建立连接。之后就是socket通信的过程了。
在第二部分应用进程启动过程中可知,AMS通知Zygote去fork进程的时候,使用的是socket的方式,而不是binder。android为何这么设计呢?如果采用binder来实现,会导致啥问题?
在 POSIX 标准中,fork 的行为是这样的:复制整个用户空间的数据,通常使用 copy-on-write 的策略, 所以可以实现的速度很快)以及所有系统对象,然后仅复制当前线程到子进程。这里所有父进程中其它线程(除了当前执行fork线程外的线程),到了子进程中都是突然蒸发掉的。
对于锁来说,从 OS 看,每个锁有一个所有者,即最后一次 lock 它的线程。假设这么一个环境,在 fork 之前,有一个子线程 lock 了某个锁,获得了对锁的所有权。fork 以后,在子进程中,所有的其它线程都消失了。而锁却被正常复制了,在子进程看来,这个锁没有这个锁没有主人,所以没有任何人可以对它解锁。当子进程想unlock 这个锁时,不再有任何手段可以解开了,程序发生死锁。
总结起来就是怕父进程binder线程有锁,然后子进程的主线程一直在等其子线程(从父进程拷贝过来的子进程)的资源,但是其实父进程的子进程并没有被拷贝过来,造成死锁,所以fork不允许存在多线程。而非常巧的是Binder通讯偏偏就是多线程,所以干脆父进程(Zgote)这个时候就不使用binder线程。