[日更-2019.5.20] Android 系統內的守護程序(三)--core類中的服務 : lmkd
宣告
- 其實很好奇Android系統中的一些關鍵守護程序服務的作用;
- 暫且大概分析下它們的作用,這樣有助於理解整個系統的工作過程;
0 寫在前面的
- 只要是作業系統,不用說的就是其中肯定會執行著一些很多守護程序(daemon)來完成很多雜亂的工作。通過系統中的init.rc檔案也可以看出來,其中每個service中就包含著系統後臺服務程序。
- 而這些服務被分為:core類服務(adbd/servicemanager/healthd/lmkd/logd/vold)和main類服務;
- main類服務又分為:網路類服務(netd/mdnsd/mtpd/rild)、圖形及媒體類服務(surfaceflinger/bootanimation/mediaserver/dnnserver)、其他類服務(installd/keystore/debuggerd/sdcard/Zygote)
1 lmkd的作用
Android系統中有一個core類的守護程序lmkd。
lmkd 提供了核心中的LMK(Low Memory Killer)機制的一個介面,它是個Android特有的。lmkd允許Android高階使用者能夠部分控制Linux的Out-Of-Memory(OOM)機制(在系統實體記憶體不足時,通過殺掉部分程序的方式,緩解記憶體壓力的機制)。lmkd可以使用/proc/pid/oom_score_adj檔案,通過調整相關程序的OOM分值的方式,使它們在系統面臨記憶體壓力時,更容易或更不容易被殺掉。Linux的OOM機制有機會再後續補充。Lmkd有兩種可能的操作模式,而具體進入哪種模式,是根據是否檢測到了LMK這一Android特性而決定的:
- 如果檢測到核心中有LMK,lmkd將只去調整目標程序在/proc偽檔案系統中的OOM分值,而把系統面臨記憶體不足的壓力時具體該殺掉哪個程序的任務交給LMK模組;
- 如果核心中沒有LMK,lmkd也會擔負起響應記憶體壓力事件的任務,並具體執行殺程序的任務(也就是向被殺的程序傳送SIGKILL訊號)。lmkd會維護一張程序的Hash表,以便快速查閱所有程序的OOM分值。
Android系統中很多其他守護程序,lmkd也會使用epoll_wait來同時等待多個socket的輸入。該程序主要監聽/dev/socket/lmkd,它是由init程序為lmkd建立的。這個socket唯一期望連線的客戶端是ActivityManagerService,ActivityManagerService通過這個socket來通知lmkd哪個程序需要調整它的OOM分值(通過ProcessList類)。
當核心中LMK不可用時(即找不到/sys/module/lowmemorykiller檔案時),lmkd還會額外去監聽/dev/memcg這個cgroup中的檔案,以獲取記憶體壓力事件,整個流程如下圖所示:
在負責處理記憶體壓力事件時(即在核心中沒有使用LMK的情況下),lmkd會去解析/proc/zoneinfo檔案,獲取下列值:
- nr_free_pages : 空閒記憶體的總數(單位:4 KB)
- nr_file_pages : 檔案對映1~i 用的記憶體總數(單位:4KB)
- nr_shmem : 共享的記憶體的總數,也就是由多個程序公用的記憶體頁,因而也應該從因檔案對映而佔用的記憶體總數中逐次減掉。
- nr_totalreserve_pages : 系統保留的記憶體的總數,儘管這些頁是空閒的,但它們實際上卻是不能使用的,所以也應該從空閒記憶體總數中減掉。
然後lmkd會不斷地殺掉被選中的程序,直到空閒記憶體總數達到要求或目標檔案能被對映到記憶體中去為止。
2 利用strace監控lmkd
使用功能強大的strace。
root@angler:/ # ps | grep "lmkd"
root 381 1 6404 1232 SyS_epoll_ 7f81e931b4 S /system/bin/lmkd
root@angler:/ # strace -p 381
Process 381 attached
epoll_pwait(3, {{EPOLLIN, {u32=2065154864, u64=367137375024}}}, 2, -1, NULL, 8) = 1
read(6, "\0\0\0\1\0\0\16\276\0\0'&\0\0\0\0", 52) = 16
openat(AT_FDCWD, "/proc/3774/oom_score_adj", O_WRONLY) = 7
write(7, "0", 1) = 1
close(7) = 0
epoll_pwait(3, {{EPOLLIN, {u32=2065154864, u64=367137375024}}}, 2, -1, NULL, 8) = 1
read(6, "\0\0\0\1\0\0\7\f\0\0'\27\0\0\0\2", 52) = 16
openat(AT_FDCWD, "/proc/1804/oom_score_adj", O_WRONLY) = 7
write(7, "117", 3) = 3
close(7) = 0
epoll_pwait(3, {{EPOLLIN, {u32=2065154864, u64=367137375024}}}, 2, -1, NULL, 8) = 1
read(6, "\0\0\0\1\0\0\4\216\0\0'\22\0\0\0\t", 52) = 16
openat(AT_FDCWD, "/proc/1166/oom_score_adj", O_WRONLY) = 7
write(7, "529", 3) = 3
close(7) = 0
epoll_pwait(3, {{EPOLLIN, {u32=2065154864, u64=367137375024}}}, 2, -1, NULL, 8) = 1
read(6, "\0\0\0\1\0\0\6\240\0\0'-\0\0\0\v", 52) = 16
openat(AT_FDCWD, "/proc/1696/oom_score_adj", O_WRONLY) = 7
write(7, "647", 3) = 3
close(7) = 0
epoll_pwait(3, {{EPOLLIN, {u32=2065154864, u64=367137375024}}}, 2, -1, NULL, 8) = 1
read(6, "\0\0\0\1\0\0\6\223\0\0':\0\0\0\v", 52) = 16
openat(AT_FDCWD, "/proc/1683/oom_score_adj", O_WRONLY) = 7
write(7, "647", 3) = 3
close(7) = 0
epoll_pwait(3, {{EPOLLIN, {u32=2065154864, u64=367137375024}}}, 2, -1, NULL, 8) = 1
read(6, "\0\0\0\1\0\0\6\371\0\0'\21\0\0\0\r", 52) = 16
openat(AT_FDCWD, "/proc/1785/oom_score_adj", O_WRONLY) = 7
write(7, "764", 3) = 3
close(7)