115°

[日更-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特性而决定的:

  1. 如果检测到内核中有LMK,lmkd将只去调整目标进程在/proc伪文件系统中的OOM分值,而把系统面临内存不足的压力时具体该杀掉哪个进程的任务交给LMK模块;
  2. 如果内核中没有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)                                = 0

本文由【小馬佩德罗】发布于开源中国,原文链接:https://my.oschina.net/XiaoMaPedro/blog/3062019

全部评论: 0

    我有话说: