打开Android go的设备,或者设置属性ro.config.low_ram
为true,此时Android会认为该设备为低内存设备,本文记录代码中(Android12)对低内存设备做的一些改动.
1. low_ram_device & per_app_memcg
在lmkd的属性处理中,当为低内存设备时,per_app_config
的默认值为true.
swap_free_low_percentage的值默认为10.
// system/memory/lmkd/lmkd.cpp
/* ro.lmk.swap_free_low_percentage property defaults */
#define DEF_LOW_SWAP 10
static int clamp(int low, int high, int value) {
return max(min(value, high), low);
}
static void update_props() {
......
per_app_memcg =
property_get_bool("ro.config.per_app_memcg", low_ram_device);
swap_free_low_percentage = clamp(0, 100, property_get_int32("ro.lmk.swap_free_low_percentage",
DEF_LOW_SWAP));
......
swap_util_max = clamp(0, 100, property_get_int32("ro.lmk.swap_util_max", 100));
filecache_min_kb = property_get_int64("ro.lmk.filecache_min_kb", 0);
}
下面分析下当per_app_config属性为true时主要做了什么?
1.1 cmd_procprio
cmd_procprio
的主要作用是更新进程的的adj,首先通过lmkd_pack_get_procprio
将packet解释为lmk_procprio
类型的数据,该数据类型包括pid,uid以及adj,接着检查需要设置的adj是否在范围以内,最后写入到/proc/[pid]/oom_score_adj
.至此如果是使用内核逻辑的话,就会返回.否则还会进行进一步处理.
假如是low_ram_device
,还会去更新soft_limit_mult
到/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_byte
.最后还会通过调用pid_lookup在哈希表中是否存在该进程,假如不存在,则将进程加入到双向链表中,否则将该进程移出,并重新加入到双向链表中的头部.
memory.soft_limit_in_byte
具体有什么用下面会继续分析
static void cmd_procprio(LMKD_CTRL_PACKET packet, int field_count, struct ucred *cred) {
......
/* lmkd should not change soft limits for services */
if (params.ptype == PROC_TYPE_APP && per_app_memcg) {
if (params.oomadj >= 900) {
soft_limit_mult = 0;
} else if (params.oomadj >= 800) {
soft_limit_mult = 0;
} else if (params.oomadj >= 700) {
soft_limit_mult = 0;
} else if (params.oomadj >= 600) {
// Launcher should be perceptible, don't kill it.
params.oomadj = 200;
soft_limit_mult = 1;
} else if (params.oomadj >= 500) {
soft_limit_mult = 0;
} else if (params.oomadj >= 400) {
soft_limit_mult = 0;
} else if (params.oomadj >= 300) {
soft_limit_mult = 1;
} else if (params.oomadj >= 200) {
soft_limit_mult = 8;
} else if (params.oomadj >= 100) {
soft_limit_mult = 10;
} else if (params.oomadj >= 0) {
soft_limit_mult = 20;
} else {
// Persistent processes will have a large
// soft limit 512MB.
soft_limit_mult = 64;
}
snprintf(path, sizeof(path), MEMCG_SYSFS_PATH
"apps/uid_%d/pid_%d/memory.soft_limit_in_bytes",
params.uid, params.pid);
snprintf(val, sizeof(val), "%d", soft_limit_mult * EIGHT_MEGA);
/*
* system_server process has no memcg under /dev/memcg/apps but should be
* registered with lmkd. This is the best way so far to identify it.
*/
is_system_server = (params.oomadj == SYSTEM_ADJ &&
(pwdrec = getpwnam("system")) != NULL &&
params.uid == pwdrec->pw_uid);
writefilestring(path, val, !is_system_server);
}
......
}
1.2 libprocessgroup
libprocessgroup会根据'ro.config.per_app_memcg'属性判断使用使用per-app-memcg,如果未设置该属性。在低内存设备会默认使用per-app-memcg,否则为false.
然后libprocessgroup会创建相关的节点,
// system/core/libprocessgroup/processgroup.cpp
bool UsePerAppMemcg() {
bool low_ram_device = GetBoolProperty("ro.config.low_ram", false);
return GetBoolProperty("ro.config.per_app_memcg", low_ram_device);
}
static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries,
int* max_processes) {
std::string hierarchy_root_path;
CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &hierarchy_root_path);
const char* cgroup = hierarchy_root_path.c_str();
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
if (max_processes != nullptr) {
*max_processes = 0;
}
int retry = retries;
int processes;
while ((processes = DoKillProcessGroupOnce(cgroup, uid, initialPid, signal)) > 0) {
if (max_processes != nullptr && processes > *max_processes) {
*max_processes = processes;
}
LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid;
if (retry > 0) {
std::this_thread::sleep_for(5ms);
--retry;
} else {
break;
}
}
if (processes < 0) {
PLOG(ERROR) << "Error encountered killing process cgroup uid " << uid << " pid "
<< initialPid;
return -1;
}
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
// We only calculate the number of 'processes' when killing the processes.
// In the retries == 0 case, we only kill the processes once and therefore
// will not have waited then recalculated how many processes are remaining
// after the first signals have been sent.
// Logging anything regarding the number of 'processes' here does not make sense.
if (processes == 0) {
if (retries > 0) {
LOG(INFO) << "Successfully killed process cgroup uid " << uid << " pid " << initialPid
<< " in " << static_cast<int>(ms) << "ms";
}
int err = RemoveProcessGroup(cgroup, uid, initialPid, retries);
if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
std::string memory_path;
CgroupGetControllerPath("memory", &memory_path);
memory_path += "/apps";
if (RemoveProcessGroup(memory_path.c_str(), uid, initialPid, retries)) return -1;
}
return err;
} else {
if (retries > 0) {
LOG(ERROR) << "Failed to kill process cgroup uid " << uid << " pid " << initialPid
<< " in " << static_cast<int>(ms) << "ms, " << processes
<< " processes remain";
}
return -1;
}
}
int createProcessGroup(uid_t uid, int initialPid, bool memControl) {
std::string cgroup;
if (memControl && !UsePerAppMemcg()) {
PLOG(ERROR) << "service memory controls are used without per-process memory cgroup support";
return -EINVAL;
}
if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
CgroupGetControllerPath("memory", &cgroup);
cgroup += "/apps";
int ret = createProcessGroupInternal(uid, initialPid, cgroup);
if (ret != 0) {
return ret;
}
}
CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &cgroup);
return createProcessGroupInternal(uid, initialPid, cgroup);
}
设置ro.config.per_app_memcg
属性为false,将不会使用per_app_memcg。
在开发板上实际测试时发现,未设置ro.config.per_app_memcg
,由于是低内存设备,swap内存会很快被用光。将ro.config.per_app_memcg
属性为false,不适用per_app_memcg,swap内存还是会快速为0.
2. art
如果ro.config.low_ram
属性为true,此时会在虚拟机启动的时候添加LowMemoryMode参数。
// frameworks/base/core/jni/AndroidRuntime.cpp
/*
* Start the Dalvik Virtual Machine.
*
* Various arguments, most determined by system properties, are passed in.
* The "mOptions" vector is updated.
*
* CAUTION: when adding options in here, be careful not to put the
* char buffer inside a nested scope. Adding the buffer to the
* options using mOptions.add() does not copy the buffer, so if the
* buffer goes out of scope the option may be overwritten. It's best
* to put the buffer at the top of the function so that it is more
* unlikely that someone will surround it in a scope at a later time
* and thus introduce a bug.
*
* Returns 0 on success.
*/
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool primary_zygote)
{
......
property_get("ro.config.low_ram", propBuf, "");
if (strcmp(propBuf, "true") == 0) {
addOption("-XX:LowMemoryMode");
}
......
}
在下面的函数中会解析出来,如果是低内存设备,background_collector_type_
为kCollectorTypeSS。
// art/runtime/parsed_options.cc
bool ParsedOptions::DoParse(const RuntimeOptions& options,
bool ignore_unrecognized,
RuntimeArgumentMap* runtime_options) {
for (size_t i = 0; i < options.size(); ++i) {
if (true && options[0].first == "-Xzygote") {
LOG(INFO) << "option[" << i << "]=" << options[i].first;
}
}
......
{
// If not set, background collector type defaults to homogeneous compaction.
// If not low memory mode, semispace otherwise.
gc::CollectorType background_collector_type_;
gc::CollectorType collector_type_ = (XGcOption{}).collector_type_;
bool low_memory_mode_ = args.Exists(M::LowMemoryMode);
background_collector_type_ = args.GetOrDefault(M::BackgroundGc);
{
XGcOption* xgc = args.Get(M::GcOption);
if (xgc != nullptr && xgc->collector_type_ != gc::kCollectorTypeNone) {
collector_type_ = xgc->collector_type_;
}
}
if (background_collector_type_ == gc::kCollectorTypeNone) {
background_collector_type_ = low_memory_mode_ ?
gc::kCollectorTypeSS : gc::kCollectorTypeHomogeneousSpaceCompact;
}
args.Set(M::BackgroundGc, BackgroundGcOption { background_collector_type_ });
}
......
}
还有下面的影响,不太清楚
// art/runtime/runtime.cc
static constexpr double kLowMemoryMinLoadFactor = 0.5;
static constexpr double kLowMemoryMaxLoadFactor = 0.8;
static constexpr double kNormalMinLoadFactor = 0.4;
static constexpr double kNormalMaxLoadFactor = 0.7;
bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
......
is_low_memory_mode_ = runtime_options.Exists(Opt::LowMemoryMode);
......
float foreground_heap_growth_multiplier;
if (is_low_memory_mode_ && !runtime_options.Exists(Opt::ForegroundHeapGrowthMultiplier)) {
// If low memory mode, use 1.0 as the multiplier by default.
foreground_heap_growth_multiplier = 1.0f;
} else {
foreground_heap_growth_multiplier =
runtime_options.GetOrDefault(Opt::ForegroundHeapGrowthMultiplier) +
kExtraDefaultHeapGrowthMultiplier;
}
}
double Runtime::GetHashTableMinLoadFactor() const {
return is_low_memory_mode_ ? kLowMemoryMinLoadFactor : kNormalMinLoadFactor;
}
double Runtime::GetHashTableMaxLoadFactor() const {
return is_low_memory_mode_ ? kLowMemoryMaxLoadFactor : kNormalMaxLoadFactor;
}
评论 (0)