进程管理上层代码-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  阅读(768)  评论(0编辑  收藏  举报

导航