關於做者java
郭孝星,程序員,吉他手,主要從事Android平臺基礎架構方面的工做,歡迎交流技術方面的問題,能夠去個人Github提issue或者發郵件至guoxiaoxingse@163.com與我交流。android
文章目錄git
Android系統的啓動流程以下圖(點擊查看大圖)所示:程序員
Loader層github
Kernel層瀏覽器
Native層緩存
Framework層網絡
init進程接着fork生成Media Server進程,該進程負責啓動和管理整個C++ Framwork(包含AudioFlinger、Camera Service等服務)。架構
Zygote進程接着會fork生成System Server進程,該進程負責啓動和管理整個Java Framwork(包含ActivityManagerService、WindowManagerService等服務)。app
App層
Zygote進程孵化出的第一個應用進程是Launcher進程(桌面),它還會孵化出Browser進程(瀏覽器)、Phone進程(電話)等。咱們每一個建立的應用都是一個單獨的進程。
經過上述流程的分析,想必讀者已經對Android的整個進程模型有了大體的理解。做爲一個應用開發者咱們每每更爲關注Framework層和App層裏進程的建立與管理相關原理,咱們來 一一分析。
在正式介紹進程以前,咱們來思考一個問題,何爲進程,進程的本質是什麼?🤔
咱們知道,代碼是靜態的,有代碼和資源組成的系統要想運行起來就須要一種動態的存在,進程就是程序的動態執行過程。何爲進程? 進程就是處理執行狀態的代碼以及相關資源的集合,包括代碼端段、文件、信號、CPU狀態、內存地址空間等。
進程使用task_struct結構體來描述,以下所示:
關於進程的更多詳細信息,讀者能夠去翻閱Linux相關書籍,這裏只是給讀者帶來一種總體上的理解,咱們的重心仍是放在進程再Android平臺上的應用。
在文章開篇的時候,咱們提到了系統中運行的各類進程,那麼這些進程如何被建立呢?🤔
咱們先來看看咱們最熟悉的應用進程是如何被建立的,前面咱們已經說來每個應用都運行在一個單獨的進程裏,當ActivityManagerService去啓動四大組件時, 若是發現這個組件所在的進程沒有啓動,就會去建立一個新的進程,啓動進程的時機咱們在分析四大組件的啓動流程的時候也有講過,這裏再總結一下:
這個新進程就是zygote進程經過複製自身來建立的,新進程在啓動的過程當中還會建立一個Binder線程池(用來作進程通訊)和一個消息循環(用來作線程通訊) 整個流程以下圖所示:
注:整個流程會涉及Binder和Socket兩種進程通訊方式,這個咱們後續會有專門的文章單獨分析,這個就再也不展開。
整個流程大體就是這樣,咱們接着來看看具體的代碼實現,先來看一張進程啓動序列圖:
從第一步到第三步主要是收集整理uid、gid、groups、target-sdk、nice-name等一系列的參數,爲後續啓動新進程作準備。而後調用openZygoteSocketIfNeeded()方法 打開Socket通訊,向zygote進程發出建立新進程的請求。
注:第二步中的Process.start()方法是個阻塞操做,它會一直等待進程建立完畢,並返回pid纔會完成該方法。
咱們來重點關注幾個關鍵的函數。
關於Process類與Zygote進程的通訊是如何進行的呢?🤔
Process的靜態內部類ZygoteState有個成員變量LocalSocket對象,它會與ZygoteInit類的成員變量LocalServerSocket對象創建鏈接,以下所示:
客戶端
public static class ZygoteState {
final LocalSocket socket;
}
複製代碼
服務端
public class ZygoteInit {
//該Socket與/dev/socket/zygote文件綁定在一塊兒
private static LocalServerSocket sServerSocket;
}
複製代碼
咱們來具體看看代碼裏的實現。
public static class ZygoteState {
public static ZygoteState connect(String socketAddress) throws IOException {
DataInputStream zygoteInputStream = null;
BufferedWriter zygoteWriter = null;
//建立LocalSocket對象
final LocalSocket zygoteSocket = new LocalSocket();
try {
//將LocalSocket與LocalServerSocket創建鏈接,創建鏈接的過程就是
//LocalSocket對象在/dev/socket目錄下查找一個名稱爲"zygote"的文件
//而後將本身與其綁定起來,這樣就創建了鏈接。
zygoteSocket.connect(new LocalSocketAddress(socketAddress,
LocalSocketAddress.Namespace.RESERVED));
//建立LocalSocket的輸入流,以即可以接收Zygote進程發送過來的數據
zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
//建立LocalSocket的輸出流,以即可以向Zygote進程發送數據。
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 opened, supported ABIS: " + abiListString);
return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
Arrays.asList(abiListString.split(",")));
}
}
複製代碼
創建Socket鏈接的流程很明朗了,以下所示:
ZygoteInit是Zygote進程的啓動類,該類會預加載一些類,而後便開啓一個循環,等待經過Socket發過來的建立新進程的命令,fork出新的 子進程。
ZygoteInit的入口函數就是main()方法,以下所示:
public class ZygoteInit {
public static void main(String argv[]) {
// Mark zygote start. This ensures that thread creation will throw
// an error.
ZygoteHooks.startZygoteNoThreadCreation();
try {
//...
registerZygoteSocket(socketName);
//...
//開啓循環
runSelectLoop(abiList);
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
caller.run();
} catch (Throwable ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
}
}
// 開啓一個選擇循環,接收經過Socket發過來的命令,建立新線程
private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
//sServerSocket指的是Socket通訊的服務端,在fds中的索引爲0
fds.add(sServerSocket.getFileDescriptor());
peers.add(null);
//開啓循環
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;
}
try {
//處理輪詢狀態,當pollFds有時間到來時則往下執行,不然阻塞在這裏。
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
for (int i = pollFds.length - 1; i >= 0; --i) {
//採用IO多路複用機制,當接收到客戶端發出的鏈接請求時或者數據處理請求到來時則
//往下執行,不然進入continue跳出本次循環。
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
//索引爲0,即爲sServerSocket,表示接收到客戶端發來的鏈接請求。
if (i == 0) {
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
}
//索引不爲0,表示經過Socket接收來自對端的數據,並執行相應的操做。
else {
boolean done = peers.get(i).runOnce();
//處理完成後移除相應的文件描述符。
if (done) {
peers.remove(i);
fds.remove(i);
}
}
}
}
}
}
複製代碼
能夠發現ZygoteInit在其入口函數main()方法裏調用runSelectLoop()開啓了循環,接收Socket發來的請求。請求分爲兩種:
沒有鏈接請求時Zygote進程會進入休眠狀態,當有鏈接請求到來時,Zygote進程會被喚醒,調用acceptCommadPeer()方法建立Socket通道ZygoteConnection
private static ZygoteConnection acceptCommandPeer(String abiList) {
try {
return new ZygoteConnection(sServerSocket.accept(), abiList);
} catch (IOException ex) {
throw new RuntimeException(
"IOException during accept()", ex);
}
}
複製代碼
而後調用runOnce()方法讀取鏈接請求裏的數據,而後建立新進程。
此外,鏈接的過程當中服務端接受的到客戶端的connect()操做會執行accpet()操做,創建鏈接手,客戶端經過write()寫數據,服務端經過read()讀數據。
class ZygoteConnection {
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;
try {
//讀取客戶端發過來的參數列表
args = readArgumentList();
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
Log.w(TAG, "IOException on command socket " + ex.getMessage());
closeSocket();
return true;
}
//... 參數處理
try {
//... 參數處理
//調用Zygote.forkAndSpecialize(來fork出新進程
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
parsedArgs.appDataDir);
} catch (ErrnoException ex) {
logAndPrintError(newStderr, "Exception creating pipe", ex);
} catch (IllegalArgumentException ex) {
logAndPrintError(newStderr, "Invalid zygote arguments", ex);
} catch (ZygoteSecurityException ex) {
logAndPrintError(newStderr,
"Zygote security policy prevents request: ", ex);
}
try {
//pid == 0時表示當前是在新建立的子進程重磅執行
if (pid == 0) {
// in child
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
// should never get here, the child is expected to either
// throw ZygoteInit.MethodAndArgsCaller or exec().
return true;
}
// pid < 0表示建立新進程失敗,pid > 0 表示當前是在父進程中執行
else {
// in parent...pid of < 0 means failure
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}
}
複製代碼
該方法主要用來讀取進程啓動參數,而後調用Zygote.forkAndSpecialize()方法fork出新進程,該方法是建立新進程的核心方法,它主要會陸續調用三個 方法來完成工做:
上面的方法都完成會後,新進程會建立完成,並返回pid,接着就調用handleChildProc()來啓動新進程。handleChildProc()方法會接着調用RuntimeInit.zygoteInit()來 完成新進程的啓動。
這個就是個關鍵的方法了,它主要用來建立一些運行時環境,咱們來看一看。
public class RuntimeInit {
public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) throws ZygoteInit.MethodAndArgsCaller {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");
redirectLogStreams();
//建立應用進程的時區和鍵盤等通用信息
commonInit();
//在應用進程中建立一個Binder線程池
nativeZygoteInit();
//建立應用信息
applicationInit(targetSdkVersion, argv, classLoader);
}
}
複製代碼
該方法主要完成三件事:
Binder線程池咱們後續的文章會分析,咱們重點來看看applicationInit(targetSdkVersion, argv, classLoader)方法的實現,它主要用來完成應用的建立。
該方法裏的argv參數指的就是ActivityThread,該方法會調用invokeStaticMain()經過反射的方式調用ActivityThread類的main()方法。以下所示:
public class RuntimeInit {
private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) throws ZygoteInit.MethodAndArgsCaller {
//...
// Remaining arguments are passed to the start class's static main
invokeStaticMain(args.startClass, args.startArgs, classLoader);
}
private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader) throws ZygoteInit.MethodAndArgsCaller {
Class<?> cl;
//經過反射調用ActivityThread類的main()方法
try {
cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
ex);
}
Method m;
try {
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
throw new RuntimeException(
"Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException(
"Problem getting static main on " + className, ex);
}
//...
}
}
複製代碼
走到ActivityThread類的main()方法,咱們就很熟悉了,咱們知道在main()方法裏,會建立主線程Looper,並開啓消息循環,以下所示:
public final class ActivityThread {
public static void main(String[] args) {
//...
Environment.initForCurrentUser();
//...
Process.setArgV0("<pre-initialized>");
//建立主線程looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
//attach到系統進程
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
//主線程進入循環狀態
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
複製代碼
前面咱們從Process.start()開始講起,分析了應用進程的建立及啓動流程,既然有啓動就會有結束,接下來咱們從 Process.killProcess()開始講起,繼續分析進程的結束流程。
進程按照優先級大小不一樣又能夠分爲實時進程與普通進程。
prio值越小表示進程優先級越高,
進程的調度在Process類裏完成。
優先級調度方法
setThreadPriority(int tid, int priority)
複製代碼
進程的優先級以及對應的nice值以下所示:
進程組優先級調度方法
setProcessGroup(int pid, int group)
setThreadGroup(int tid, int group)
複製代碼
組優先級及對應取值
調度策略設置方法
setThreadScheduler(int tid, int policy, int priority)
複製代碼
另外除了這些基本的調度策略,Android系統還定義了兩個和進程相關的狀態值,一個就是定義在ProcessList.java裏的adj值,另外一個 是定義在ActivityManager.java裏的procState值。
定義在ProcessList.java文件,oom_adj劃分爲16級,從-17到16之間取值。
更新進程adj值的方法定義在ActivityManagerService中,分別爲:
那麼進程的adj值何時會被更新呢?🤔
Activity
Service
BroadcastReceiver
ContentProvider
另外,Lowmemorykiller也會根據當前的內存狀況逐級進行進程釋放,一共有六個級別(上面加粗的部分):
定義在ActivityManager.java文件,process_state劃分18類,從-1到16之間取值
根據上面說描述的adj值和state值,咱們又能夠按照重要性程度的不一樣,將進程劃分爲五級:
前臺進程
用戶當前操做所必需的進程。若是一個進程知足如下任一條件,即視爲前臺進程:
一般,在任意給定時間前臺進程都爲數很少。只有在內存不足以支持它們同時繼續運行這一萬不得已的狀況下,系統纔會終止它們。 此時,設備每每已達到內存分頁狀態,所以須要終止一些前臺進程來確保用戶界面正常響應。
可見進程
沒有任何前臺組件、但仍會影響用戶在屏幕上所見內容的進程。 若是一個進程知足如下任一條件,即視爲可見進程:
可見進程被視爲是極其重要的進程,除非爲了維持全部前臺進程同時運行而必須終止,不然系統不會終止這些進程。
服務進程
正在運行已使用 startService() 方法啓動的服務且不屬於上述兩個更高類別進程的進程。儘管服務進程與用戶所見內容沒有直接關聯,可是它們一般在執行一些用戶關 心的操做(例如,在後臺播放音樂或從網絡下載數據)。所以,除非內存不足以維持全部前臺進程和可見進程同時運行,不然系統會讓服務進程保持運行狀態。
後臺進程
包含目前對用戶不可見的 Activity 的進程(已調用 Activity 的 onStop() 方法)。這些進程對用戶體驗沒有直接影響,系統可能隨時終止它們,以回收內存供前臺進程、可見進程或服務進程使用。 一般會有不少後臺進程在運行,所以它們會保存在 LRU (最近最少使用)列表中,以確保包含用戶最近查看的 Activity 的進程最後一個被終止。若是某個 Activity 正確實現了生命週期方法,並保存了其當前狀態,則終止其進程不會對用戶體驗產生明顯影響,由於當用戶導航回該 Activity 時,Activity 會恢復其全部可見狀態。
空進程
不含任何活動應用組件的進程。保留這種進程的的惟一目的是用做緩存,以縮短下次在其中運行組件所需的啓動時間。 爲使整體系統資源在進程緩存和底層內核緩存之間保持平衡,系統每每會終止這些進程。