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范围等,以达到对不同优先级进程的控制。

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

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

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;
}

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

system\core\libcutils\Sched_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

Android中关于cpu/cpuset/schedtune的应用的更多相关文章

  1. 【月入41万】Mono For Android中使用百度地图SDK

    借助于Mono For Android技术,.Net开发者也可以使用自己熟悉的C#语言以及.Net来开发Android应用.由于Mono For Android把Android SDK中绝大部分类库都 ...

  2. Android 中关于static的使用问题

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/5251564.html 项目中,在不停地接收串口数据很长一段时间(几小时)后,会偶然性的报错.初步排除了oom ...

  3. android中的线程池学习笔记

    阅读书籍: Android开发艺术探索 Android开发进阶从小工到专家 对线程池原理的简单理解: 创建多个线程并且进行管理,提交的任务会被线程池指派给其中的线程进行执行,通过线程池的统一调度和管理 ...

  4. 关于Android中ArrayMap/SparseArray比HashMap性能好的深入研究

    由于网上有朋友对于这个问题已经有了很详细的研究,所以我就不班门弄斧了: 转载于:http://android-performance.com/android/2014/02/10/android-sp ...

  5. Android中Activity的生命周期

    简介: 这个基本是必问的问题了,说一下你对Activity生命周期的理解,呵呵… onCreate, onStart, onResume, onPause, onStop, onDestroy, on ...

  6. Android中Handler 、Thread和Runnable之间的关系

    在多线程编程的时候,我们经常会用到Handler,Thread和Runnable这三个类,我们来看看这三个类之间是怎么样的关系? 首先说明Android的CPU分配的最小单元是线程,Handler一般 ...

  7. Android中对Log日志文件的分析[转]

    一,Bug出现了, 需要“干掉”它 bug一听挺吓人的,但是只要你懂了,android里的bug是很好解决的,因为android里提供了LOG机制,具体的底层代码,以后在来分析,只要你会看bug, a ...

  8. Android中Service(服务)详解

    http://blog.csdn.net/ryantang03/article/details/7770939 Android中Service(服务)详解 标签: serviceandroidappl ...

  9. Android 中的 Service 全面总结(转载)

    转载地址:http://www.cnblogs.com/newcj/archive/2011/05/30/2061370.html 感谢作者 Android 中的 Service 全面总结 1.Ser ...

随机推荐

  1. “全能”选手—Django 1.10文档中文版Part1

    本文是博主翻译的Django1.10版本官方文档的第一部分,如时间充裕,争取一直翻译下去,经验不足,或有错漏,敬请指正. 另外对于公开文档进行翻译的版权问题不是很清楚,如有侵权请联系我! 另外,要转载 ...

  2. 支持多返回值存储过程的SqlHelper

    public readonly string connStr = ConfigurationManager.ConnectionStrings["sql"].ConnectionS ...

  3. 使用图片视频展示插件blueimp Gallery改造网站的视频图片展示

    在很多情况下,我们网站可能会展示我们的产品图片.以及教程视频等内容,结合一个比较好的图片.视频展示插件,能够使得我们的站点更加方便使用,也更加酷炫,在Github上有很多相关的处理插件可以找来使用,有 ...

  4. MahApps.Metro使用

    # MahApps.Metro使用 # ## 下载MahApps.Metro ## PM> Install-Package MahApps.Metro ## MainWindow.xaml中添加 ...

  5. asp.net获取数据库连接字符串

    1.添加引用 using System.Configuration; 2.代码 string strConnectionString=ConfigurationManager.AppSettings[ ...

  6. 前端渲染利器——JsRender入门

    JsRender不少前端人员应该都用过,它是一个比较强大的模板,不牵涉太多技术依赖,使用起来非常舒服.我本人在前端开发中使用React之前,都是用的它了(实际上我感觉React没有JsViewes好用 ...

  7. 【视频处理】YUV与RGB格式转换

    YUV格式具有亮度信息和色彩信息分离的特点,但大多数图像处理操作都是基于RGB格式. 因此当要对图像进行后期处理显示时,需要把YUV格式转换成RGB格式. RGB与YUV的变换公式如下: YUV(25 ...

  8. centos 域名硬解析(linux)

    centos做硬解析跟Windows一样修改一个文件. 具体文件为:/etc/hosts.修改命令: vi /etc/hosts 格式个Windows也一样的.

  9. node-sass报错解决方法

    在Vue.js中,每一个vue文件都是一个组件,在.vue文件中可以将模板,脚本,样式写在一起,便于组织整个组件.在使用template,script时,编写css样式时,都进行的特别顺利,唯独当我想 ...

  10. Oracle死锁

    当两个或多个用户相互等待锁定的数据时就会发生死锁,这时这些用户被卡在不能继续处理业务,oracle可以自动检测死锁并解决他们,通过回滚一个死锁中的语句,释放锁定的数据,回滚的话会遇到ora-00060 ...