问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Android系统启动流程之lmkd进程详解

创作时间:
作者:
@小白创作中心

Android系统启动流程之lmkd进程详解

引用
CSDN
1.
https://blog.csdn.net/qq_41751728/article/details/139625461

本文详细介绍了Android系统中lmkd进程的启动流程、工作原理以及其在系统资源紧张时如何选择和杀死进程。文章内容深入且技术性强,涉及多个关键组件和机制,如PSI、vmpressure、lowmemorykiller等,并通过代码示例和详细解释帮助读者理解复杂的系统内部运作。

lmkd进程创建

当CPU、IO、内存资源出现紧张的时候,需要lmkd从所有进程中把一些不重要、不干活但又占据大量内存的用户空间进程杀掉,以释放公共资源供大家使用。

启动过程

lmkd进程的启动过程如下:

  1. /system/core/rootdir/init.rc文件中,通过on init触发器启动lmkd
  2. init.rc文件中引用了lmkd.rc配置
  3. lmkd.rc文件定义了lmkd服务的相关配置
  4. 最终执行/system/bin/lmkd可执行程序,调用lmkd.cpp中的main()方法

代码示例

// 文件路径:/system/core/rootdir/init.rc
on init
    write /proc/sys/vm/watermark_boost_factor 0
    chown root system /sys/module/lowmemorykiller/parameters/adj
    chmod 0664 /sys/module/lowmemorykiller/parameters/adj
    chown root system /sys/module/lowmemorykiller/parameters/minfree
    chmod 0664 /sys/module/lowmemorykiller/parameters/minfree
    start lmkd

// 文件路径:/system/memory/lmkd/Android.bp
cc_binary {
    name: "lmkd",
    init_rc: ["lmkd.rc"],
}

// 文件路径:/system/memory/lmkd/lmkd.rc
service lmkd /system/bin/lmkd
    class core
    user lmkd
    group lmkd system readproc
    capabilities DAC_OVERRIDE KILL IPC_LOCK SYS_NICE SYS_RESOURCE
    critical
    socket lmkd seqpacket+passcred 0660 system system
    task_profiles ServiceCapacityLow

// 文件路径:system/memory/lmkd/lmkd.cpp
int main(int argc, char **argv) {
    if (!init()) {
        if (init_reaper()) {
            ALOGI("Process reaper initialized with %d threads in the pool", reaper.thread_cnt());
        }
        if (!watchdog.init()) {
            ALOGE("Failed to initialize the watchdog");
        }
        mainloop();
    }
    android_log_destroy(&ctx);
    ALOGI("exiting");
    return 0;
}

何时去杀进程

lmkd进程本身无权直接检测系统资源使用情况,因此需要借助其他工具来监听内核空间的资源使用情况。在Android高版本中,主要使用PSI(Pressure Stall Information)来监控CPU、内存及IO性能异常。

PSI监控机制

PSI是一种可以监控CPU、内存及IO性能异常的内核功能。lmkd通过PSI来监听系统资源紧张情况,并在资源紧张时触发进程杀戮机制。

代码示例

// 文件路径:system/memory/lmkd/lmkd.cpp
static int init(void) {
    if (use_inkernel_interface) {
        // 使用内核接口
    } else {
        if (!init_monitors()) {
            return -1;
        }
        property_set("sys.lmk.reportkills", "1");
    }
    return 0;
}

static bool init_monitors() {
    use_psi_monitors = GET_LMK_PROPERTY(bool, "use_psi", true) && init_psi_monitors();
    if (!use_psi_monitors && (!init_mp_common(VMPRESS_LEVEL_LOW) || !init_mp_common(VMPRESS_LEVEL_MEDIUM) || !init_mp_common(VMPRESS_LEVEL_CRITICAL))) {
        ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
        return false;
    }
    return true;
}

static bool init_psi_monitors() {
    bool use_new_strategy = GET_LMK_PROPERTY(bool, "use_new_strategy", low_ram_device || !use_minfree_levels);
    if (!use_new_strategy && memcg_version() != MemcgVersion::kV1) {
        ALOGE("Old kill strategy can only be used with v1 cgroup hierarchy");
        return false;
    }
    if (use_new_strategy) {
        psi_thresholds[VMPRESS_LEVEL_LOW].threshold_ms = 0;
        psi_thresholds[VMPRESS_LEVEL_MEDIUM].threshold_ms = psi_partial_stall_ms;
        psi_thresholds[VMPRESS_LEVEL_CRITICAL].threshold_ms = psi_complete_stall_ms;
    }
    if (!init_mp_psi(VMPRESS_LEVEL_LOW, use_new_strategy)) {
        return false;
    }
    if (!init_mp_psi(VMPRESS_LEVEL_MEDIUM, use_new_strategy)) {
        destroy_mp_psi(VMPRESS_LEVEL_LOW);
        return false;
    }
    if (!init_mp_psi(VMPRESS_LEVEL_CRITICAL, use_new_strategy)) {
        destroy_mp_psi(VMPRESS_LEVEL_MEDIUM);
        destroy_mp_psi(VMPRESS_LEVEL_LOW);
        return false;
    }
    return true;
}

