ZygoteInit 相关分析
上一篇文章我们已经分析到调用com.android.internal.os.ZygoteInit类的main函数。
今天分析一下com.android.internal.os.ZygoteInit类的main函数。
public static void main(String argv[]) {
// 注册zygote的socket
registerZygoteSocket();
//加载Android Application Framework使用的类与资源
preloadClasses();
preloadResources(); //启动SystemServer
if(argv[1].equals("true")) {
startSystemServer();
}
/**
* 处理客户端连接及请求。由zygoteConnection的runOnce函数来处理。
*/
runSelectLoopMode();
}
在main函数我们就主要看registerZygoteSocket()与runSelectLoopMode()。
- registerZygoteSocket():就是创建一个localsocket,也就是说注册了一个zygote的服务端socket。
- runSelectLoopMode():就是一直无限循环,等待客户端socket的连接,然后通过zygoteConnection的runOnce函数来处理对应的请求。
PreloadClass(): 读取framework/base/tools/preload/preload_class文件约1268类。
PreloadResources(): 加载framework-res.apk中的资源。
gc():执行垃圾收集。
那么我们就看一下registerZygoteSocket()
private static void registerZygoteSocket() { String env = System.getenv(ANDROID_SOCKET_ENV);
fileDesc = Integer.parseInt(env); if (sServerSocket == null) {
sServerSocket = new LocalServerSocket(
createFileDescriptor(fileDesc));
}
}
很简单就是创建一个LocalServerSocket。
建立IPC通信服务器,从环境变量中获取(System.getenv(ANDROID_SOCKET_ENV);)socket的fd,之前用sig的fd来调用handle去创建的zygote。
我们在来看一下runSelectLoopMode()
private static void runSelectLoopMode() throws MethodAndArgsCaller {
…
while (true) {
int index; fdArray = fds.toArray(fdArray);
index = selectReadable(fdArray); if(index == ) {
//连接客户端发送的请求(Command)
ZygoteConnection newPeer = acceptCommandPeer();
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
//处理客户端发送的请求(Command)
boolean done;
done = peers.get(index).runOnce();
}
}
}
这个函数也很简单等待客户端发来的请求,当接收到请求时就运行run0nce函数去处理请求即可。
客户端可以理解成AMS或SystemServer。
在这里简单说明一下AMS与SystemServer
- SystemServer:
Zygote的子进程,为JAVA世界做服务。
Java世界系统Service,此进程是framework的核心,此server挂掉了,zygote会自杀。
Zygote.forkSystemServer()而诞生。
- AMS
ActivityManagerService是由SystemServer创建。
当一个Acitvity开启的时候,是由AMS中开启一个新的Process,而在Process类中最终会创建一个LocalSocket去连接ZYGOTE_SOCKET。
而在AMS中的startProcessLocked()中调用Process.start()函数中会传入一个参数-android.app.ActivityThread。最终会通过socket传入zygote处理。
我们继续看以下done = peers.get(index).runOnce();
boolean runOnce( ) {
Arguments parsedArgs = null;
FileDescriptor[] descriptors; //Reads command socket.
args = readArgumentList();
descriptors = mSocket.getAncillaryFileDescriptors(); try {
parsedArgs = new Arguments(args);
...... pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName);
} catch (IOException ex) {
......
} catch (ErrnoException ex) {
......
} catch (IllegalArgumentException ex) {
......
} catch (ZygoteSecurityException ex) {
......
} //新创建的进程 pid == 0
if (pid == ) {
// in child
serverPipeFd = null;
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
return true;
} else { // in parent,也就是Zygote进程执行过程
childPipeFd = null;
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
}
}
我们分析一下以下几个函数
- readArgumentList()与parsedArgs = new Arguments(args) 是用来获取command和解析command的。
- Zygote.forkAndSpecialize()是很简单的,就是去调用native函数来fork的。
- handleChildProc()是处理子进程。
- handleParentProc()是处理父进程。也就是zygote进程,此函数会原路返回,通过socket的写入返还给systemserver最终到AMS,然后重新进入runSelectLoopMode。
首先我们来看一下forkAndSpecialize()
这个函数定义在文件libcore/dalvik/src/main/java/dalvik/system/Zygote.java中。
public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName) {
preFork();
int pid = nativeForkAndSpecialize(uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName);
postFork();
return pid;
}
很简单就是调用native的ForkAndSpecialize函数。
JNI函数nativeForkAndSpecialize的由Dalvik_dalvik_system_Zygote_forkAndSpecialize函数来实现。
那我们就去看一下。
这个函数定义在文件dalvik/vm/native/dalvik_system_Zygote.cpp中。
static void Dalvik_dalvik_system_Zygote_forkAndSpecialize(const u4* args, JValue* pResult)
{
pid_t pid;
//args指向的一块内存中
pid = forkAndSpecializeCommon(args, false); RETURN_INT(pid);
}
Dalvik_dalvik_system_Zygote_forkAndSpecialize函数通过调用forkAndSpecializeCommon函数来执行创建进程,
实现如下所示:
这个函数定义在文件dalvik/vm/native/dalvik_system_Zygote.cpp中。
static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer)
{
pid_t pid; uid_t uid = (uid_t) args[];
gid_t gid = (gid_t) args[];
......
char *seInfo = NULL;
char *niceName = NULL; if (isSystemServer) {
//参数isSystemServer表示true时创建的是System Server进程。
......
} else {
//参数isSystemServer表示true时创建的是应用程序进程。
......
} ...... pid = fork();
//表示当前是新创建的进程
if (pid == ) {
...... err = setSELinuxContext(uid, isSystemServer, seInfo, niceName);
......
} ...... return pid;
}
我们不去看具体代码实现,主要了解一下主要是干什么的就可以了。
我们要知道的是zygote是负责应用程序进程与SystemServer的进程就可以了。
接下来我们回到之前的handleParentProc()
private boolean handleParentProc(int pid,
FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs) {
if (pid > ) {
setChildPgid(pid);
}
if (descriptors != null) {
for (FileDescriptor fd: descriptors) {
IoUtils.closeQuietly(fd);
}
}
boolean usingWrapper = false;
if (pipeFd != null && pid > ) {
DataInputStream is = new DataInputStream(new FileInputStream(pipeFd));
int innerPid = -;
try {
innerPid = is.readInt();
} catch (IOException ex) {
Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex);
} finally {
try {
is.close();
} catch (IOException ex) {
}
}
// Ensure that the pid reported by the wrapped process is either the
// child process that we forked, or a descendant of it.
if (innerPid > ) {
int parentPid = innerPid;
while (parentPid > && parentPid != pid) {
parentPid = Process.getParentPid(parentPid);
}
if (parentPid > ) {
Log.i(TAG, "Wrapped process has pid " + innerPid);
pid = innerPid;
usingWrapper = true;
} else {
Log.w(TAG, "Wrapped process reported a pid that is not a child of "
+ "the process that we forked: childPid=" + pid
+ " innerPid=" + innerPid);
}
}
}
//将创建的应用程序进程ID返回给SystemServer进程的ActivityManagerService服务
try {
mSocketOutStream.writeInt(pid);
mSocketOutStream.writeBoolean(usingWrapper);
} catch (IOException ex) {
Log.e(TAG, "Error reading from command socket", ex);
return true;
}
/*
* If the peer wants to use the socket to wait on the
* newly spawned process, then we're all done.
*/
if (parsedArgs.peerWait) {
try {
mSocket.close();
} catch (IOException ex) {
Log.e(TAG, "Zygote: error closing sockets", ex);
}
return true;
}
return false;
}
也就是创建失败会进入此函数,也就是处理zygote进程的函数,此函数会原路返回,通过socket的写入返还给systemserver最终到AMS,然后重新进入runSelectLoopMode。继续监听客户端的请求。
如果我们新创建进程成功了,会调用handleChildProc()。
代码如下
private void handleChildProc(Arguments parsedArgs,
FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller {
/*
* Close the socket, unless we're in "peer wait" mode, in which
* case it's used to track the liveness of this process.
*/ if (parsedArgs.peerWait) {
try {
ZygoteInit.setCloseOnExec(mSocket.getFileDescriptor(), true);
sPeerWaitSocket = mSocket;
} catch (IOException ex) {
Log.e(TAG, "Zygote Child: error setting peer wait "
+ "socket to be close-on-exec", ex);
}
} else {//关闭从Zygote进程复制过来的Socket连接
closeSocket();
ZygoteInit.closeServerSocket();
}
if (descriptors != null) {
try {
//为新创建的应用程序进程重新打开标准输入输出控制台
ZygoteInit.reopenStdio(descriptors[],descriptors[], descriptors[]);
for (FileDescriptor fd: descriptors) {
IoUtils.closeQuietly(fd);
}
newStderr = System.err;
} catch (IOException ex) {
Log.e(TAG, "Error reopening stdio", ex);
}
}
//设置新进程名称
if (parsedArgs.niceName != null) {
//设置新进程名称niceName是指systemServer
Process.setArgV0(parsedArgs.niceName);
}
//重新初始化Runtime
if (parsedArgs.runtimeInit) {
if (parsedArgs.invokeWith != null) {
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
pipeFd, parsedArgs.remainingArgs);
} else {
//为应用程序进程启动Binder线程池
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,parsedArgs.remainingArgs);
}
} else {
String className;
try {
//读取新进程执行的类名,在Process.start()函数中,传过来的类名为:"android.app.ActivityThread"
className = parsedArgs.remainingArgs[];
} catch (ArrayIndexOutOfBoundsException ex) {
logAndPrintError(newStderr,"Missing required class name argument", null);
return;
}
String[] mainArgs = new String[parsedArgs.remainingArgs.length - ];
System.arraycopy(parsedArgs.remainingArgs, ,mainArgs, , mainArgs.length);
if (parsedArgs.invokeWith != null) {
WrapperInit.execStandalone(parsedArgs.invokeWith,parsedArgs.classpath, className, mainArgs);
} else {
//获取类加载器
ClassLoader cloader;
if (parsedArgs.classpath != null) {
cloader = new PathClassLoader(parsedArgs.classpath,ClassLoader.getSystemClassLoader());
} else {
cloader = ClassLoader.getSystemClassLoader();
}
//加载并执行"android.app.ActivityThread"类
try {
ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
} catch (RuntimeException ex) {
logAndPrintError(newStderr, "Error starting.", ex);
}
}
}
}
由于应用程序启动参数中已经设置了"--runtime-init"标志位,因此新创建的应用程序进程将调用RuntimeInit.zygoteInit()函数来初始化运行时库,为应用程序启动Binder线程池,完成这些准备工作后,调用应用程序进程的入口函数ActivityThread.main()为应用程序进程创建消息循环。
接下来我们就看一下RuntimeInit.zygoteInit()
此函数在/frameworks/base/core/java/com/android/internal/os/RuntimeInit.java中
public static final void zygoteInit(int targetSdkVersion, String[] argv)
throws ZygoteInit.MethodAndArgsCaller {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
//重定向Log输出流
redirectLogStreams();
//初始化运行环境
commonInit();
//启动Binder线程池
nativeZygoteInit();
//调用程序入口函数
applicationInit(targetSdkVersion, argv);
}
接下来我们看一下applicationInit()
private static void applicationInit(int targetSdkVersion, String[] argv)
throws ZygoteInit.MethodAndArgsCaller {
// If the application calls System.exit(), terminate the process
// immediately without running any shutdown hooks. It is not possible to
// shutdown an Android application gracefully. Among other things, the
// Android runtime shutdown hooks close the Binder driver, which can cause
// leftover running threads to crash before the process actually exits.
nativeSetExitWithoutCleanup(true); // We want to be fairly aggressive about heap utilization, to avoid
// holding on to a lot of memory that isn't needed.
VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion); final Arguments args;
try {
args = new Arguments(argv);
} catch (IllegalArgumentException ex) {
Slog.e(TAG, ex.getMessage());
// let the process exit
return;
} // Remaining arguments are passed to the start class's static main
invokeStaticMain(args.startClass, args.startArgs);
}
在这里我们重点要看一下invokeStaticMain()
通过调用invokeStaticMain来调用args.startClass这个类的main()方法。
说明开启又一个新世界。
在之前提过zygote的socket的客户端可以是AMS,下一节我们分析如何开启一个Activity。
ZygoteInit 相关分析的更多相关文章
- 多视图学习利器----CCA(典型相关分析)及MATLAB实现
Hello,我是你们人见人爱花见花开的小花.又和大家见面了,今天我们来聊一聊多视图学习利器------CCA. 一 典型相关分析的基本思想 当我们研究两个变量x和y之间的相关关系的时候,相关系数(相关 ...
- SPSS数据分析—基于最优尺度变换的典型相关分析
传统的典型相关分析只能考虑变量之间的线性相关情况,且必须为连续变量,而我们依然可以使用最优尺度变换来拓展其应用范围,使其可以分析非线性相关.数据为分类数据等情况,并且不再仅限于两个变量间的分析, 虽然 ...
- SPSS数据分析—典型相关分析
我们已经知道,两个随机变量间的相关关系可以用简单相关系数表示,一个随机变量和多个随机变量的相关关系可以用复相关系数表示,而如果需要研究多个随机变量和多个随机变量间的相关关系,则需要使用典型相关分析. ...
- com.android.internal.os.ZygoteInit$MethodAndArgsCaller 解决
好久没写博客了,带着点小愧疚来,添上几个字: 这是今天遇到的一个bug,之前也遇到过,为了后面方便,就记下. bug提示:com.android.internal.os.ZygoteInit$Meth ...
- SPSS数据分析—相关分析
相关系数是衡量变量之间相关程度的度量,也是很多分析的中的当中环节,SPSS做相关分析比较简单,主要是区别如何使用这些相关系数,如果不想定量的分析相关性的话,直接观察散点图也可以. 相关系数有一些需要注 ...
- 中国各城市PM2.5数据间的相关分析
code{white-space: pre;} pre:not([class]) { background-color: white; }if (window.hljs && docu ...
- Zygote(app_process)相关分析2
在前一篇文章中已经分析了从init.c到Zygote(app_process)的启动流程. 今天开始分析frameworks/base/cmds/app_process/app_main.cpp. s ...
- Jordan Lecture Note-12: Kernel典型相关分析(Kernel Canonical Correlation Analysis, KCCA).
Kernel典型相关分析 (一)KCCA 同样,我们可以引入Kernel函数,通过非线性的坐标变换达到之前CCA所寻求的目标.首先,假设映射$\Phi_X: x\rightarrow \Phi_X(x ...
- Jordan Lecture Note-11: 典型相关分析(Canonical Correlation Analysis, CCA).
典型相关分析 (一)引入 典型相关分析(Canonical Correlation Analysis)是研究两组变量之间相关关系的一种多元统计方法.他能够揭示出两组变量之间的内在联系. 我们知道,在一 ...
随机推荐
- django中添加用户
在django中添加用户,直接在auth_user表中添加会有问题,因为这里密码是加密的,可以通过manage.py shell加入 创建User: 1 >>> from djang ...
- haproxy配置文件简单管理
版本:python3功能:对haproxy配置文件进行简单的查询.添加以及删除功能操作流程:1.根据提示选择相应的选项2.进入所选项后,根据提示写入相应的参数3.查询功能会返回查询结果,添加.删除以及 ...
- display:inline-block的空白bug问题
产生原因:我们写代码的时候习惯在结束标签的后面添加换行符,这个时候就会产生空白符.但是不同浏览器对空白符的理解是不同的,IE6/7会忽略掉此空白符,正常显示内容:IE8以上的IE浏览器以及FF.chr ...
- Linux下的多进程编程
1.进程 1.1进程的定义 <计算机操作系统>这门课对进程有这样的描述:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统 ...
- chkdsk 和sfc.exe修复命令
1:chkdsk:chkdsk的全称是checkdisk,就是磁盘检查. CMD->help chkdsk CHKDSK [volume[[path]filename]]] [/F] [/V] ...
- Apache虚拟主机配置(多个域名访问多个目录)
Apache虚拟主机配置(多个域名访问多个目录) 为了方便管理虚拟主机,我决定使用一种方法,那就是修改httpd-vhosts.conf文件. 第一步首先要使扩展文件httpd-vhosts.conf ...
- CentOS6.5_python2.7.3下virt-manager无法启动
配置virt-manager: 1.安装virt-manager, libvirt, qemu-kvm 2.配置libvirtd开机启动: chkconfig libvirtd on #取消开机启 ...
- iOS 支付宝支付集成获取私钥
http://doc.open.alipay.com/doc2/apiList?docType=4 登录到支付宝开放平台,下载相关支付宝支付的demo.解压出来有3个文件夹.(服务端demo,客户端 ...
- Crystal Report在.net中的两种显示方式
Crystal Report在.net中的两种显示方式 编写人:CC阿爸 2014-7-29 近来在完成深圳一公司的项目,对方对各方面要求相当严格,一不满意就拒绝签收,为了对修正水晶报表显示及导出的一 ...
- Bootstrap Alert Auto Close
http://stackoverflow.com/questions/23101966/bootstrap-alert-auto-close http://jsfiddle.net/mfX57/ $( ...