• Android中关于cpu/cpuset/schedtune的应用


    Android中关于cpu/cpuset/schedtune的应用都是基于进程优先级的,根据不同优先级划分进程类型。AMS(ActivityManagerService)和PMS(PackageManagerService)等通过class Process设置进程优先级、调度策略等;android/osProcess JNI通过调用libcutils.so/libutils.so执行getpriority/setpriority/sched_setscheduler/sched_getschedler系统调用或者直接操作CGroup文件节点以达到设置优先级,限制进程CPU资源的目的。

    根据优先级,通过设置CGroup的cpu/cpuset/stune控制进程获得CPU执行时间、可调度CPU范围等,以达到对不同优先级进程的控制。

    image

    Android关于cpu/cpuset/schedtune的框架结构

    image

    进程优先级和调度策略从上到下贯穿其中,但是在不同的层级的名称有一些变化。下面逐一介绍。

    class Process以及android/os/Process JNI

    frameworks/base/core/java/android/os/Process.java

    其他服务通过class Process来设置进程优先级、调度侧率等。

    class Process中优先级划分:

    public static final int THREAD_PRIORITY_DEFAULT = 0;  应用的默认优先级

    /*
    * ***************************************
    * ** Keep in sync with utils/threads.h **
    * ***************************************
    */

    public static final int THREAD_PRIORITY_LOWEST = 19;  线程的最低优先级

    public static final int THREAD_PRIORITY_BACKGROUND = 10;  后台线程的默认优先级

    public static final int THREAD_PRIORITY_FOREGROUND = -2;  前台进程的标准优先级

    public static final int THREAD_PRIORITY_DISPLAY = -4;  系统用于显示功能的优先级

    public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8;  系统用于重要显示功能的优先级

    public static final int THREAD_PRIORITY_AUDIO = -16;  音频线程默认优先级

    public static final int THREAD_PRIORITY_URGENT_AUDIO = -19;  重要音频线程默认优先级

    调度策略划分:

    public static final int SCHED_OTHER = 0; 默认调度策略,对应CFS调度类

    public static final int SCHED_FIFO = 1;  FIFO调度策略,对应RT调度类

    public static final int SCHED_RR = 2;  RR调度策略,对应RT调度类

    public static final int SCHED_BATCH = 3;  批调度策略,对应CFS调度类

    public static final int SCHED_IDLE = 5;  idle调度策略

    class Process相关API,主要用于:

      

    public static final native void setThreadPriority(int tid, int priority)
            throws IllegalArgumentException, SecurityException;

    public static final native void setThreadScheduler(int tid, int policy, int priority)
            throws IllegalArgumentException;

    public static final native void setThreadPriority(int tid, int priority)
            throws IllegalArgumentException, SecurityException;

    public static final native int getThreadPriority(int tid)
            throws IllegalArgumentException;

    public static final native int getThreadScheduler(int tid)
            throws IllegalArgumentException;

    public static final native void setThreadGroup(int tid, int group)
            throws IllegalArgumentException, SecurityException;

    public static final native void setProcessGroup(int pid, int group)
            throws IllegalArgumentException, SecurityException;

    frameworks/base/core/jni/android_util_Process.cpp

    对应JNINativeMethod如下:

    static const JNINativeMethod methods[] = {

        {"setThreadPriority",   "(II)V", (void*)android_os_Process_setThreadPriority},
        {"setThreadScheduler",  "(III)V", (void*)android_os_Process_setThreadScheduler},
        {"setCanSelfBackground", "(Z)V", (void*)android_os_Process_setCanSelfBackground},
        {"setThreadPriority",   "(I)V", (void*)android_os_Process_setCallingThreadPriority},
        {"getThreadPriority",   "(I)I", (void*)android_os_Process_getThreadPriority},
        {"getThreadScheduler",   "(I)I", (void*)android_os_Process_getThreadScheduler},
        {"setThreadGroup",      "(II)V", (void*)android_os_Process_setThreadGroup},
        {"setProcessGroup",     "(II)V", (void*)android_os_Process_setProcessGroup},
        {"getProcessGroup",     "(I)I", (void*)android_os_Process_getProcessGroup},

    };

    scheduler相关API直接调用sched_setscheduler/sched_getscheduler。

    libcutils.so/libutils.so

    在介绍这个函数之前先介绍一下此处所使用的优先级定义,可以看出和class Process中是完全的对应关系:

    ANDROID_PRIORITY_LOWEST         =  19,

    /* use for background tasks */
    ANDROID_PRIORITY_BACKGROUND     =  10,

    /* most threads run at normal priority */
    ANDROID_PRIORITY_NORMAL         =   0,

    /* threads currently running a UI that the user is interacting with */
    ANDROID_PRIORITY_FOREGROUND     =  -2,

    /* the main UI thread has a slightly more favorable priority */
    ANDROID_PRIORITY_DISPLAY        =  -4,

    /* ui service treads might want to run at a urgent display (uncommon) */
    ANDROID_PRIORITY_URGENT_DISPLAY =  HAL_PRIORITY_URGENT_DISPLAY,

    /* all normal audio threads */
    ANDROID_PRIORITY_AUDIO          = -16,

    /* service audio threads (uncommon) */
    ANDROID_PRIORITY_URGENT_AUDIO   = -19,

    /* should never be used in practice. regular process might not
    * be allowed to use this level */
    ANDROID_PRIORITY_HIGHEST        = -20,

    ANDROID_PRIORITY_DEFAULT        = ANDROID_PRIORITY_NORMAL,

    还需要在研究一下,Sched Policy中使用的优先级映射关系:

    /* Keep in sync with THREAD_GROUP_* in frameworks/base/core/java/android/os/Process.java */
    typedef enum {
        SP_DEFAULT    = -1,
        SP_BACKGROUND = 0,
        SP_FOREGROUND = 1,
        SP_SYSTEM     = 2,  // can't be used with set_sched_policy()
        SP_AUDIO_APP  = 3,
        SP_AUDIO_SYS  = 4,
        SP_TOP_APP    = 5,
        SP_CNT,
        SP_MAX        = SP_CNT - 1,
        SP_SYSTEM_DEFAULT = SP_FOREGROUND,
    } SchedPolicy;

    Threads.cpp中定义了androidSetThreadPriority用于设置线程的优先级。

    int androidSetThreadPriority(pid_t tid, int pri)
    {
        int rc = 0;
        int lasterr = 0;

        if (pri >= ANDROID_PRIORITY_BACKGROUND) {  如果priority大于等于BACKGROUND,则设置为BACKGROUND类型的调度策略。
            rc = set_sched_policy(tid, SP_BACKGROUND);
        } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) {  如果priority小于BACKGROUND,且当线程为BACKGROUND类型,则设置为FOREGROUND类型。
            rc = set_sched_policy(tid, SP_FOREGROUND);
        }

        if (rc) {
            lasterr = errno;
        }

        if (setpriority(PRIO_PROCESS, tid, pri) < 0) {  设置优先级
            rc = INVALID_OPERATION;
        } else {
            errno = lasterr;
        }

        return rc;
    }

    set_cpuset_policy根据SchedPolicy类型将tid写入cpuset和schedtune子系统中。

    有下面的函数可以得出cpuset、schedtune和不同类型SchedPolicy之间的对应关系

    /dev/cpuset/foreground/tasks  SP_FOREGROUND SP_AUDIO_APP SP_AUDIO_SYS
    /dev/cpuset/background/tasks  SP_BACKGROUND
    /dev/cpuset/system-background/tasks  SP_SYSTEM
    /dev/cpuset/top-app/tasks  SP_TOP_APP

    /dev/stune/top-app/tasks  SP_TOP_APP
    /dev/stune/foreground/tasks  SP_FOREGROUND SP_AUDIO_APP SP_AUDIO_SYS
    /dev/stune/background/tasks  SP_BACKGROUND

    int set_cpuset_policy(int tid, SchedPolicy policy)
    {
        // in the absence of cpusets, use the old sched policy
    #ifndef USE_CPUSETS
        return set_sched_policy(tid, policy);
    #else
        if (tid == 0) {
            tid = gettid();
        }
        policy = _policy(policy);
        pthread_once(&the_once, __initialize);

        int fd = -1;
        int boost_fd = -1;
        switch (policy) {
        case SP_BACKGROUND:
            fd = bg_cpuset_fd;
            boost_fd = bg_schedboost_fd;
            break;
        case SP_FOREGROUND:
        case SP_AUDIO_APP:
        case SP_AUDIO_SYS:
            fd = fg_cpuset_fd;
            boost_fd = fg_schedboost_fd;
            break;
        case SP_TOP_APP :
            fd = ta_cpuset_fd;
            boost_fd = ta_schedboost_fd;
            break;
        case SP_SYSTEM:
            fd = system_bg_cpuset_fd;
            break;
        default:
            boost_fd = fd = -1;
            break;
        }

        if (add_tid_to_cgroup(tid, fd) != 0) {
            if (errno != ESRCH && errno != ENOENT)
                return -errno;
        }

    #ifdef USE_SCHEDBOOST
        if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
            if (errno != ESRCH && errno != ENOENT)
                return -errno;
        }
    #endif

        return 0;
    #endif
    }

    set_sched_policy设置cpu/schedtune两个子系统,子系统节点和SchedPolicy类型对应如下:

    /dev/cpuctl/tasks  SP_FOREGROUND SP_AUDIO_APP SP_AUDIO_SYS
    /dev/cpuctl/bg_non_interactive/tasks  SP_BACKGROUND

    /dev/stune/top-app/tasks  SP_TOP_APP
    /dev/stune/foreground/tasks  SP_FOREGROUND SP_AUDIO_APP SP_AUDIO_SYS
    /dev/stune/background/tasks  SP_BACKGROUND

    int set_sched_policy(int tid, SchedPolicy policy)
    {
        if (tid == 0) {
            tid = gettid();
        }
        policy = _policy(policy);
        pthread_once(&the_once, __initialize);

    #if POLICY_DEBUG
        char statfile[64];
        char statline[1024];
        char thread_name[255];

        snprintf(statfile, sizeof(statfile), "/proc/%d/stat", tid);
        memset(thread_name, 0, sizeof(thread_name));

        int fd = open(statfile, O_RDONLY | O_CLOEXEC);
        if (fd >= 0) {
            int rc = read(fd, statline, 1023);
            close(fd);
            statline[rc] = 0;
            char *p = statline;
            char *q;

            for (p = statline; *p != '('; p++);
            p++;
            for (q = p; *q != ')'; q++);

            strncpy(thread_name, p, (q-p));
        }
        switch (policy) {
        case SP_BACKGROUND:
            SLOGD("vvv tid %d (%s)", tid, thread_name);
            break;
        case SP_FOREGROUND:
        case SP_AUDIO_APP:
        case SP_AUDIO_SYS:
        case SP_TOP_APP:
            SLOGD("^^^ tid %d (%s)", tid, thread_name);
            break;
        case SP_SYSTEM:
            SLOGD("/// tid %d (%s)", tid, thread_name);
            break;
        default:
            SLOGD("??? tid %d (%s)", tid, thread_name);
            break;
        }
    #endif

        if (__sys_supports_schedgroups) {  是否使能schedtune CGroup
            int fd = -1;
            int boost_fd = -1;
            switch (policy) {
            case SP_BACKGROUND:
                fd = bg_cgroup_fd;
                boost_fd = bg_schedboost_fd;
                break;
            case SP_FOREGROUND:
            case SP_AUDIO_APP:
            case SP_AUDIO_SYS:
                fd = fg_cgroup_fd;
                boost_fd = fg_schedboost_fd;
                break;
            case SP_TOP_APP:
                fd = fg_cgroup_fd;
                boost_fd = ta_schedboost_fd;
                break;
            default:
                fd = -1;
                boost_fd = -1;
                break;
            }


            if (add_tid_to_cgroup(tid, fd) != 0) {
        
          if (errno != ESRCH && errno != ENOENT)
                    return -errno;
            }

    #ifdef USE_SCHEDBOOST
            if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
                if (errno != ESRCH && errno != ENOENT)
                    return -errno;
            }
    #endif
        } else {  如果没有使能schedtune CGroup,则使用系统调用sched_setscheduler设置为SCHED_BATCH或者SCHED_NORMAL
            struct sched_param param;

            param.sched_priority = 0;
            sched_setscheduler(tid,
                               (policy == SP_BACKGROUND) ?
                               SCHED_BATCH : SCHED_NORMAL,
                               &param);
        }

        if (__sys_supports_timerslack) {
            set_timerslack_ns(tid, policy == SP_BACKGROUND ?
                                   TIMER_SLACK_BG : TIMER_SLACK_FG);
        }

        return 0;
    }

    上面的一系列转换可以用下图表示:

    image

    systemcorelibcutilsSched_policy.c中,对SP_*系列SchedPolicy转换成使用不同cpuctl、cpuset、stune句柄,将对应的pid、tid写入tasks中。
    SP_BACKGROUND对应SCHED_BACH调度策略,其他对应SCHED_NORMAL。
    SCHED_NORMAL:默认的调度策略,在旧版中为SCHED_OTHER。SCHED_BATCH:针对批处理进程。SCHED_IDLE:使用此调度侧率的进程优先级最低。
    SCHED_NORMAL和SCHED_BATCH区别只是再唤醒时有区别,唤醒较频繁的进程不适合SCHED_BATCH。
    如果使能__sys_supports_schedgroups,就不会调用sched_setscheduler去设置SchedulePolicy。

    SCHED_NORMAL和SCHED_BACH区别

    SP_BACKGROUND对应SCHED_BACH调度策略,其他对应SCHED_NORMAL。

    SCHED_NORMAL:默认的调度策略,在旧版中为SCHED_OTHER。SCHED_BATCH:针对批处理进程。SCHED_IDLE:使用此调度侧率的进程优先级最低。

    SCHED_NORMAL和SCHED_BATCH区别只是再唤醒时有区别,唤醒较频繁的进程不适合SCHED_BATCH。

    如果使能__sys_supports_schedgroups,就不会调用sched_setscheduler去设置SchedulePolicy。

    Android中的一个应用

    cpu子系统:

    # Create cgroup mount points for process groups

    mkdir /dev/cpuctl

    mount cgroup none /dev/cpuctl cpu

    chown system system /dev/cpuctl

    chown system system /dev/cpuctl/tasks

    chmod 0666 /dev/cpuctl/tasks

    write /dev/cpuctl/cpu.shares 1024

    write /dev/cpuctl/cpu.rt_runtime_us 800000

    write /dev/cpuctl/cpu.rt_period_us 1000000

    mkdir /dev/cpuctl/bg_non_interactive

    chown system system /dev/cpuctl/bg_non_interactive/tasks

    chmod 0666 /dev/cpuctl/bg_non_interactive/tasks

    # 5.0 %

    write /dev/cpuctl/bg_non_interactive/cpu.shares 52

    write /dev/cpuctl/bg_non_interactive/cpu.rt_runtime_us 700000

    write /dev/cpuctl/bg_non_interactive/cpu.rt_period_us 1000000

    cpuset子系统:

    # sets up initial cpusets for ActivityManager

    mkdir /dev/cpuset

    mount cpuset none /dev/cpuset

    # this ensures that the cpusets are present and usable, but the device's

    # init.rc must actually set the correct cpus

    mkdir /dev/cpuset/foreground

    write /dev/cpuset/foreground/cpus 0

    write /dev/cpuset/foreground/mems 0

    mkdir /dev/cpuset/foreground/boost

    write /dev/cpuset/foreground/boost/cpus 0

    write /dev/cpuset/foreground/boost/mems 0

    mkdir /dev/cpuset/background

    write /dev/cpuset/background/cpus 0

    write /dev/cpuset/background/mems 0

    # system-background is for system tasks that should only run on

    # little cores, not on bigs

    # to be used only by init, so don't change system-bg permissions

    mkdir /dev/cpuset/system-background

    write /dev/cpuset/system-background/cpus 0

    write /dev/cpuset/system-background/mems 0

    mkdir /dev/cpuset/top-app

    write /dev/cpuset/top-app/cpus 0

    write /dev/cpuset/top-app/mems 0

    # change permissions for all cpusets we'll touch at runtime

    chown system system /dev/cpuset

    chown system system /dev/cpuset/foreground

    chown system system /dev/cpuset/foreground/boost

    chown system system /dev/cpuset/background

    chown system system /dev/cpuset/system-background

    chown system system /dev/cpuset/top-app

    chown system system /dev/cpuset/tasks

    chown system system /dev/cpuset/foreground/tasks

    chown system system /dev/cpuset/foreground/boost/tasks

    chown system system /dev/cpuset/background/tasks

    chown system system /dev/cpuset/system-background/tasks

    chown system system /dev/cpuset/top-app/tasks

    # set system-background to 0775 so SurfaceFlinger can touch it

    chmod 0775 /dev/cpuset/system-background

    chmod 0664 /dev/cpuset/foreground/tasks

    chmod 0664 /dev/cpuset/foreground/boost/tasks

    chmod 0664 /dev/cpuset/background/tasks

    chmod 0664 /dev/cpuset/system-background/tasks

    chmod 0664 /dev/cpuset/top-app/tasks

    chmod 0664 /dev/cpuset/tasks

    schedtune子系统:

    # Create energy-aware scheduler tuning nodes

    mkdir /dev/stune

    mount cgroup none /dev/stune schedtune

    mkdir /dev/stune/foreground

    chown system system /dev/stune

    chown system system /dev/stune/foreground

    chown system system /dev/stune/tasks

    chown system system /dev/stune/foreground/tasks

    chmod 0664 /dev/stune/tasks

    chmod 0664 /dev/stune/foreground/tasks

  • 相关阅读:
    C# Thread.Sleep 卡死的问题解决方法
    GridView多行显示 TableAdapter没有Update 问题
    python zlib 压缩 解压 文件夹
    17、【Python】mac安装多个版本的python
    18、【常见算法】二叉树的遍历
    29、【C++基础】内存泄露
    17、【常见算法】topN问题
    16、【常见算法】查找斐波那契数列的第N项
    15、【常见算法】实现sqrt函数
    14、【常见算法】数组分组排序
  • 原文地址:https://www.cnblogs.com/arnoldlu/p/6221608.html
Copyright © 2020-2023  润新知