static bool init_mp_psi(enum vmpressure_level level, bool use_new_strategy) {
    int fd;
    if (!psi_thresholds[level].threshold_ms) {
        return true;
    }
    fd = init_psi_monitor(psi_thresholds[level].stall_type, psi_thresholds[level].threshold_ms * US_PER_MS, PSI_WINDOW_SIZE_MS * US_PER_MS);
    if (fd < 0) {
        return false;
    }
    vmpressure_hinfo[level].handler = use_new_strategy ? mp_event_psi : mp_event_common;
    vmpressure_hinfo[level].data = level;
    if (register_psi_monitor(epollfd, fd, &vmpressure_hinfo[level]) < 0) {
        destroy_psi_monitor(fd);
        return false;
    }
    maxevents++;
    mpevfd[level] = fd;
    return true;
}

哪些进程需要杀

lmkd需要收集可能被杀的进程信息,并为杀进程提供目标。这些进程信息通过特定的数据结构存储,并通过特定的通信机制传递给lmkd。

数据结构设计

lmkd使用一个数组procadjslot_list来存储进程信息,数组的每个元素是一个双向循环链表,链表的每一个节点指向一个进程。进程的分数通过ADJTOSLOT(adj)算法转换为数组的索引。

// 文件路径:system/memory/lmkd/lmkd.cpp
#define ADJTOSLOT(adj) ((adj) + -OOM_SCORE_ADJ_MIN)
#define ADJTOSLOT_COUNT (ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1)

struct adjslot_list {
    struct adjslot_list *next;
    struct adjslot_list *prev;
};

struct proc {
    struct adjslot_list asl;
    int pid;
    int pidfd;
    uid_t uid;
    int oomadj;
    pid_t reg_pid;
    bool valid;
    struct proc *pidhash_next;
};

static struct adjslot_list procadjslot_list[ADJTOSLOT_COUNT];

进程信息传递

进程信息主要由init进程和system_server进程传递给lmkd。init进程负责管理系统native进程,而system_server进程负责管理运行Java代码的进程。它们通过socket通信将进程信息传递给lmkd。

// 文件路径:system/memory/lmkd/lmkd.cpp
static int init(void) {
    ctrl_sock.sock = android_get_control_socket("lmkd");
    if (ctrl_sock.sock < 0) {
        ALOGE("get lmkd control socket failed");
        return -1;
    }
    return 0;
}

数据协议

通信渠道上传递的数据协议非常简单,格式如下:

![](https://wy-static.wenxiaobai.com/chat-rag-image/8615878855423041540)
cmd xxx xxx xxx

其中,LMK_PROCPRIO用于注册进程并设置其oom_adj_scoreLMK_PROCREMOVE用于注销进程,LMK_SUBSCRIBE用于订阅事件。

如何选择和杀死进程

lmkd在收到资源紧张的通知后,会根据进程的分数(oom_adj_score)来选择需要杀死的进程。分数越高,被杀的优先级越高。

查找和杀死进程

lmkd从最高分数开始查找,直到找到可以杀死的进程。如果所有分数大于等于0的进程都被杀死后,系统资源仍然紧张,lmkd会继续尝试杀死分数更低的进程,但不会杀死分数为负值的进程。

// 文件路径:system/memory/lmkd/lmkd.cpp
static int find_and_kill_process(int min_score_adj, struct kill_info *ki, union meminfo *mi,
                                 struct wakeup_info *wi, struct timespec *tm,
                                 struct psi_data *pd) {
    int i;
    int killed_size = 0;
    bool choose_heaviest_task = kill_heaviest_task;
    for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
        struct proc *procp;
        if (!choose_heaviest_task && i <= PERCEPTIBLE_APP_ADJ) {
            choose_heaviest_task = true;
        }
        while (true) {
            procp = choose_heaviest_task ? proc_get_heaviest(i) : proc_adj_tail(i);
            if (!procp)
                break;
            killed_size = kill_one_process(procp, min_score_adj, ki, mi, wi, tm, pd);
            if (killed_size >= 0) {
                break;
            }
        }
        if (killed_size) {
            break;
        }
    }
    return killed_size;
}

min_score_adj的取值

  • PSI条件下,一般情况下min_score_adj的值是201(PREVIOUS_APP_ADJ + 1),内存极度紧张时为0
  • watchdog条件下,min_score_adj的值为0

总结

lmkd进程在Android系统中扮演着重要的角色,负责在系统资源紧张时选择并杀死合适的进程以释放资源。通过PSI监控系统资源使用情况,结合进程的oom_adj_score分数,lmkd能够智能地选择需要杀死的进程,确保系统稳定运行。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号