概述
Zygote,翻譯為中文是“受精卵”,它是Android系統(tǒng)中,所有Java進(jìn)程的父進(jìn)程,負(fù)責(zé)創(chuàng)建新進(jìn)程。System進(jìn)程,是Android系統(tǒng)中系統(tǒng)服務(wù)運(yùn)行所在的進(jìn)程,它負(fù)責(zé)創(chuàng)建、管理所有的系統(tǒng)服務(wù),包括AMS、PackageManagerServer等等十分重要的服務(wù),都運(yùn)行在System進(jìn)程中。
Zygote和System進(jìn)程的生命周期伴隨整個(gè)系統(tǒng),在系統(tǒng)啟動(dòng)時(shí)就會(huì)啟動(dòng)這兩個(gè)進(jìn)程,而直到系統(tǒng)關(guān)閉,它們才會(huì)被殺死。
值得一提的是,在Android5.0之后,Zygote進(jìn)程在系統(tǒng)中一共存在兩個(gè),這主要是為了適應(yīng)新增加的64位app而設(shè)計(jì)的,它們兩個(gè)的主要功能其實(shí)是一致的。
And,System進(jìn)程其實(shí)也是Zygote進(jìn)程的子進(jìn)程,它們啟動(dòng)的順序大概可以描述為,Linux系統(tǒng)的初始化進(jìn)程<init>
通過讀取腳本,創(chuàng)建Zygote,Zygote創(chuàng)建完成之后,先啟動(dòng)了System進(jìn)程,然后自己再等待其他創(chuàng)建請求。
本文以API28的源碼為基礎(chǔ)進(jìn)行分析。
Zygote啟動(dòng)過程
總述
- init進(jìn)程加載腳本,啟動(dòng)app_process文件中的main函數(shù)
- 創(chuàng)建虛擬機(jī)實(shí)例,進(jìn)入ZygoteInit類的main方法中
- 創(chuàng)建一個(gè)Socket
- fork出System進(jìn)程
- 自己進(jìn)入死循環(huán)中,不斷從第3步創(chuàng)建的Socket中獲取是否有創(chuàng)建進(jìn)程的請求,并進(jìn)行處理。
Step1: app_main.main
frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[])
{
...
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
...
bool zygote = false;
bool startSystemServer = false;
bool application = false;
String8 niceName;
String8 className;
while (i < argc) {
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = ZYGOTE_NICE_NAME;
} else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = true;
} else if (strcmp(arg, "--application") == 0) {
application = true;
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
niceName.setTo(arg + 12);
} else if (strncmp(arg, "--", 2) != 0) {
className.setTo(arg);
break;
} else {
--i;
break;
}
}
...
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
}
...
}
走這個(gè)方法的目的是解析啟動(dòng)腳本中傳進(jìn)來的參數(shù)并真正調(diào)用方法啟動(dòng)進(jìn)程。
- 創(chuàng)建一個(gè)AppRuntime對象,是AndroidRuntime的子類,它的定義就在app_main文件中。
- 做一些解析工作,解析從啟動(dòng)腳本傳過來的參數(shù),這里要啟動(dòng)的是zygote進(jìn)程,所以zygote變量為true。
- 調(diào)用runtime的start方法,runtime就是第一步創(chuàng)建的AppRuntime對象,但是它沒有重寫start方法,start方法是在它的父類AndroidRuntime中的。注意:傳入的參數(shù)是ZygoteInit類的全名以及要傳給這個(gè)類的參數(shù),以及最后一個(gè)判斷啟動(dòng)的是否是zygote進(jìn)程的bool值。
Step2: AndroidRuntime.start
frameworks/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
...
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
。。。
char* slashClassName = toSlashClassName(className != NULL ? className : "");
jclass startClass = env->FindClass(slashClassName);
...
strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
...
jmethodID startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V");
...
env->CallStaticVoidMethod(startClass, startMeth, strArray);
...
}
走這個(gè)方法的目的是從c++的世界轉(zhuǎn)入Java的世界。
- 初始化JNI
- 創(chuàng)建并開啟一個(gè)虛擬機(jī)實(shí)例
- 給這個(gè)虛擬機(jī)注冊JNI方法
- 各種解析之后,調(diào)用
env->CallStaticVoidMethod
方法,從而調(diào)起Java的方法,傳入的三個(gè)參數(shù)分別是要調(diào)用的Java類、要調(diào)用的方法、傳給方法的參數(shù),這里要調(diào)用的Java類正是從上一步傳進(jìn)來ZygoteInit類的全稱解析出來的,而startMeth
參數(shù)是main方法,所以這里調(diào)用起來的是ZygoteInit的main方法。
**宣布進(jìn)入Java的世界!**
Step3: ZygoteInit.main
public static void main(String argv[]) {
ZygoteServer zygoteServer = new ZygoteServer();
...
boolean startSystemServer = false;
String socketName = "zygote";
String abiList = null;
boolean enableLazyPreload = false;
for (int i = 1; i < argv.length; i++) {
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;
} else if ("--enable-lazy-preload".equals(argv[i])) {
enableLazyPreload = true;
} else if (argv[i].startsWith(ABI_LIST_ARG)) {
abiList = argv[i].substring(ABI_LIST_ARG.length());
} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
socketName = argv[i].substring(SOCKET_NAME_ARG.length());
} else {
throw new RuntimeException("Unknown command line argument: " + argv[i]);
}
}
...
zygoteServer.registerServerSocketFromEnv(socketName);
...
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
// {@code r == null} in the parent (zygote) process, and {@code r != null} in the
// child (system_server) process.
if (r != null) {
r.run();
return;
}
}
...
caller = zygoteServer.runSelectLoop(abiList);
...
if (caller != null) {
caller.run();
}
}
- 創(chuàng)建ZygoteServer,然后做各種標(biāo)記位的判斷,比如
startSystemServer
判斷是否要啟動(dòng)System進(jìn)程,這里為true。
- 調(diào)用
zygoteServer.registerServerSocketFromEnv
(詳見Step3.1),創(chuàng)建一個(gè)Server端Socket,用來等待AMS請求,以創(chuàng)建應(yīng)用程序進(jìn)程。但這時(shí)候只是創(chuàng)建,還沒開始等待。
-
forkSystemServer
(詳見Step3.2) 創(chuàng)建一個(gè)新的子進(jìn)程,這個(gè)新的子進(jìn)程就是System進(jìn)程,這里討論的是Zygote的創(chuàng)建,這個(gè)方法會(huì)返回null,具體原因見Step3.2
- 調(diào)用
runSelectLoop
方法,在這個(gè)方法中無限等待請求。
Step3.1: ZygoteServer.registerServerSocketFromEnv
void registerServerSocketFromEnv(String socketName) {
if (mServerSocket == null) {
int fileDesc;
...
String env = System.getenv(fullSocketName);
fileDesc = Integer.parseInt(env);
...
FileDescriptor fd = new FileDescriptor();
...
mServerSocket = new LocalServerSocket(fd);
...
}
}
這是Step3的第2步,創(chuàng)建并注冊Socket。
通過socket的名字創(chuàng)建文件操作符,然后再通過文件操作符創(chuàng)建一個(gè)socket,其實(shí)這個(gè)socket在操作系統(tǒng)中的表現(xiàn)形式就是一個(gè)文件(這是關(guān)于Linux系統(tǒng)的知識(shí),這里不詳述)這個(gè)socket就存在成員變量mServerSocket
中。
Step3.2: ZygoteInit.forkSystemServer
private static Runnable forkSystemServer(String abiList, String socketName,
ZygoteServer zygoteServer) {
...
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1024,1032,1065,3001,3002,3003,3006,3007,3009,3010",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
"--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
"com.android.server.SystemServer",
};
...
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,
parsedArgs.runtimeFlags,
null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
...
/* For child process */
if (pid == 0) {
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
zygoteServer.closeServerSocket();
return handleSystemServerProcess(parsedArgs);
}
return null;
}
這是Step3的第2步,它的目的是創(chuàng)建System進(jìn)程
- 首先是置頂了一些參數(shù),這些參數(shù)主要是針對即將創(chuàng)建的子進(jìn)程,即System進(jìn)程的,可以看到這里為System進(jìn)程設(shè)定了其UID、GID等等信息,而且指定了它的執(zhí)行類是
com.android.server.SystemServe
r。
- 調(diào)用
Zygote.forkSystemServer
方法,這個(gè)方法會(huì)調(diào)用操作系統(tǒng)提供的fork方法,fork方法是Linux系統(tǒng)中創(chuàng)建一個(gè)新進(jìn)程的方式,是一個(gè)非常特殊的方法,父進(jìn)程執(zhí)行fork方法之后,會(huì)出現(xiàn)兩個(gè)幾乎完全一樣的進(jìn)程,即原先的父進(jìn)程和新建的子進(jìn)程,同時(shí)返回fork方法,而且繼續(xù)執(zhí)行的代碼是一致的,但是不同的是,fork的返回值不相同。在父進(jìn)程中,會(huì)返回子進(jìn)程的pid(ProcessID),而子進(jìn)程中會(huì)返回0。
- 然后看接下來的代碼,if語句判斷pid是否為0,所以if語句塊中的代碼是子進(jìn)程會(huì)執(zhí)行的,而對于父進(jìn)程,pid不為零,所以下面會(huì)返回null,這也就解釋了Step3中為什么是返回null。讀者看到這里可以重新返回去看看step3就會(huì)明白。(這種寫法是非常典型的fork子進(jìn)程之后的寫法)
- 這里重新理一理Step3最后的步驟,在父進(jìn)程,也就是Zygote進(jìn)程中,
forkSystemServer
方法返回了null,于是會(huì)執(zhí)行runSelectLoop
方法,而子進(jìn)程,也就是System進(jìn)程,返回的是一個(gè)runnable,是由forkSystemServer
方法中調(diào)用的handleSystemServerProcess
方法返回的,它嵌套返回到main方法中后,會(huì)執(zhí)行run方法,然后main方法就返回掉了,因?yàn)檫@是System進(jìn)程了。
Step3.3: ZygoteServer.runSelectLoop
Runnable runSelectLoop(String abiList) {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
fds.add(mServerSocket.getFileDescriptor());
...
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;
}
...
Os.poll(pollFds, -1);
...
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 {
try {
ZygoteConnection connection = peers.get(i);
final Runnable command = connection.processOneCommand(this);
if (mIsForkChild) {
// We're in the child. We should always have a command to run at this
// stage if processOneCommand hasn't called "exec".
if (command == null) {
throw new IllegalStateException("command == null");
}
return command;
} else {
// We're in the server - we should never have any commands to run.
if (command != null) {
throw new IllegalStateException("command != null");
}
// We don't know whether the remote side of the socket was closed or
// not until we attempt to read from it from processOneCommand. This shows up as
// a regular POLLIN event in our regular processing loop.
if (connection.isClosedByPeer()) {
connection.closeSocket();
peers.remove(i);
fds.remove(i);
}
}
}
...
}
}
}
}
前面說到這里是Zygote進(jìn)程死循環(huán)等待的地方,是Zygote進(jìn)程fork出System進(jìn)程之后,自己等待別的請求的地方。
- 首先聲明了兩個(gè)數(shù)組,一個(gè)是文件描述符數(shù)組,第一個(gè)元素放的就是
mServerSocket
。另一個(gè)數(shù)組是ZygoteConnection
數(shù)組,從類名就能猜出(當(dāng)然事實(shí)也確實(shí)是這樣滴)這個(gè)類描述的是一個(gè)和Zygote進(jìn)程的連接,也就是說當(dāng)Zygote接收到請求之后,會(huì)將它封裝成一個(gè)ZygoteConnection對象。
- 開個(gè)死循環(huán),調(diào)用方法
Os.poll
,處理輪詢狀態(tài)。poll也是Linux提供的系統(tǒng)調(diào)用,用于獲知指定的Socket是否有事件到達(dá)。可以看到這里創(chuàng)建了一個(gè)StructPollfd
數(shù)組,StructPollfd類是專門與Poll調(diào)用配合的一個(gè)類,用來描述poll調(diào)用的目的和結(jié)果,它既包含了一個(gè)描述poll監(jiān)聽目的的成員變量,也包含了一個(gè)描述已發(fā)生事件的成員變量。Poll調(diào)用根據(jù)傳入的StructPollfd,指定監(jiān)聽的socket,指定監(jiān)聽的事件類型,如果沒有事件到達(dá),會(huì)阻塞在poll方法中,如果有事件到達(dá),則將發(fā)生的事件寫入StructPollfd對象中,然后返回。
- 可以看到,一開始裝入的
pollfd
是mServerSocket
,然后進(jìn)入poll調(diào)用,一旦有事件到達(dá),poll
方法將跳出,進(jìn)入下面的for循環(huán),這時(shí)如果i=0,就是AMS請求與Zygote連接(注意這里并非請求創(chuàng)建進(jìn)程),AMS一般在第一次請求創(chuàng)建進(jìn)程時(shí)會(huì)先請求連接,之后如果沒有關(guān)閉這個(gè)鏈接,則再請求創(chuàng)建之時(shí)不用請求連接。Zygote接收到連接請求,則將請求包裝后,放入peers中,然后又回到Poll了。注意這里的連接,即ZygoteConnection
對象,描述的是和AMS的連接,并不會(huì)包括AMS請求創(chuàng)建進(jìn)程所傳遞的任何參數(shù)。
- 等到AMS再次請求創(chuàng)建時(shí),取出peers中對應(yīng)的連接,處理、調(diào)用
processOneCommand
方法。這個(gè)地方Poll的作用就是在于監(jiān)聽到socket有數(shù)據(jù)傳入了,這些數(shù)據(jù)才是AMS請求創(chuàng)建進(jìn)程所傳遞的數(shù)據(jù),而讀出這些數(shù)據(jù)的地方就在processOneCommand
方法中。
- 往下又是一個(gè)非常典型的fork之后的寫法,如果是子進(jìn)程,就把processOneCommand的結(jié)果,即一個(gè)Runnable返回出去,在前面提到的ZygoteInit的main方法中會(huì)調(diào)用它run。父進(jìn)程會(huì)判斷是否和AMS已經(jīng)斷開了,通過方法
connection.isClosedByPeer()
判斷的。如果已經(jīng)斷開,則把連接刪除掉,如果沒有斷開,就繼續(xù)在poll等待AMS請求創(chuàng)建進(jìn)程。
Step3.4:ZygoteConnection.processOneCommand
Runnable processOneCommand(ZygoteServer zygoteServer) {
...
//從socket中讀出要?jiǎng)?chuàng)建的進(jìn)程的參數(shù)
args = readArgumentList();
...
parsedArgs = new Arguments(args);
...
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal,parsedArgs.seInfo, parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote, parsedArgs.instructionSet, parsedArgs.appDataDir);
...
if (pid == 0) {
// 子進(jìn)程
zygoteServer.setForkChild();
// 子進(jìn)程中要關(guān)掉ServerSocket
zygoteServer.closeServerSocket();
...
return handleChildProc(parsedArgs, descriptors, childPipeFd, parsedArgs.startChildZygote);
} else {
// 父進(jìn)程,即Zygote
handleParentProc(pid, descriptors, serverPipeFd);
return null;
}
...
}
這個(gè)方法要處理創(chuàng)建進(jìn)程的請求,在這里會(huì)fork出子進(jìn)程了。
- 調(diào)用
readArgumentList
方法,從mServerSocket
中讀出AMS放進(jìn)去的請求參數(shù),并封裝一下
- 調(diào)用
Zygote.forkAndSpecialize
方法fork出子進(jìn)程
- 父進(jìn)程和子進(jìn)程分開處理。這個(gè)地方涉及到的主要是應(yīng)用程序進(jìn)程的啟動(dòng)過程,這里不詳述。
到這里Zygote就啟動(dòng)完成啦,在死循環(huán)里面等著。
System啟動(dòng)
Step1: ZygoteInit.handleSystemServerProcess
private static Runnable handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs) {
...
if (parsedArgs.invokeWith != null) {
...
} else {
ClassLoader cl = null;
if (systemServerClasspath != null) {
cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion);
Thread.currentThread().setContextClassLoader(cl);
}
/*
* Pass the remaining arguments to SystemServer.
*/
return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
}
...
}
- 這個(gè)方法在ZygoteInit里調(diào)用的,前面有說到。而且它的調(diào)用順序是在Zygote的死循環(huán)開始之前的。
- 它的參數(shù)是前面設(shè)置好的一個(gè)參數(shù),而且是指向子進(jìn)程的,也就是system進(jìn)程。其中
invokeWith
是沒有設(shè)置的,應(yīng)該是為了檢測進(jìn)程內(nèi)存泄露等等Debug用途時(shí)才會(huì)有值,這一點(diǎn)如果有大大知道,望指點(diǎn)一二。
- 接下來調(diào)用
ZygoteInit.zygoteInit
Step2: ZygoteInit.zygoteInit
public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
...
ZygoteInit.nativeZygoteInit();
return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
...
}
-
nativeZygoteInit
在System進(jìn)程中啟動(dòng)了一個(gè)Binder線程池。這里不詳述,關(guān)于Binder的知識(shí)后面會(huì)寫文章。
- 調(diào)用了
RuntimeInit.applicationInit
Step3: RuntimeInit.applicationInit
protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
ClassLoader classLoader) {
...
return findStaticMain(args.startClass, args.startArgs, classLoader);
}
繼續(xù)調(diào)用findStaticMain
Step4: RuntimeInit.findStaticMain
protected static Runnable findStaticMain(String className, String[] argv,
ClassLoader classLoader) {
Class<?> cl;
...
cl = Class.forName(className, true, classLoader);
...
Method m;
...
m = cl.getMethod("main", new Class[] { String[].class });
...
return new MethodAndArgsCaller(m, argv);
}
這里調(diào)用了className指向的類的main方法,也就是SystemServer
類的main方法。包裝了一下返回了一個(gè)MethodAndArgsCaller
對象,這個(gè)類實(shí)現(xiàn)了Runnable接口,于是這個(gè)對象會(huì)一直返回一直返回到ZygoteInit方法中。
值得注意的是,這里返回到ZygoteInit.main
方法中的進(jìn)程,不是前面的Zygote進(jìn)程,而是System進(jìn)程,是由Zygote進(jìn)程fork出來的。我們重新看看main方法中的那一段代碼。
Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
// {@code r == null} in the parent (zygote) process, and {@code r != null} in the
// child (system_server) process.
if (r != null) {
r.run();
return;
}
返回出來的對象就是r,然后調(diào)用了r的run方法,然后把ZygoteInit的main方法返回掉,于是接下來就是SystemServer.main
。
SystemServer.main
new SystemServer().run();
就調(diào)用了這么個(gè)方法
SystemServer.run
private void run() {
try {
...
String timezoneProperty = SystemProperties.get("persist.sys.timezone");
if (timezoneProperty == null || timezoneProperty.isEmpty()) {
Slog.w(TAG, "Timezone not set; setting to GMT.");
SystemProperties.set("persist.sys.timezone", "GMT");
}
...
if (!SystemProperties.get("persist.sys.language").isEmpty()) {
final String languageTag = Locale.getDefault().toLanguageTag();
SystemProperties.set("persist.sys.locale", languageTag);
SystemProperties.set("persist.sys.language", "");
SystemProperties.set("persist.sys.country", "");
SystemProperties.set("persist.sys.localevar", "");
}
...
Looper.prepareMainLooper();
...
// Initialize the system context.
createSystemContext();
// Create the system service manager.
mSystemServiceManager = new SystemServiceManager(mSystemContext
...
SystemServerInitThreadPool.get();
);
} finally {
traceEnd(); // InitBeforeStartServices
}
...
// Start services.
startBootstrapServices();
startCoreServices();
startOtherServices();
...
// Loop forever.
Looper.loop();
}
這里是真正運(yùn)行System進(jìn)程要初始化的工作的地方!!!
- 先設(shè)置了一些時(shí)區(qū)、語言等等
- 創(chuàng)建了一個(gè)消息循環(huán)Looper
- 創(chuàng)建系統(tǒng)上下文、創(chuàng)建SystemServiceManager,創(chuàng)建一個(gè)線程池用來維護(hù)系統(tǒng)服務(wù)。
- 調(diào)用各個(gè)方法啟動(dòng)各種系統(tǒng)服務(wù),關(guān)于系統(tǒng)服務(wù)。
startBootstrapServices
:這里啟動(dòng)一些依賴性比較強(qiáng)的服務(wù)。如安裝器、設(shè)備標(biāo)識(shí)服務(wù)、AMS、PackageManagerServices、UserManagerService、電池管理服務(wù)、Recovery服務(wù)、亮度服務(wù)、傳感器等等等等。startCoreServices
:這里啟動(dòng)了UsageStatsService(用戶使用情況服務(wù))、WebViewUpdate服務(wù)startOtherServices
:一些雜七雜八的服務(wù),比如鬧鐘、藍(lán)牙、網(wǎng)絡(luò)(包括wifi)、媒體、存儲(chǔ)等等甚至是statusBar也是單開一個(gè)服務(wù)的,還有一個(gè)重要的是WindowymanagerServices,他在這里的原因是它要等到傳感器都初始化好之后,它才能啟動(dòng),這里順便一提,還給AMS設(shè)置了一個(gè)回調(diào)systemReady,告訴AMS可以運(yùn)行非系統(tǒng)的代碼了。以及NotificationManager也是在這里創(chuàng)建的。
- 啟動(dòng)Looper
到這里System進(jìn)程就啟動(dòng)完成啦。