frameworks/base/services/core/java/com/android/server/am/LmkdConnection.java
/**
* Exchange a request/reply packets with lmkd
*
* @param req The buffer holding the request data to be sent
* @param repl The buffer to receive the reply
*/// Exchange a request/reply packets with lmkd
public boolean exchange(ByteBuffer req, ByteBuffer repl) {
if (repl == null) {
return write(req);
}boolean result = false;
// set reply buffer to user-defined one to fill it
synchronized (mReplyBufLock) {
mReplyBuf = repl;if (write(req)) {
try {
// wait for the reply
mReplyBufLock.wait();
result = (mReplyBuf != null);
} catch (InterruptedException ie) {
result = false;
}
}// reset reply buffer
mReplyBuf = null;
}
return result;
}
//向native 层写数据
private boolean write(ByteBuffer buf) {
synchronized (mLmkdSocketLock) {
try {
mLmkdOutputStream.write(buf.array(), 0, buf.position());
} catch (IOException ex) {
return false;
}
return true;
}
}
//从native 中读取数据
private int read(ByteBuffer buf) {
synchronized (mLmkdSocketLock) {
try {
return mLmkdInputStream.read(buf.array(), 0, buf.array().length);
} catch (IOException ex) {
}
return -1;
}
}
androidu/system/memory/lmkd/lmkd.cpp
/*
* Write the state_changed over the data socket to be propagated via AMS to statsd
*///将state_changed 状态通过socket传给AMS
static void stats_write_lmk_state_changed(enum lmk_state state) {
ALOGE("stats_write_lmk_state_changed");
LMKD_CTRL_PACKET packet_state_changed;
const size_t len = lmkd_pack_set_state_changed(packet_state_changed, state);
if (len == 0) {
return;
}
for (int i = 0; i < MAX_DATA_CONN; i++) {
if (data_sock[i].sock >= 0 && data_sock[i].async_event_mask & 1 << LMK_ASYNC_EVENT_STAT) {
ctrl_data_write(i, (char*)packet_state_changed, len);
}
}
}
androidu/frameworks/base/services/core/java/com/android/server/am/ProcessList.java
//向lmkd 写数据
private static boolean writeLmkd(ByteBuffer buf, ByteBuffer repl) {
if (!sLmkdConnection.isConnected()) {
// try to connect immediately and then keep retrying
sKillHandler.sendMessage(
sKillHandler.obtainMessage(KillHandler.LMKD_RECONNECT_MSG));// wait for connection retrying 3 times (up to 3 seconds)
// 等待连接重试3次(最多3秒)
if (!sLmkdConnection.waitForConnection(3 * LMKD_RECONNECT_DELAY_MS)) {
return false;
}
}
//调用LmkdConnection的exchange 方法
return sLmkdConnection.exchange(buf, repl);
}
/**
* Handle the unsolicited message from zygote.
*///监听来自Zygote的event的数据
private int handleZygoteMessages(FileDescriptor fd, int events) {
final int eventFd = fd.getInt$();
if ((events & EVENT_INPUT) != 0) {
// An incoming message from zygote
try {
final int len = Os.read(fd, mZygoteUnsolicitedMessage, 0,
mZygoteUnsolicitedMessage.length);
if (len > 0 && mZygoteSigChldMessage.length == Zygote.nativeParseSigChld(
mZygoteUnsolicitedMessage, len, mZygoteSigChldMessage)) {
mAppExitInfoTracker.handleZygoteSigChld(
mZygoteSigChldMessage[0] /* pid */,
mZygoteSigChldMessage[1] /* uid */,
mZygoteSigChldMessage[2] /* status */);
}
} catch (Exception e) {
Slog.w(TAG, "Exception in reading unsolicited zygote message: " + e);
}
}
return EVENT_INPUT;
}
方案记录:
1.mem_free:剩余可分配内存大小,每个策略对应不同的剩余内存大小阈值
2. pgscan_kswapd:间接回收的内存页数,通过前后两次的数量差和时间差,计算出单位时间内kswpad回收的内存页数,来表示kswpad的活跃状态
3. swap_free:Swap的剩余大小,每个策略对应不同的交换分区内存大小阈值
4. pgscan_direct:直接回收的内存页数,通过前后两次的数量差和时间差,计算出单位时间内直接回收内存页数,来表示内存的压力情况
5. mem_cache
6. mem_available_kb
7. mem_total_kb
8. wmark
9 .swap_total
策略记录:
1. 当CPU 占有率超过 85% 开始杀. adj >=900
2. 使用kill进程的方法,不是forcestop 方法。
获取CPU 占用率
计算CPU 占用率:
安卓cpu信息查看与cpu占用率计算_android cpu 占用率-CSDN博客
代码:
1.androidu/system/memory/lmkd/include/lmkd.h
enum lmk_cmd {
LMK_TARGET = 0, /* Associate minfree with oom_adj_score */
LMK_PROCPRIO, /* Register a process and set its oom_adj_score */
LMK_PROCREMOVE, /* Unregister a process */
LMK_PROCPURGE, /* Purge all registered processes */
LMK_GETKILLCNT, /* Get number of kills */
LMK_SUBSCRIBE, /* Subscribe for asynchronous events */
LMK_PROCKILL, /* Unsolicited msg to subscribed clients on proc kills */
LMK_UPDATE_PROPS, /* Reinit properties */
LMK_STAT_KILL_OCCURRED, /* Unsolicited msg to subscribed clients on proc kills for statsd log */
LMK_STAT_STATE_CHANGED, /* Unsolicited msg to subscribed clients on state changed */
+ LMK_STAT_LEVEL_CHANGED,
};
-#define MAX_TARGETS 6
+#define MAX_TARGETS 16
+/* LMK_LEVEL packet payload */
+struct lmk_level_data {
+ int level;
+ int64_t free_swap_kb;
+ int64_t free_mem_kb;
+ int64_t pgscan_kswapd;
+ int64_t pgscan_direct;
+ int64_t mem_cache_kb;
+ int64_t mem_available_kb;
+ int64_t mem_total_kb;
+ int64_t swap_total_kb;
+ int wmark;
+};
+static inline size_t lmkd_pack_set_level_changed(LMKD_CTRL_PACKET packet,struct lmk_level_data data) {
+ packet[0] = htonl(LMK_STAT_LEVEL_CHANGED);
+ packet[1] = htonl(data.level);
+ packet[2] = htonl(data.free_swap_kb);
+ packet[3] = htonl(data.free_mem_kb);
+ packet[4] = htonl(data.pgscan_kswapd);
+ packet[5] = htonl(data.pgscan_direct);
+ packet[6] = htonl(data.mem_cache_kb);
+ packet[7] = htonl(data.mem_available_kb);
+ packet[8] = htonl(data.mem_total_kb);
+ packet[9] = htonl(data.swap_total_kb);
+ packet[10] = htonl(data.wmark);
+ return 3 * sizeof(int) + 8 * sizeof(int64_t);
+}
2. androidu/system/memory/lmkd/lmkd.cpp
/* Fields to parse in /proc/meminfo */
enum meminfo_field {
- MI_NR_FREE_PAGES = 0,
+ MI_MEM_TOTAL = 0,
+ MI_NR_FREE_PAGES,
+ MI_MEM_AVAILABLE,
MI_CACHED,
MI_SWAP_CACHED,
MI_BUFFERS,
static const char* const meminfo_field_names[MI_FIELD_COUNT] = {
+ "MemTotal:",
"MemFree:",
+ "MemAvailable:",
"Cached:",
"SwapCached:",
"Buffers:",
union meminfo {
struct {
+ int64_t mem_total_kb;
int64_t nr_free_pages;
+ int64_t nr_mem_available;
int64_t cached;
int64_t swap_cached;
int64_t buffers;
@@ -770,6 +776,7 @@ static ssize_t ctrl_data_read(int dsock_idx, char* buf, size_t bufsz, struct ucr
}
static int ctrl_data_write(int dsock_idx, char* buf, size_t bufsz) {
+ ALOGE("ctrl_data_write");
int ret = 0;
ret = TEMP_FAILURE_RETRY(write(data_sock[dsock_idx].sock, buf, bufsz));
@@ -789,6 +796,7 @@ static int ctrl_data_write(int dsock_idx, char* buf, size_t bufsz) {
* will receive this unsolicited notification.
*/
static void ctrl_data_write_lmk_kill_occurred(pid_t pid, uid_t uid) {
+ ALOGE("ctrl_data_write_lmk_kill_occurred pid:%i ,uid:%i", pid,uid);
LMKD_CTRL_PACKET packet;
size_t len = lmkd_pack_set_prockills(packet, pid, uid);
@@ -799,11 +807,26 @@ static void ctrl_data_write_lmk_kill_occurred(pid_t pid, uid_t uid) {
}
}
+
+static void ctrl_data_write_lmk_level_changed(lmk_level_data data) {
+ ALOGE("ctrl_data_write_lmk_level_changed");
+ LMKD_CTRL_PACKET packet;
+ size_t len = lmkd_pack_set_level_changed(packet,data);
+
+ for (int i = 0; i < MAX_DATA_CONN; i++) {
+ if (data_sock[i].sock >= 0 && data_sock[i].async_event_mask & 1 << LMK_ASYNC_EVENT_KILL) {
+ ctrl_data_write(i, (char*)packet, len);
+ }
+ }
+}
+
/*
* Write the kill_stat/memory_stat over the data socket to be propagated via AMS to statsd
*/
static void stats_write_lmk_kill_occurred(struct kill_stat *kill_st,
struct memory_stat *mem_st) {
+ ALOGE("stats_write_lmk_kill_occurred");
LMK_KILL_OCCURRED_PACKET packet;
const size_t len = lmkd_pack_set_kill_occurred(packet, kill_st, mem_st);
if (len == 0) {
@@ -830,6 +853,7 @@ static void stats_write_lmk_kill_occurred_pid(int pid, struct kill_stat *kill_st
* Write the state_changed over the data socket to be propagated via AMS to statsd
*/
static void stats_write_lmk_state_changed(enum lmk_state state) {
+ ALOGE("stats_write_lmk_state_changed");
LMKD_CTRL_PACKET packet_state_changed;
const size_t len = lmkd_pack_set_state_changed(packet_state_changed, state);
if (len == 0) {
@@ -1473,6 +1497,7 @@ static void cmd_target(int ntargets, LMKD_CTRL_PACKET packet) {
}
static void ctrl_command_handler(int dsock_idx) {
+ ALOGE("ctrl_command_handler");
LMKD_CTRL_PACKET packet;
struct ucred cred;
int len;
@@ -1492,6 +1517,7 @@ static void ctrl_command_handler(int dsock_idx) {
}
cmd = lmkd_pack_get_cmd(packet);
+ ALOGE("ctrl_command_handler command code %d", cmd);
nargs = len / sizeof(int) - 1;
if (nargs < 0)
goto wronglen;
@@ -1578,6 +1604,7 @@ wronglen:
static void ctrl_data_handler(int data, uint32_t events,
struct polling_params *poll_params __unused) {
+ ALOGI("ctrl_data_handler");
if (events & EPOLLIN) {
static void mp_event_psi(int data, uint32_t events, struct polling_params *poll_params) {
+
+ ALOGE("mp_event_psi");
if (!psi_parse_mem(&psi_data)) {
critical_stall = psi_data.mem_stats[PSI_FULL].avg10 > (float)stall_limit_critical;
}
+
+ //上报:mem_free, swap_free, pgscan_kswpad, pgscan_direct
+
+ ALOGE("mp_event_psi level:%s,free_swap_kb:%ld,free_mem_kb:%ld,pgscan_kswapd:%ld,pgscan_direct:%ld",level_name[level],get_free_swap(&mi) * page_k,mi.field.nr_free_pages * page_k,vs.field.pgscan_kswapd,vs.field.pgscan_direct);
+
+
+ ALOGE("mp_event_psi level:%s,page_k:%ld,mem_cache:%ld,mem_available_kb:%ld,mem_total_kb:%ld,swap_total:%ld,wmark:%d",level_name[level],page_k,mi.field.cached,mi.field.nr_mem_available,mi.field.mem_total_kb,mi.field.total_swap,wmark);
+
+ struct lmk_level_data level_data;
+ level_data.level = level;
+ level_data.free_swap_kb = get_free_swap(&mi) * page_k;
+ level_data.free_mem_kb = mi.field.nr_free_pages * page_k;
+ level_data.pgscan_kswapd = vs.field.pgscan_kswapd;
+ level_data.pgscan_direct = vs.field.pgscan_direct;
+ level_data.mem_cache_kb = mi.field.cached;
+ level_data.mem_available_kb = mi.field.nr_mem_available;
+ level_data.mem_total_kb = mi.field.mem_total_kb;
+ level_data.swap_total_kb = mi.field.total_swap;
+ level_data.wmark = wmark;
+ //if(level_changed){
+ ctrl_data_write_lmk_level_changed(level_data);
+ //}
3. androidu/frameworks/base/services/core/java/com/android/server/am/LmkdConnection.java
- private static final String TAG = TAG_WITH_CLASS_NAME ? "LmkdConnection" : TAG_AM;
+ private static final String TAG = "LmkdConnection";
4. androidu/frameworks/base/services/core/java/com/android/server/am/ProcessList.java
+import java.io.FileReader;
+import java.io.BufferedReader;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
- static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;
+ static final String TAG = "ProcessList";
static final byte LMK_STATE_CHANGED = 9; // Msg to subscribed clients on state changed
+ static final byte LMK_LEVEL_CHANGED = 10;
LmkdStatsReporter.logStateChanged(state);
return true;
+ case LMK_LEVEL_CHANGED:
+ Slog.i(TAG, "handleUnsolicitedMessage LMK_LEVEL_CHANGED");
+ int level = inputData.readInt();
+ long freeSwapKb = inputData.readInt();
+ long freeMemKb = inputData.readInt();
+ long pgscanKswapd = inputData.readInt();
+ long pgscanDirect = inputData.readInt();
+ long memCacheKb = inputData.readInt();
+ long memAvailableKb = inputData.readInt();
+ long memTotalKb = inputData.readInt();
+ long swapTotalKb = inputData.readInt();
+ long wmark = inputData.readInt();
+
+
+ Slog.i(TAG, "handleUnsolicitedMessage LMK_LEVEL_CHANGED level:" + level +",freeSwapKb:"+freeSwapKb +",freeMemKb:"+freeMemKb+",pgscanKswapd:"+pgscanKswapd+",pgscanDirect:"+pgscanDirect+",memCacheKb:"+memCacheKb+",memAvailableKb:"+memAvailableKb+",memTotalKb:"+memTotalKb+",swapTotalKb:"+swapTotalKb+",wmark:"+wmark);
+
+ Slog.i(TAG, "handleUnsolicitedMessage LMK_LEVEL_CHANGED cpu rate:" + getCPURate());
+ return true;
default:
return false;
+
+ private void getKillApp(){
+ //cache current running process
+ ArrayList<ProcessRecord> procs = new ArrayList<>();
+ synchronized(mService) {
+ procs.addAll(mService.mProcessList.getLruProcessesLOSP());
+ }
+
+ for (int i = 0; i < procs.size(); i++) {
+ ProcessRecord app = procs.get(i);
+ final ProcessErrorStateRecord errState = app.mErrorstate;
+ if (app.getThread() == null) {
+ Slog.w(TAG,"skip"+ app.processName + " by app.getThread( ) == null");
+ continue;
+ }
+ if (errState.isCrashing()) {
+ Slog.w(TAG,"skip" + app.processName + " by crashing");
+ continue;
+ }
+
+ if (errState.isNotResponding()) {
+ Slog.w(TAG,"skip"+ app.processName +"by NotResponding");
+ continue;
+ }
+ ApplicationInfo appInfo = app.info;
+ int appAdj = app.mstate.getSetAdj();
+ ProcessStateRecord state - app.mstate;
+ if (DEBUG) {
+ Slog.d(TAG,"checking process:" + app.processName + ",adj:" + appAdj + ", state:" + state.getCurProcState()
+ +",adjtype:"+ state.getAdjType() + ",cached:"+ state.isCached());
+ }
+
+ if (appAdj < minAdj) {
+ // process adj lower than minAdj, this package should not be killed
+ skipPkgSet.add(appInfo.packageName);
+ if (killTargetMap.containsKey(appInfo.packageName)){
+ killTargetMap.remove(appInfo.packageName);
+ if (DEBUG_MORE) {
+ slog.d(TAG,"skip pkg, adj: " + appAdj + "procName:" + app.processName + "pkgname:" + appInfo.packageName);
+ }
+ continue;
+ }
+}
+
+
+
+ /**
+ * ^ 表示匹配行的开头。
+ * cpu 匹配 "cpu" 这个单词。
+ * \\s+ 匹配一个或多个空格字符。
+ * (\\d+\\s+){9} 匹配由一个或多个数字加上一个或多个空格字符组成的序列,重复9次。
+ * \\d+ 匹配一个或多个数字。
+ * $ 匹配行的结尾。 因此,整个正则表达式可以匹配以 "cpu" 开头,后面跟着10个由空格分隔的数字的行。
+ */
+private static String getCPURate() {
+ String path = "/proc/stat";// 系统CPU信息文件
+ long Totaljiffies[] = new long[2];
+ long totalIdle[] = new long[2];
+ FileReader fileReader = null;
+ BufferedReader bufferedReader = null;
+ Pattern pattern = Pattern.compile("^cpu\\s+(\\d+\\s+){9}\\d+$", Pattern.MULTILINE);
+ //正则表达式,只获取第一行
+ for (int i = 0; i < 2; i++) { //每一次调用分为两次获取 方便求差
+ Totaljiffies[i] = 0;
+ totalIdle[i] = 0;
+ try {
+ fileReader = new FileReader(path);
+ bufferedReader = new BufferedReader(fileReader, 8192);
+ String str;
+ while ((str = bufferedReader.readLine()) != null) { //读取stat信息
+ if (str.toLowerCase().startsWith("cpu")) {//以cpu开头的
+ Matcher matcher = pattern.matcher(str);//直接获取第一行cpu开头的数据
+ // 不需要cpu0-7的,那样的话还得多几步运算
+ while (matcher.find()) {
+ String[] values = extractValues(matcher.group());
+ Totaljiffies[i] = sumValues(values);
+ totalIdle[i] = Long.parseLong(values[3]);
+ }
+ }
+ if(i==0){//第一次获取后进行延时等待系统更新信息
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } finally {
+ if (bufferedReader != null) {
+ try {
+ bufferedReader.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ double rate = 0;
+ if (Totaljiffies[1] > Totaljiffies[0] ) {//正常情况下第二次总的jiffies一定比第一次获得的数据大
+ rate = 1.0 * ((Totaljiffies[1] - totalIdle[1]) - (Totaljiffies[0] - totalIdle[0]))
+ / (Totaljiffies[1] - Totaljiffies[0]);
+ }
+ return String.valueOf(rate);
+ }
+ /**
+ * 头行去掉cpu,合并成String[]数组
+ * @param input
+ * @return
+ */
+ public static String[] extractValues(String input) {
+ String[] parts = input.split("\\s+");
+ String[] values = new String[parts.length - 1]; // 去掉 "cpu",所以长度减一
+
+ System.arraycopy(parts, 1, values, 0, parts.length - 1);
+ return values;
+ }
+ /**
+ * 求数组和
+ * @param input
+ * @return
+ */
+ public static Long sumValues(String input[]) {
+ Long sum = Long.valueOf(0);
+ for (String value : input) {
+ sum += Integer.parseInt(value);
+ }
+ return sum;
+ }
计算CPU 占有率:
/**
* ^ 表示匹配行的开头。
* cpu 匹配 "cpu" 这个单词。
* \\s+ 匹配一个或多个空格字符。
* (\\d+\\s+){9} 匹配由一个或多个数字加上一个或多个空格字符组成的序列,重复9次。
* \\d+ 匹配一个或多个数字。
* $ 匹配行的结尾。 因此,整个正则表达式可以匹配以 "cpu" 开头,后面跟着10个由空格分隔的数字的行。
*/
private static String getCPURate() {
String path = "/proc/stat";// 系统CPU信息文件
long Totaljiffies[] = new long[2];
long totalIdle[] = new long[2];
FileReader fileReader = null;
BufferedReader bufferedReader = null;
Pattern pattern = Pattern.compile("^cpu\\s+(\\d+\\s+){9}\\d+$", Pattern.MULTILINE);
//正则表达式,只获取第一行
for (int i = 0; i < 2; i++) { //每一次调用分为两次获取 方便求差
Totaljiffies[i] = 0;
totalIdle[i] = 0;
try {
fileReader = new FileReader(path);
bufferedReader = new BufferedReader(fileReader, 8192);
String str;
while ((str = bufferedReader.readLine()) != null) { //读取stat信息
if (str.toLowerCase().startsWith("cpu")) {//以cpu开头的
Matcher matcher = pattern.matcher(str);//直接获取第一行cpu开头的数据
// 不需要cpu0-7的,那样的话还得多几步运算
while (matcher.find()) {
String[] values = extractValues(matcher.group());
Totaljiffies[i] = sumValues(values);
totalIdle[i] = Long.parseLong(values[3]);
}
}
if(i==0){//第一次获取后进行延时等待系统更新信息
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
double rate = 0;
if (Totaljiffies[1] > Totaljiffies[0] ) {//正常情况下第二次总的jiffies一定比第一次获得的数据大
rate = 1.0 * ((Totaljiffies[1] - totalIdle[1]) - (Totaljiffies[0] - totalIdle[0]))
/ (Totaljiffies[1] - Totaljiffies[0]);
}
return String.valueOf(rate);
}
/**
* 头行去掉cpu,合并成String[]数组
* @param input
* @return
*/
public static String[] extractValues(String input) {
String[] parts = input.split("\\s+");
String[] values = new String[parts.length - 1]; // 去掉 "cpu",所以长度减一
System.arraycopy(parts, 1, values, 0, parts.length - 1);
return values;
}
/**
* 求数组和
* @param input
* @return
*/
public static Long sumValues(String input[]) {
Long sum = Long.valueOf(0);
for (String value : input) {
sum += Integer.parseInt(value);
}
return sum;
}