一、task_struct
Android的進(jìn)程管理建立在Linux內(nèi)核的基礎(chǔ)上颁湖。Linux內(nèi)核通過一個被稱為進(jìn)程描述符的task_struct結(jié)構(gòu)體來管理進(jìn)程宣蠕,這個結(jié)構(gòu)體包含了一個進(jìn)程所需的所有信息。它定義在include/linux/sched.h文件中或者Android的kernel/msm-4.9/include/linux/sched.h中甥捺。包含一下的信息:
其中抢蚀,進(jìn)程的狀態(tài)有以下取值:
二、進(jìn)程創(chuàng)建流程
idle進(jìn)程 -> init進(jìn)程 -> zygote進(jìn)程 -> system_server進(jìn)程 →App進(jìn)程
他們之間的關(guān)系如下:
64位下有兩個zygote镰禾,zygote64和zygote皿曲。64位應(yīng)用的父進(jìn)程是zygote64,它的pgid也是zygote64的pid吴侦;32位應(yīng)用的父進(jìn)程是zygote屋休,它的pgid卻是zygote64的pid,如:com.ss.android.article.news的父進(jìn)程是zygote(1112)备韧,但它的pgid是zygote64(1111)劫樟,這是怎么回事呢?原來不管32位或64位的zygote织堂,它在創(chuàng)建完子進(jìn)程后叠艳,會調(diào)用setChildPgid()來改變子進(jìn)程的pgid捧挺。
多個進(jìn)程組還可以構(gòu)成一個會話 (session)翅睛,sid標(biāo)識會話id捕发,Android中進(jìn)程的sid基本都是0。
一張更為詳細(xì)的圖:
ZygoteServer啟動過程:
Zygote本身是一個Native的應(yīng)用程序法挨,剛開始的名字為“app_process”窃植,運(yùn)行過程中巷怜,通過調(diào)用setArgv0將名字改為Zygote延塑。
ZygoteInit進(jìn)程啟動后,會注冊一個Socket豫缨,在runSelectLoop方法中開啟一個while死循環(huán)等待ActivityManagerService創(chuàng)建新進(jìn)程的請求,其次冲呢,ZygoteInit啟動了SystemServer進(jìn)程敬拓,執(zhí)行SystemServer的main方法。
Socket通信框架:
LocalSocket就是作為客戶端建立于服務(wù)端的連接营勤,發(fā)送數(shù)據(jù)葛作。LocalServerSocket作為服務(wù)端使用,建立服務(wù)端的socket監(jiān)聽客戶端請求虱岂,典型的C/S架構(gòu)
三、如何創(chuàng)建一個進(jìn)程
#inlucde<unistd.h>
#inlucde<stdio.h>
#inlucde<wait.h>
int main(){
int count = 0;
pid_t fpid = fork();
if( fpid < 0){
printf("創(chuàng)建子進(jìn)程失敗");
} else if( fpid == 0){
printf("子進(jìn)程Id:%d\n",getpid());
} else {
printf("父進(jìn)程Id:%d\n",getpid());
}
printf("count=%d\n",count);
waitpid(fpid,NULL,0); // 暫時(shí)停止目前進(jìn)程的執(zhí)行难菌,直到有信號來到或子進(jìn)程結(jié)束
return 0;
}
- fork函數(shù)執(zhí)行一次,返回兩次猎塞,第一次返回父進(jìn)程的id荠耽,第二次返回子進(jìn)程的id铝量。
- count是全局變量慢叨,子進(jìn)程和父進(jìn)程同時(shí)操作拍谐,但是互相不受影響
fork()使用寫時(shí)復(fù)制轩拨,copy-on-write,是一種可以推遲甚至避免拷貝數(shù)據(jù)的技術(shù)砍濒。內(nèi)核此時(shí)并不復(fù)制整個進(jìn)程的地址空間梯影,而是讓父子進(jìn)程共享同一個地址空間甲棍。只用在需要寫入的時(shí)候才會復(fù)制地址空間七扰,從而使各個進(jìn)行擁有各自的地址空間颈走。如下圖所示,同左到右大的方向箭頭表示復(fù)制內(nèi)容:
為這四個部分分配物理塊锐膜,P2的:正文段-->PI的正文段的物理塊道盏,指的是不為P2分配正文段塊,讓P2的正文段指向P1的正文段塊种远,數(shù)據(jù)段-->P2自己的數(shù)據(jù)段塊(為其分配對應(yīng)的塊)院促,堆-->P2自己的堆塊,棧-->P2自己的棧塊辉浦。
一個進(jìn)程有正文段宪郊、數(shù)據(jù)段弛槐、堆和棧等段乎串,內(nèi)核只為新生成的子進(jìn)程創(chuàng)建虛擬空間結(jié)構(gòu)叹誉,它們來復(fù)制于父進(jìn)程的虛擬空間結(jié)構(gòu)钧唐,但是不為這些段分配物理內(nèi)存钝侠,它們共享父進(jìn)程的物理空間,當(dāng)父子進(jìn)程中有更改相應(yīng)段的行為發(fā)生時(shí)弱匪,再為子進(jìn)程相應(yīng)的段分配物理空間萧诫。
對應(yīng)到Android系統(tǒng)中:
4、Zygote進(jìn)程預(yù)加載資源
android系統(tǒng)資源加載分兩種方式及刻,預(yù)加載和使用進(jìn)程中加載。 預(yù)加載是指在zygote進(jìn)程啟動的時(shí)候就加載颗搂,這樣系統(tǒng)只在zygote執(zhí)行一次加載操作丢氢,所有APP用到該資源不需要再重新加載,減少資源加載時(shí)間貌嫡,加快了應(yīng)用啟動速度嫁艇,一般情況下步咪,系統(tǒng)中App共享的資源會被列為預(yù)加載資源。
預(yù)加載的內(nèi)容:
上面文件中列舉的四千多個類都要通過Class.forName加載到系統(tǒng)中,生成字節(jié)碼
preload的過程主要發(fā)生在ZygoteInit.preload()函數(shù)里面:
static void preload(TimingsTraceLog bootTimingsTraceLog) {
Log.d(TAG, "begin preload");
beginIcuCachePinning();
preloadClasses();
preloadResources();
nativePreloadAppProcessHALs();
preloadOpenGL();
preloadSharedLibraries();
preloadTextResources();
WebViewFactory.prepareWebViewInZygote();
endIcuCachePinning();
warmUpJcaProviders();
Log.d(TAG, "end preload");
sPreloadComplete = true;
}
會輸出以下log:
Zygote : begin preload
Zygote : Preloading ICU data...
Zygote : Preloading classes...
Zygote : ...preloaded 6558 classes in 441ms.
Zygote64Timing: PreloadClasses took to complete: 545ms
Zygote : Preloading resources...
Zygote : ...preloaded 86 resources in 45ms.
Zygote : ...preloaded 41 resources in 2ms.
Zygote : Preloading shared libraries...
Zygote : end preload
可見preload加載的有類,資源闰挡,共享庫长酗。系統(tǒng)中有大量的資源可以直接被App所使用,比如一個顏色咧叭,一個drawble,這些都是通過preloadResources加載的听想。
4衔峰、進(jìn)程優(yōu)先級
進(jìn)程管理主要涉及到兩個值:
adj:通過調(diào)整oom_score_adj來影響進(jìn)程壽命(Lowmemorykiller殺進(jìn)程策略)威彰;
schedGroup:影響進(jìn)程的CPU資源調(diào)度與分配歇盼;
adj,即進(jìn)程的優(yōu)先級粗略劃分如下:
前臺進(jìn)程:正在與用戶進(jìn)行交互的進(jìn)程
可見進(jìn)程:可在屏幕上顯示但不在前臺運(yùn)行,比如一個前臺進(jìn)程以對話框的形式顯示在該進(jìn)程前面氮惯。典型的如輸入法。
服務(wù)進(jìn)程:正在運(yùn)行已使用 startService() 方法啟動的服務(wù)且不屬于上述兩個更高類別進(jìn)程的進(jìn)程铛纬。盡管服務(wù)進(jìn)程與用戶所見內(nèi)容沒有直接關(guān)聯(lián),但是它們通常在執(zhí)行一些用戶關(guān)心的操作(例如唬滑,在后臺播放音樂或從網(wǎng)絡(luò)下載數(shù)據(jù))告唆。
后臺進(jìn)程:包含目前對用戶不可見的 Activity 的進(jìn)程(已調(diào)用 Activity 的 onStop() 方法)。這些進(jìn)程對用戶體驗(yàn)沒有直接影響晶密,系統(tǒng)可能隨時(shí)終止它們擒悬,以回收內(nèi)存供前臺進(jìn)程、可見進(jìn)程或服務(wù)進(jìn)程使用稻艰。
空進(jìn)程:不含任何活動應(yīng)用組件的進(jìn)程懂牧。保留這種進(jìn)程的的唯一目的是用作緩存,以縮短下次在其中運(yùn)行組件所需的啟動時(shí)間。
進(jìn)程的優(yōu)先級取決于進(jìn)程四大組件的運(yùn)行狀態(tài)尸变。例如Activity是否在前臺痛垛,用戶是否可見蹂析;Service正在被哪些客戶端使用;ContentProvider正在被哪些客戶端使用;BroadcastReceiver是否正在接受廣播
ProcessRecord中有以下成員變量:
// all activities running in the process
final ArrayList<ActivityRecord> activities = new ArrayList<>();
// any tasks this process had run root activities in
final ArrayList<TaskRecord> recentTasks = new ArrayList<>();
// all ServiceRecord running in this process
final ArraySet<ServiceRecord> services = new ArraySet<>();
// services that are currently executing code (need to remain foreground).
final ArraySet<ServiceRecord> executingServices = new ArraySet<>();
// All ConnectionRecord this process holds
final ArraySet<ConnectionRecord> connections = new ArraySet<>();
// all IIntentReceivers that are registered from this process.
final ArraySet<ReceiverList> receivers = new ArraySet<>();
// class (String) -> ContentProviderRecord
final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();
// All ContentProviderRecord process is using
final ArrayList<ContentProviderConnection> conProviders = new ArrayList<>();
activities 記錄了進(jìn)程中運(yùn)行的Activity
services,executingServices 記錄了進(jìn)程中運(yùn)行的Service
receivers 記錄了進(jìn)程中運(yùn)行的BroadcastReceiver
pubProviders 記錄了進(jìn)程中運(yùn)行的ContentProvider
而:
connections 記錄了對于Service連接
conProviders 記錄了對于ContentProvider的連接
連接的意義在于:連接的客戶端的進(jìn)程優(yōu)先級會影響被使用的Service和ContentProvider所在進(jìn)程的優(yōu)先級它匕。 例如:當(dāng)一個后臺的Service正在被一個前臺的Activity使用烧给,那么這個后臺的Service就需要設(shè)置一個較高的優(yōu)先級以便不會被回收榴鼎。(否則后臺Service進(jìn)程一旦被回收,便會對前臺的Activity造成影響心赶。)
ADJ的具體數(shù)值:
級別 | 常量名稱 | 簡述 |
---|---|---|
-1000 | NATIVE_ADJ | 并不被系統(tǒng)管理的native進(jìn)程的優(yōu)先級 |
-900 | SYSTEM_ADJ | 系統(tǒng)進(jìn)程 |
-800 | PERSISTENT_PROC_ADJ | 系統(tǒng)級常駐進(jìn)程咏闪,比如telephony |
-700 | PERSISTENT_SERVICE_ADJ | 關(guān)聯(lián)著系統(tǒng)進(jìn)程或者常駐進(jìn)程的進(jìn)程 |
0 | FOREGROUND_APP_ADJ | 前臺APP |
100 | VISIBLE_APP_ADJ | 可見APP |
200 | PERCEPTIBLE_APP_ADJ | 可感知的APP |
300 | BACKUP_APP_ADJ | 正在備份的進(jìn)程 |
400 | HEAVY_WEIGHT_APP_ADJ | 后臺重量級進(jìn)程,在system/rootdir.init.rc中配置 |
500 | SERVICE_ADJ | 正在運(yùn)行這service的進(jìn)程 |
600 | HOME_APP_ADJ | 桌面進(jìn)程 |
700 | PREVIOUS_APP_ADJ | 上一個APP的進(jìn)程(通過back返回) |
800 | SERVICE_B_ADJ | List B中的service進(jìn)程(區(qū)分于上一個 service list瓶籽,更老裤唠、使用到的可能性更胁着) |
900 | CACHED_APP_MIN_ADJ | 緩存進(jìn)程的最小值 |
906 | CACHED_APP_MAX_ADJ | 緩存進(jìn)程的最大值 |
以上各值數(shù)值越小椅贱,優(yōu)先級越高
BACKUP_APP_ADJ(300):執(zhí)行bindBackupAgent()過程的進(jìn)程
HEAVY_WEIGHT_APP_ADJ(400): realStartActivityLocked()過程舍悯,當(dāng)應(yīng)用的privateFlags標(biāo)識PRIVATE_FLAG_CANT_SAVE_STATE的進(jìn)程馁蒂;
進(jìn)程由SERVICE_ADJ(500)降低到SERVICE_B_ADJ(800),有以下兩種情況:
A類Service占比過高:當(dāng)A類Service個數(shù) > Service總數(shù)的1/3時(shí),則加入到B類Service。換句話說蝙云,B Service的個數(shù)至少是A Service的2倍。
內(nèi)存緊張&&A類Service占用內(nèi)存較高:當(dāng)系統(tǒng)內(nèi)存緊張級別(mLastMemoryLevel)高于ADJ_MEM_FACTOR_NORMAL垢揩,且該應(yīng)用所占內(nèi)存lastPss大于或等于CACHED_APP_MAX_ADJ級別所對應(yīng)的內(nèi)存閾值的1/3(默認(rèn)值閾值約等于110MB)悬包。
Android P開始垫释,進(jìn)一步細(xì)化VISIBLE_APP_ADJ級別:
從Android P開始,進(jìn)一步細(xì)化ADJ級別,增加了VISIBLE_APP_LAYER_ MAX(99)吁朦,是指VISIBLE_APP_ADJ(100)跟PERCEPTIBLE_APP_ADJ(200)之間有99個槽,則可見級別ADJ的取值范圍為[100,199]擂仍。 算法會根據(jù)其所在task的mLayerRank來調(diào)整其ADJ囤屹,100加上mLayerRank就等于目標(biāo)ADJ,layer越大逢渔,則ADJ越小肋坚。
如何查看進(jìn)程優(yōu)先級:
- cat proc/[pid]/oom_score_adj
- adb shell dumpsys activity o/p
5、進(jìn)程的回收
LMK基本原理
所有應(yīng)用進(jìn)程都是從zygote孵化出來的肃廓,記錄在AMS中mLruProcesses列表中智厌,由AMS進(jìn)行統(tǒng)一管理,AMS中會根據(jù)進(jìn)程的狀態(tài)更新進(jìn)程對應(yīng)的oom_adj值盲赊,這個值會通過文件傳遞到kernel中去铣鹏,kernel有個低內(nèi)存回收機(jī)制,在內(nèi)存達(dá)到一定閥值時(shí)會觸發(fā)清理oom_adj值高的進(jìn)程騰出更多的內(nèi)存空間哀蘑,這就是LowMemoryKiller工作原理诚卸。
LMK基本實(shí)現(xiàn)方案
根據(jù)不同手機(jī)的配置,就有對應(yīng)的殺進(jìn)程標(biāo)準(zhǔn)绘迁,這個標(biāo)準(zhǔn)用minfree和adj兩個文件來定義:
/sys/module/lowmemorykiller/parameters/minfree:里面是以","分割的一組數(shù)合溺,每個數(shù)字代表一個內(nèi)存級別。
/sys/module/lowmemorykiller/parameters/adj:對應(yīng)上面的一組數(shù)缀台,每個數(shù)組代表一個進(jìn)程優(yōu)先級級別
例如:
$ adb shell cat /sys/module/lowmemorykiller/parameters/minfree
18432,23040,27648,32256,85296,120640
$ adb shell cat /sys/module/lowmemorykiller/parameters/adj
0,100,200,300,900,906
Minfree是一個閾值棠赛,它跟內(nèi)存大小和分辨率有關(guān)系,通過ProcessList中updateOomLevels計(jì)算設(shè)置的,這里的adj并不是直接對應(yīng)ProcessList中定義的恭朗。
而是adj*17/1000=ProcessList文件里面的值
對于應(yīng)用進(jìn)程來說屏镊,也需要有自身的adj,由AMS負(fù)責(zé)更新痰腮。定義在oom_adj和oom_score_adj文件中:
/proc/pid/oom_adj:代表當(dāng)前進(jìn)程的優(yōu)先級而芥,這個優(yōu)先級是kernel中的優(yōu)先級。
/proc/pid/oom_score_adj:這個是AMS上層的優(yōu)先級膀值,與ProcessList中的優(yōu)先級對應(yīng)棍丐,例如:
LMK回收過程
用戶在啟動一個進(jìn)程之后,通常伴隨著啟動一個Activity游覽頁面或者一個Service播放音樂等等沧踏,這個時(shí)候此進(jìn)程的adj被AMS提高歌逢,LMK就不會殺死這個進(jìn)程,當(dāng)這個進(jìn)程要做的事情做完了翘狱,退出后臺了秘案,此進(jìn)程的adj很快又被AMS降低。
當(dāng)需要?dú)⑺酪粋€進(jìn)程釋放內(nèi)存時(shí)潦匈,一般先根據(jù)當(dāng)前手機(jī)剩余內(nèi)存的狀態(tài)阱高,在minfree節(jié)點(diǎn)中找到當(dāng)前等級,再根據(jù)這個等級去adj節(jié)點(diǎn)中找到這個等級應(yīng)該殺掉的進(jìn)程的優(yōu)先級茬缩, 之后遍歷所有進(jìn)程并比較進(jìn)程優(yōu)先級adj與優(yōu)先級閾值赤惊,并殺死優(yōu)先級低于閾值的進(jìn)程,達(dá)到釋放內(nèi)存的目的
AMS負(fù)責(zé)更新各應(yīng)用的進(jìn)程優(yōu)先級與閾值數(shù)組
lmkd負(fù)責(zé)接收AMS傳輸過來的數(shù)據(jù)然后寫入到sys與proc節(jié)點(diǎn)
lowmemorykiller則在內(nèi)存低于閾值時(shí)才會被觸發(fā)并負(fù)責(zé)殺死低優(yōu)先級的進(jìn)程凰锡。
AMS.updateConfiguration:更新窗口配置未舟,這個過程中,分別向/sys/module/lowmemorykiller/parameters目錄下的minfree和adj節(jié)點(diǎn)寫入相應(yīng)數(shù)值掂为;
AMS.applyOomAdjLocked:應(yīng)用adj裕膀,當(dāng)需要?dú)⒌裟繕?biāo)進(jìn)程則返回false;否則返回true勇哗,這個過程中魂角,調(diào)用setOomAdj(),向/proc/pid/oom_score_adj寫入oom_adj 后直接返回;
AMS.cleanUpApplicationRecordLocked & AMS.handleAppDiedLocked:進(jìn)程死亡后智绸,調(diào)用remove()野揪,直接返回;
時(shí)序圖:
AMS:
Lmkd.c:
LMK殺進(jìn)程過程:
入口是__init函數(shù), register_shrinker(&lowmem_shrinker);
關(guān)鍵點(diǎn)1:其實(shí)就是確定當(dāng)前低內(nèi)存對應(yīng)的閾值;
關(guān)鍵點(diǎn)2 :找到比該閾值優(yōu)先級低瞧栗,并且內(nèi)存占用較多的進(jìn)程(tasksize = get_mm_rss(p->mm)其實(shí)就是獲取內(nèi)存占用)斯稳。如何殺死的呢?很直接迹恐,通過Linux的中的信號量挣惰,發(fā)送SIGKILL信號直接將進(jìn)程殺死。
到這就分析完了LomemoryKiller內(nèi)核部分如何工作的。其實(shí)很簡單憎茂,一句話:被動掃描珍语,找到低優(yōu)先級的進(jìn)程,殺死竖幔。
onTrimMemory和onLowMemory
Activity, Service, ContentProvider和Application都實(shí)現(xiàn)了這個接口
應(yīng)用處于Runnig狀態(tài)可能收到的級別
TRIM_MEMORY_RUNNING_MODERATE 表示系統(tǒng)內(nèi)存已經(jīng)稍低
TRIM_MEMORY_RUNNING_LOW 表示系統(tǒng)內(nèi)存已經(jīng)相當(dāng)?shù)?br>
TRIM_MEMORY_RUNNING_CRITICAL 表示系統(tǒng)內(nèi)存已經(jīng)非常低板乙,你的應(yīng)用程序應(yīng)當(dāng)考慮釋放部分資源
應(yīng)用的可見性發(fā)生變化時(shí)收到的級別
TRIM_MEMORY_UI_HIDDEN 表示應(yīng)用已經(jīng)處于不可見狀態(tài),可以考慮釋放一些與顯示相關(guān)的資源
應(yīng)用處于后臺時(shí)可能收到的級別
TRIM_MEMORY_BACKGROUND 表示系統(tǒng)內(nèi)存稍低拳氢,你的應(yīng)用被殺的可能性不大募逞。但可以考慮適當(dāng)釋放資源
TRIM_MEMORY_MODERATE 表示系統(tǒng)內(nèi)存已經(jīng)較低,當(dāng)內(nèi)存持續(xù)減少馋评,你的應(yīng)用可能會被殺死
TRIM_MEMORY_COMPLETE 表示系統(tǒng)內(nèi)存已經(jīng)非常低放接,你的應(yīng)用即將被殺死,請釋放所有可能釋放的資源
這里的通知也是來自ActivityManagerService留特,在updateOomAdjLocked的時(shí)候纠脾,ActivityManagerService會根據(jù)系統(tǒng)內(nèi)存以及應(yīng)用的狀態(tài)通過app.thread.scheduleTrimMemory發(fā)送通知給應(yīng)用程序。
如果你不釋放資源蜕青,那么請看上面的算法:找到比該閾值優(yōu)先級低乳乌,并且內(nèi)存占用較多的進(jìn)程,你的應(yīng)用將最先被干掉