进程管理上层代码-1—Process.java
一、简介
1. 此文件位于 frameworks/base/core/java/android/os/Process.java。是Android中为Java层提供调度相关配置接口的类,其JNI文件是 frameworks/base/core/jni/android_util_Process.cpp。android/os/Process.java 中只是为了为外部提供功能实现接口,所有函数和变量全部是static修饰的。
2. 内核调度器提供了如下系统调用接口
/* 设置当前线程的nice值 */ int nice(int inc); /* 设置指定线程的调度策略和参数 */ int sched_setscheduler(pid_t pid, int policy, const struct sched_param *param); /* 查询指定线程的调度策略和参数 */ int sched_getscheduler(pid_t pid); /* 设置指定线程的调度参数 */ int sched_setattr(pid_t pid, struct sched_attr *attr, unsigned int flags); /* 查询指定线程的调度策略和调度参数 */ int sched_getattr(pid_t pid, struct sched_attr *attr, unsigned int size, unsigned int flags); /* 设置指定线程的调度参数 */ int sched_setparam(pid_t pid, const struct sched_param *param); /* 查询指定线程的调度参数 */ int sched_getparam(pid_t pid, struct sched_param *param); /* 设置指定线程的CPU掩码,设置 p->cpus_allowed */ int sched_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t *mask); /* 查询指定线程的CPU掩码 */ int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask); /* 使得当前线程让出CPU给其他线程使用 */ int sched_yield(void); /* 查询特定调度策略的最大优先级值 */ int sched_get_priority_max(int policy); /* 查询特定调度策略的最小优先级值 */ int sched_get_priority_min(int policy); /* 查询round-robin策略下线程的定量 */ int sched_rr_get_interval(pid_t pid, struct timespec * tp); /* 查询某个线程,或者进程组的nice值 */ int getpriority(int which, id_t who); /* 设置线程或者进程组的nice值 */ int setpriority(int which, id_t who, int prio);
注:其中内核中的 membarrier() 和使能CONFIG_COMPAT_32BIT_TIME下的 sched_rr_get_interval_time32() 函数C库没有导出来。除了 getpriority/setpriority 两个系统调用是 kernel/sys.c 中导出来的外,其它都是 kernel/sched/core.c 中导出来的。
3. android_util_Process.cpp 中向 Process,java 中注册的 Native 接口
static const JNINativeMethod methods[] = { {"getUidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getUidForName}, {"getGidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getGidForName}, {"setThreadPriority", "(II)V", (void*)android_os_Process_setThreadPriority}, //传了tid {"setThreadScheduler", "(III)V", (void*)android_os_Process_setThreadScheduler}, {"setCanSelfBackground", "(Z)V", (void*)android_os_Process_setCanSelfBackground}, {"setThreadPriority", "(I)V", (void*)android_os_Process_setCallingThreadPriority}, //只传了一个prio,tid取自己 {"getThreadPriority", "(I)I", (void*)android_os_Process_getThreadPriority}, {"getThreadScheduler", "(I)I", (void*)android_os_Process_getThreadScheduler}, {"setThreadGroup", "(II)V", (void*)android_os_Process_setThreadGroup}, {"setThreadGroupAndCpuset", "(II)V", (void*)android_os_Process_setThreadGroupAndCpuset}, {"setProcessGroup", "(II)V", (void*)android_os_Process_setProcessGroup}, {"setCgroupProcsProcessGroup", "(IIIZ)V", (void*)android_os_Process_setCgroupProcsProcessGroup}, {"getProcessGroup", "(I)I", (void*)android_os_Process_getProcessGroup}, {"createProcessGroup", "(II)I", (void*)android_os_Process_createProcessGroup}, {"getExclusiveCores", "()[I", (void*)android_os_Process_getExclusiveCores}, {"setSwappiness", "(IZ)Z", (void*)android_os_Process_setSwappiness}, {"setArgV0Native", "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0}, {"setUid", "(I)I", (void*)android_os_Process_setUid}, {"setGid", "(I)I", (void*)android_os_Process_setGid}, {"sendSignal", "(II)V", (void*)android_os_Process_sendSignal}, {"sendSignalQuiet", "(II)V", (void*)android_os_Process_sendSignalQuiet}, {"setProcessFrozen", "(IIZ)V", (void*)android_os_Process_setProcessFrozen}, {"getFreeMemory", "()J", (void*)android_os_Process_getFreeMemory}, {"getTotalMemory", "()J", (void*)android_os_Process_getTotalMemory}, {"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V", (void*)android_os_Process_readProcLines}, {"getPids", "(Ljava/lang/String;[I)[I", (void*)android_os_Process_getPids}, {"readProcFile", "(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z", (void*)android_os_Process_readProcFile}, {"parseProcLine", "([BII[I[Ljava/lang/String;[J[F)Z", (void*)android_os_Process_parseProcLine}, {"getElapsedCpuTime", "()J", (void*)android_os_Process_getElapsedCpuTime}, {"getPss", "(I)J", (void*)android_os_Process_getPss}, {"getRss", "(I)[J", (void*)android_os_Process_getRss}, {"getPidsForCommands", "([Ljava/lang/String;)[I", (void*)android_os_Process_getPidsForCommands}, //{"setApplicationObject", "(Landroid/os/IBinder;)V", (void*)android_os_Process_setApplicationObject}, {"killProcessGroup", "(II)I", (void*)android_os_Process_killProcessGroup}, {"removeAllProcessGroups", "()V", (void*)android_os_Process_removeAllProcessGroups}, {"nativePidFdOpen", "(II)I", (void*)android_os_Process_nativePidFdOpen}, };
可以看到,调度上,缺少设置CPU亲和性(绑核)、yield(让出CPU)、setattr(per-task的uclamp设置在里面)的接口。多出了一些Cgroup相关设置接口。
3. Process.java 中导出的与调度相关的接口
(1) 对进程指定的分组:
/* 默认线程组 - 仅对 setProcessGroup() 有意义,不能与 setThreadGroup() 一起使用。 当与 setProcessGroup() 一起使用时,进程中每个线程的组会根据该线 程的当前优先级有条件地更改,优先级数值小于 THREAD_PRIORITY_BACKGROUND 的线程被移动到前台线程组,所有其他线程保持不变。 */ public static final int THREAD_GROUP_DEFAULT = -1; /* 后台线程组 - 该组中的所有线程都以减少的 CPU 份额进行调度。值与枚举 SchedPolicy 的常 量 SP_BACKGROUND 相同。 */ public static final int THREAD_GROUP_BACKGROUND = 0; /* * 前台线程组 - * 该组中的所有线程都以正常的 CPU 份额进行调度。 值与枚举 SchedPolicy * 的常量 SP_FOREGROUND 相同。 此级别未使用。 */ private static final int THREAD_GROUP_FOREGROUND = 1; /* System thread group. */ public static final int THREAD_GROUP_SYSTEM = 2; /* Application audio thread group. */ public static final int THREAD_GROUP_AUDIO_APP = 3; /* System audio thread group. */ public static final int THREAD_GROUP_AUDIO_SYS = 4; /* Thread group for top foreground app. */ public static final int THREAD_GROUP_TOP_APP = 5; /* Thread group for RT app */ public static final int THREAD_GROUP_RT_APP = 6; /* 绑定前台服务的线程组,在屏幕关闭期间应该有额外的 CPU 限制 */ public static final int THREAD_GROUP_RESTRICTED = 7;
(2) 相关函数
/* CFS线程优先级设置,priority 取[-20(max)——19] */ public static final native void setThreadPriority(int tid, int priority) throws IllegalArgumentException, SecurityException; public static final native void setThreadPriority(int priority) throws IllegalArgumentException, SecurityException; /* 传0获取自己的优先级,返回值是[-20——19] */ public static final native int getThreadPriority(int tid) throws IllegalArgumentException; /* policy 取 SCHED_OTHER、SCHED_FIFO等,可 chrt --help 查看 */ public static final native void setThreadScheduler(int tid, int policy, int priority) throws IllegalArgumentException; /* 获取tid线程的调度策略 */ public static final native int getThreadScheduler(int tid) throws IllegalArgumentException; /* GUARD_THREAD_PRIORITY 为false不起作用 */ public static final native void setCanSelfBackground(boolean backgroundOk); /* * 只设置SchedPolicy(top/background..)传参 group=THREAD_GROUP_*,如 THREAD_GROUP_TOP_APP=5, * 设置后具体动作见task_profiles.json文件中 "AggregateProfiles" 条目下对应的配置。 * 注:设置的是线程。 */ public static final native void setThreadGroup(int tid, int group) throws IllegalArgumentException, SecurityException; /* 将tid设置到 group=THREAD_GROUP_* 的分组中,实现上和上面setThreadGroup函数完全一样!*/ public static final native void setThreadGroupAndCpuset(int tid, int group) throws IllegalArgumentException, SecurityException; /* * group == THREAD_GROUP_DEFAULT 表示将pid的所有非后台优先级线程移动到前台调度组, * 但不理会后台优先级的线程。 * group == THREAD_GROUP_BACKGROUND 将pid的所有线程,无论优先级如何,都移动到后台调度组。 * group == THREAD_GROUP_FOREGROUND 是不允许的。 * 始终设置 cpuset。 * 注:设置的是一个进程的所有线程。 */ public static final native void setProcessGroup(int pid, int group) throws IllegalArgumentException, SecurityException; /* * group == THREAD_GROUP_DEFAULT 表示将所有非后台优先级线程移动到前台调度组,但不理会后台优先级线程。 * group == THREAD_GROUP_BG_NONINTERACTIVE 将所有线程(无论优先级如何)移动到后台调度组。 * group == THREAD_GROUP_FOREGROUND 是不允许的。 * 注:是Qcom添加的函数。 */ public static final native void setCgroupProcsProcessGroup(int uid, int pid, int group, boolean dex2oat_only) throws IllegalArgumentException, SecurityException; /* 返回pid的scheduling group,直接调用get_sched_policy(pid, &sp)根据SP_*的值返回例如"top-app"的字符串 */ public static final native int getProcessGroup(int pid) throws IllegalArgumentException, SecurityException; /* Create a new process group in the cgroup uid/pid hierarchy */ public static final native int createProcessGroup(int uid, int pid);
注:上面很多函数标记为 @hide,对App不可见。
二、实现
1. setThreadPriority 为例
Process.java 中的 setThreadPriority 成员函数直接使用的就是JNI中注册的函数
/* pri 取[-20(max)——19], pid传0表示设置当前线程 */ void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz, jint pid, jint pri) //android_util_Process.cpp { androidSetThreadPriority(pid, pri); } int androidSetThreadPriority(pid_t tid, int pri) //system/core/libutils/Threads.cpp { int rc = 0; int curr_pri = getpriority(PRIO_PROCESS, tid); /* 要设置的优先级和目前的优先级相同 */ if (curr_pri == pri) { return rc; } /* PRIO_PROCESS 0; PRIO_PGRP 1; PRIO_USER 2;*/ if (setpriority(PRIO_PROCESS, tid, pri) < 0) { rc = INVALID_OPERATION; } else { errno = 0; } return rc; }
2. setThreadGroup 为例
Process.java 中的 setThreadGroup 成员函数直接使用的就是JNI中注册的函数
void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int tid, jint grp) { ALOGV("%s tid=%d grp=%" PRId32, __func__, tid, grp); if (!verifyGroup(env, grp)) { return; } int res = SetTaskProfiles(tid, {get_sched_policy_profile_name((SchedPolicy)grp)}, true) ? 0 : -1; if (res != NO_ERROR) { signalExceptionForGroupError(env, -res, tid); } } //system/core/libprocessgroup/processgroup.cpp bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) { return TaskProfiles::GetInstance().SetTaskProfiles(tid, profiles, use_fd_cache); } //system/core/libprocessgroup/task_profiles.cpp bool TaskProfiles::SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) { /* profiles 是个数组,set_cpuset_policy调用传参 SetTaskProfiles(tid, {"CPUSET_SP_FOREGROUND", "BlkIOForeground"}, true) */ for (const auto& name : profiles) { TaskProfile* profile = GetProfile(name); if (profile != nullptr) { /* 顶层函数传参为true使用的是这个 */ if (use_fd_cache) { /* 只做一个遍历打开对应文件的操作 */ profile->EnableResourceCaching(); } /* 实际配置函数 */ if (!profile->ExecuteForTask(tid)) { PLOG(WARNING) << "Failed to apply " << name << " task profile"; } } else { PLOG(WARNING) << "Failed to find " << name << "task profile"; } } return true; } //system/core/libprocessgroup/task_profiles.cpp void TaskProfile::EnableResourceCaching() { //若已经cache了,就直接返回了 if (res_cached_) { return; } /* * 遍历profile下的每个element进行设置,elements_ 是 * std::vector<std::unique_ptr<ProfileAction>> 类型, * 是一个抽象类,会调用各 TaskProfile 的实现类的函数, * 主要就是打开要配置的文件。 */ for (auto& element : elements_) { element->EnableResourceCaching(); } res_cached_ = true; } //system/core/libprocessgroup/task_profiles.cpp bool TaskProfile::ExecuteForTask(int tid) const { /* tid传0就表示要设置自己 */ if (tid == 0) { tid = GetThreadId(); } for (const auto& element : elements_) { /* 一次调用此profile的各个实现类的方法 */ if (!element->ExecuteForTask(tid)) { return false; } } return true; }
比如下面设置cgroup的一个实现类,调用其 ExecuteForTask()
//system/core/libprocessgroup/task_profiles.cpp bool SetCgroupAction::ExecuteForTask(int tid) const { std::lock_guard<std::mutex> lock(fd_mutex_); if (IsFdValid()) { // fd is cached, reuse it /* 这里是实际的配置 */ if (!AddTidToCgroup(tid, fd_, controller()->name())) { LOG(ERROR) << "Failed to add task into cgroup"; return false; } return true; } ... // fd was not cached because cached fd can't be used std::string tasks_path = controller()->GetTasksFilePath(path_); unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC))); if (tmp_fd < 0) { PLOG(WARNING) << "Failed to open " << tasks_path; return false; } if (!AddTidToCgroup(tid, tmp_fd, controller()->name())) { LOG(ERROR) << "Failed to add task into cgroup"; return false; } return true; } //system/core/libprocessgroup/task_profiles.cpp bool SetCgroupAction::AddTidToCgroup(int tid, int fd, const char* controller_name) { std::string value = std::to_string(tid); /* 配置到cgroup文件中 */ if (TEMP_FAILURE_RETRY(write(fd, value.c_str(), value.length())) == value.length()) { return true; } /* 翻译:如果线程正在退出过程中,不要标记错误 */ if (errno == ESRCH) { return true; } /* 翻译:当我们加入的 cpuset cgroup 没有在线 cpus 时返回 ENOSPC */ if (errno == ENOSPC && !strcmp(controller_name, "cpuset")) { /* This is an abnormal case happening only in testing, so report it only once */ static bool empty_cpuset_reported = false; if (empty_cpuset_reported) { return true; } LOG(ERROR) << "Failed to add task '" << value << "' into cpuset because all cpus in that cpuset are offline"; empty_cpuset_reported = true; } else { PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; fd=" << fd; } return false; }
3. set_cpuset_policy 函数
这个函数在 system/core/libprocessgroup/sched_policy.cpp 中导出来的,但是在 Process.java 和其JNI android_util_Process.cpp 中都没有使用,若想单独精细化调整cpuset,可以自行导出,并修改下面函数。
int set_cpuset_policy(int tid, SchedPolicy policy) { if (tid == 0) { tid = GetThreadId(); } policy = _policy(policy); switch (policy) { case SP_BACKGROUND: return SetTaskProfiles(tid, {"CPUSET_SP_BACKGROUND", "BlkIOBackground"}, true) ? 0 : -1; case SP_FOREGROUND: case SP_AUDIO_APP: case SP_AUDIO_SYS: return SetTaskProfiles(tid, {"CPUSET_SP_FOREGROUND"}, true) ? 0 : -1; case SP_TOP_APP: return SetTaskProfiles(tid, {"CPUSET_SP_TOP_APP"}, true) ? 0 : -1; case SP_SYSTEM: return SetTaskProfiles(tid, {"CPUSET_SP_SYSTEM"}, true) ? 0 : -1; case SP_RESTRICTED: return SetTaskProfiles(tid, {"CPUSET_SP_RESTRICTED"}, true) ? 0 : -1; default: break; } return 0; }
可以看到,在cpuset分组上默认的对应关系。
注:sys和vnd分区的 task_profiles.h 文件的内容实现还不一样。
三、使用
1. OomAdjuster.java 中,在任务前后台切换时调用 setProcessGroup() 对任务cgroup分组进行切换
//frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java /* 只是单成员导入,若使用其它成员也需要导入 */ import static android.os.Process.setProcessGroup; OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids, ServiceThread adjusterThread) { ... /* 匿名函数-Lambda表达式 */ mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> { final int pid = msg.arg1; final int group = msg.arg2; /* 传参如 THREAD_GROUP_TOP_APP = 5*/ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setProcessGroup " + app.processName + " to " + group); try { setProcessGroup(pid, group); } catch (Exception e) { if (DEBUG_ALL) { Slog.w(TAG, "Failed setting process group of " + pid + " to " + group, e); } } finally { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } return true; } }
我的相关博客:
Cgroup学习笔记1—Cgroup 抽象层:https://www.cnblogs.com/hellokitty2/p/16663669.html
posted on 2022-09-14 23:17 Hello-World3 阅读(814) 评论(0) 编辑 收藏 举报